forked from bluesky-social/atproto
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Secp256k1 support (bluesky-social#267)
* add secp256k1 to crypto module * integrate into plc * use secp256k1 key in test * use jwt alg consts & fix did resolution
- Loading branch information
Showing
22 changed files
with
331 additions
and
187 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,6 @@ | ||
export const P256_DID_PREFIX = new Uint8Array([0x80, 0x24]) | ||
export const SECP256K1_DID_PREFIX = new Uint8Array([0xe7, 0x01]) | ||
export const BASE58_DID_PREFIX = 'did:key:z' // z is the multibase prefix for base58btc byte encoding | ||
|
||
export const P256_JWT_ALG = 'ES256' | ||
export const SECP256K1_JWT_ALG = 'ES256K' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,15 @@ | ||
export * from './aes' | ||
export * from './const' | ||
export * from './did' | ||
export * from './multibase' | ||
export * from './random' | ||
export * from './sha' | ||
export * from './verify' | ||
|
||
export * from './p256/ecdh' | ||
export * from './p256/ecdsa' | ||
export * from './p256/keypair' | ||
export * from './p256/plugin' | ||
export * from './p256/operations' | ||
export * from './p256/encoding' | ||
|
||
export * from './secp256k1/keypair' | ||
export * from './secp256k1/plugin' | ||
|
||
export type { DidableKey } from '@ucans/core' |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import p256Plugin from './p256/plugin' | ||
import secp256k1Plugin from './secp256k1/plugin' | ||
|
||
export const plugins = [p256Plugin] | ||
export const plugins = [p256Plugin, secp256k1Plugin] | ||
|
||
export default plugins |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import * as secp from '@noble/secp256k1' | ||
|
||
export const compressPubkey = (pubkeyBytes: Uint8Array): Uint8Array => { | ||
const hex = secp.utils.bytesToHex(pubkeyBytes) | ||
const point = secp.Point.fromHex(hex) | ||
return point.toRawBytes(true) | ||
} | ||
|
||
export const decompressPubkey = (compressed: Uint8Array): Uint8Array => { | ||
if (compressed.length !== 33) { | ||
throw new Error('Expected 33 byte compress pubkey') | ||
} | ||
const hex = secp.utils.bytesToHex(compressed) | ||
const point = secp.Point.fromHex(hex) | ||
return point.toRawBytes(false) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import * as ucan from '@ucans/core' | ||
import * as secp from '@noble/secp256k1' | ||
import * as uint8arrays from 'uint8arrays' | ||
import * as did from '../did' | ||
import { SECP256K1_JWT_ALG } from '../const' | ||
|
||
export type Secp256k1KeypairOptions = { | ||
exportable: boolean | ||
} | ||
|
||
export class Secp256k1Keypair implements ucan.DidableKey { | ||
jwtAlg = SECP256K1_JWT_ALG | ||
private publicKey: Uint8Array | ||
|
||
constructor(private privateKey: Uint8Array, private exportable: boolean) { | ||
this.publicKey = secp.getPublicKey(privateKey) | ||
} | ||
|
||
static async create( | ||
opts?: Partial<Secp256k1KeypairOptions>, | ||
): Promise<Secp256k1Keypair> { | ||
const { exportable = false } = opts || {} | ||
const privKey = secp.utils.randomPrivateKey() | ||
return new Secp256k1Keypair(privKey, exportable) | ||
} | ||
|
||
static async import( | ||
privKey: Uint8Array | string, | ||
opts?: Partial<Secp256k1KeypairOptions>, | ||
): Promise<Secp256k1Keypair> { | ||
const { exportable = false } = opts || {} | ||
const privKeyBytes = | ||
typeof privKey === 'string' | ||
? uint8arrays.fromString(privKey, 'hex') | ||
: privKey | ||
return new Secp256k1Keypair(privKeyBytes, exportable) | ||
} | ||
|
||
publicKeyBytes(): Uint8Array { | ||
return this.publicKey | ||
} | ||
|
||
publicKeyStr(encoding: ucan.Encodings = 'base64pad'): string { | ||
return uint8arrays.toString(this.publicKey, encoding) | ||
} | ||
|
||
did(): string { | ||
return did.formatDidKey(this.jwtAlg, this.publicKey) | ||
} | ||
|
||
async sign(msg: Uint8Array): Promise<Uint8Array> { | ||
const msgHash = await secp.utils.sha256(msg) | ||
return secp.sign(msgHash, this.privateKey) | ||
} | ||
|
||
async export(): Promise<Uint8Array> { | ||
if (!this.exportable) { | ||
throw new Error('Private key is not exportable') | ||
} | ||
return this.privateKey | ||
} | ||
} | ||
|
||
export default Secp256k1Keypair |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import * as secp from '@noble/secp256k1' | ||
import { SECP256K1_JWT_ALG } from '../const' | ||
import { parseDidKey } from '../did' | ||
|
||
export const verifyDidSig = async ( | ||
did: string, | ||
data: Uint8Array, | ||
sig: Uint8Array, | ||
): Promise<boolean> => { | ||
const { jwtAlg, keyBytes } = parseDidKey(did) | ||
if (jwtAlg !== SECP256K1_JWT_ALG) { | ||
throw new Error(`Not a secp256k1 did:key: ${did}`) | ||
} | ||
const msgHash = await secp.utils.sha256(data) | ||
return secp.verify(sig, msgHash, keyBytes) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { DidKeyPlugin } from '@ucans/core' | ||
import * as operations from './operations' | ||
import { SECP256K1_DID_PREFIX, SECP256K1_JWT_ALG } from '../const' | ||
|
||
export const secp256k1Plugin: DidKeyPlugin = { | ||
prefix: SECP256K1_DID_PREFIX, | ||
jwtAlg: SECP256K1_JWT_ALG, | ||
verifySignature: operations.verifyDidSig, | ||
} | ||
|
||
export default secp256k1Plugin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { parseDidKey } from './did' | ||
import plugins from './plugins' | ||
|
||
export const verifyDidSig = ( | ||
did: string, | ||
data: Uint8Array, | ||
sig: Uint8Array, | ||
): Promise<boolean> => { | ||
const parsed = parseDidKey(did) | ||
const plugin = plugins.find((p) => p.jwtAlg === parsed.jwtAlg) | ||
if (!plugin) { | ||
throw new Error(`Unsupported signature alg: :${parsed.jwtAlg}`) | ||
} | ||
return plugin.verifySignature(did, data, sig) | ||
} |
Oops, something went wrong.