From ef961a9e44c9d1521a1b6a39de19eb5957d61fc2 Mon Sep 17 00:00:00 2001 From: Matthieu Sieben Date: Fri, 8 Dec 2023 15:08:33 +0100 Subject: [PATCH 1/4] refactor(crypto): remove circular dependency --- packages/crypto/src/did.ts | 26 +++++++-------------- packages/crypto/src/p256/operations.ts | 14 ++++++----- packages/crypto/src/secp256k1/operations.ts | 10 ++++---- packages/crypto/src/utils.ts | 23 ++++++++++++++++++ 4 files changed, 45 insertions(+), 28 deletions(-) create mode 100644 packages/crypto/src/utils.ts diff --git a/packages/crypto/src/did.ts b/packages/crypto/src/did.ts index 7ced78a394d..3dca93582fe 100644 --- a/packages/crypto/src/did.ts +++ b/packages/crypto/src/did.ts @@ -1,13 +1,15 @@ import * as uint8arrays from 'uint8arrays' -import * as p256 from './p256/encoding' -import * as secp from './secp256k1/encoding' -import plugins from './plugins' + import { BASE58_MULTIBASE_PREFIX, DID_KEY_PREFIX, P256_JWT_ALG, SECP256K1_JWT_ALG, } from './const' +import * as p256 from './p256/encoding' +import plugins from './plugins' +import * as secp from './secp256k1/encoding' +import { extractMultikey, extractPrefixedBytes, hasPrefix } from './utils' export type ParsedMultikey = { jwtAlg: string @@ -15,13 +17,7 @@ export type ParsedMultikey = { } export const parseMultikey = (multikey: string): ParsedMultikey => { - if (!multikey.startsWith(BASE58_MULTIBASE_PREFIX)) { - throw new Error(`Incorrect prefix for multikey: ${multikey}`) - } - const prefixedBytes = uint8arrays.fromString( - multikey.slice(BASE58_MULTIBASE_PREFIX.length), - 'base58btc', - ) + const prefixedBytes = extractPrefixedBytes(multikey) const plugin = plugins.find((p) => hasPrefix(prefixedBytes, p.prefix)) if (!plugin) { throw new Error('Unsupported key type') @@ -58,16 +54,10 @@ export const formatMultikey = ( } export const parseDidKey = (did: string): ParsedMultikey => { - if (!did.startsWith(DID_KEY_PREFIX)) { - throw new Error(`Incorrect prefix for did:key: ${did}`) - } - return parseMultikey(did.slice(DID_KEY_PREFIX.length)) + const multikey = extractMultikey(did) + return parseMultikey(multikey) } export const formatDidKey = (jwtAlg: string, keyBytes: Uint8Array): string => { return DID_KEY_PREFIX + formatMultikey(jwtAlg, keyBytes) } - -const hasPrefix = (bytes: Uint8Array, prefix: Uint8Array): boolean => { - return uint8arrays.equals(prefix, bytes.subarray(0, prefix.byteLength)) -} diff --git a/packages/crypto/src/p256/operations.ts b/packages/crypto/src/p256/operations.ts index e41c494ae55..7422c282281 100644 --- a/packages/crypto/src/p256/operations.ts +++ b/packages/crypto/src/p256/operations.ts @@ -1,9 +1,10 @@ import { p256 } from '@noble/curves/p256' import { sha256 } from '@noble/hashes/sha256' -import * as ui8 from 'uint8arrays' -import { P256_JWT_ALG } from '../const' -import { parseDidKey } from '../did' +import { equals as ui8equals } from 'uint8arrays' + +import { P256_DID_PREFIX } from '../const' import { VerifyOptions } from '../types' +import { extractMultikey, extractPrefixedBytes, hasPrefix } from '../utils' export const verifyDidSig = async ( did: string, @@ -11,10 +12,11 @@ export const verifyDidSig = async ( sig: Uint8Array, opts?: VerifyOptions, ): Promise => { - const { jwtAlg, keyBytes } = parseDidKey(did) - if (jwtAlg !== P256_JWT_ALG) { + const prefixedBytes = extractPrefixedBytes(extractMultikey(did)) + if (!hasPrefix(prefixedBytes, P256_DID_PREFIX)) { throw new Error(`Not a P-256 did:key: ${did}`) } + const keyBytes = prefixedBytes.slice(P256_DID_PREFIX.length) return verifySig(keyBytes, data, sig, opts) } @@ -39,7 +41,7 @@ export const verifySig = async ( export const isCompactFormat = (sig: Uint8Array) => { try { const parsed = p256.Signature.fromCompact(sig) - return ui8.equals(parsed.toCompactRawBytes(), sig) + return ui8equals(parsed.toCompactRawBytes(), sig) } catch { return false } diff --git a/packages/crypto/src/secp256k1/operations.ts b/packages/crypto/src/secp256k1/operations.ts index bc2415feb47..9214f63014a 100644 --- a/packages/crypto/src/secp256k1/operations.ts +++ b/packages/crypto/src/secp256k1/operations.ts @@ -1,9 +1,10 @@ import { secp256k1 as k256 } from '@noble/curves/secp256k1' import { sha256 } from '@noble/hashes/sha256' import * as ui8 from 'uint8arrays' -import { SECP256K1_JWT_ALG } from '../const' -import { parseDidKey } from '../did' + +import { SECP256K1_DID_PREFIX } from '../const' import { VerifyOptions } from '../types' +import { extractMultikey, extractPrefixedBytes, hasPrefix } from '../utils' export const verifyDidSig = async ( did: string, @@ -11,10 +12,11 @@ export const verifyDidSig = async ( sig: Uint8Array, opts?: VerifyOptions, ): Promise => { - const { jwtAlg, keyBytes } = parseDidKey(did) - if (jwtAlg !== SECP256K1_JWT_ALG) { + const prefixedBytes = extractPrefixedBytes(extractMultikey(did)) + if (!hasPrefix(prefixedBytes, SECP256K1_DID_PREFIX)) { throw new Error(`Not a secp256k1 did:key: ${did}`) } + const keyBytes = prefixedBytes.slice(SECP256K1_DID_PREFIX.length) return verifySig(keyBytes, data, sig, opts) } diff --git a/packages/crypto/src/utils.ts b/packages/crypto/src/utils.ts new file mode 100644 index 00000000000..a2875758039 --- /dev/null +++ b/packages/crypto/src/utils.ts @@ -0,0 +1,23 @@ +import * as uint8arrays from 'uint8arrays' +import { BASE58_MULTIBASE_PREFIX, DID_KEY_PREFIX } from './const' + +export const extractMultikey = (did: string): string => { + if (!did.startsWith(DID_KEY_PREFIX)) { + throw new Error(`Incorrect prefix for did:key: ${did}`) + } + return did.slice(DID_KEY_PREFIX.length) +} + +export const extractPrefixedBytes = (multikey: string): Uint8Array => { + if (!multikey.startsWith(BASE58_MULTIBASE_PREFIX)) { + throw new Error(`Incorrect prefix for multikey: ${multikey}`) + } + return uint8arrays.fromString( + multikey.slice(BASE58_MULTIBASE_PREFIX.length), + 'base58btc', + ) +} + +export const hasPrefix = (bytes: Uint8Array, prefix: Uint8Array): boolean => { + return uint8arrays.equals(prefix, bytes.subarray(0, prefix.byteLength)) +} From 64270cfec179fe4e7453cc15348ea8d04b2f05c3 Mon Sep 17 00:00:00 2001 From: Matthieu Sieben Date: Fri, 8 Dec 2023 15:23:42 +0100 Subject: [PATCH 2/4] refactor(crypto): expose compress/decompress as part of the DidKeyPlugin interface --- packages/crypto/src/did.ts | 28 +++++++------------------ packages/crypto/src/p256/plugin.ts | 9 ++++++-- packages/crypto/src/secp256k1/plugin.ts | 9 ++++++-- packages/crypto/src/types.ts | 3 +++ 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/packages/crypto/src/did.ts b/packages/crypto/src/did.ts index 3dca93582fe..a8e4f03a7c1 100644 --- a/packages/crypto/src/did.ts +++ b/packages/crypto/src/did.ts @@ -1,14 +1,7 @@ import * as uint8arrays from 'uint8arrays' -import { - BASE58_MULTIBASE_PREFIX, - DID_KEY_PREFIX, - P256_JWT_ALG, - SECP256K1_JWT_ALG, -} from './const' -import * as p256 from './p256/encoding' +import { BASE58_MULTIBASE_PREFIX, DID_KEY_PREFIX } from './const' import plugins from './plugins' -import * as secp from './secp256k1/encoding' import { extractMultikey, extractPrefixedBytes, hasPrefix } from './utils' export type ParsedMultikey = { @@ -22,12 +15,9 @@ export const parseMultikey = (multikey: string): ParsedMultikey => { if (!plugin) { throw new Error('Unsupported key type') } - let keyBytes = prefixedBytes.slice(plugin.prefix.length) - if (plugin.jwtAlg === P256_JWT_ALG) { - keyBytes = p256.decompressPubkey(keyBytes) - } else if (plugin.jwtAlg === SECP256K1_JWT_ALG) { - keyBytes = secp.decompressPubkey(keyBytes) - } + const keyBytes = plugin.decompressPubkey( + prefixedBytes.slice(plugin.prefix.length), + ) return { jwtAlg: plugin.jwtAlg, keyBytes, @@ -42,12 +32,10 @@ export const formatMultikey = ( if (!plugin) { throw new Error('Unsupported key type') } - if (jwtAlg === P256_JWT_ALG) { - keyBytes = p256.compressPubkey(keyBytes) - } else if (jwtAlg === SECP256K1_JWT_ALG) { - keyBytes = secp.compressPubkey(keyBytes) - } - const prefixedBytes = uint8arrays.concat([plugin.prefix, keyBytes]) + const prefixedBytes = uint8arrays.concat([ + plugin.prefix, + plugin.compressPubkey(keyBytes), + ]) return ( BASE58_MULTIBASE_PREFIX + uint8arrays.toString(prefixedBytes, 'base58btc') ) diff --git a/packages/crypto/src/p256/plugin.ts b/packages/crypto/src/p256/plugin.ts index d2c304d0fde..4fcbafc00ca 100644 --- a/packages/crypto/src/p256/plugin.ts +++ b/packages/crypto/src/p256/plugin.ts @@ -1,11 +1,16 @@ -import * as operations from './operations' +import { verifyDidSig } from './operations' +import { compressPubkey, decompressPubkey } from './encoding' + import { DidKeyPlugin } from '../types' import { P256_DID_PREFIX, P256_JWT_ALG } from '../const' export const p256Plugin: DidKeyPlugin = { prefix: P256_DID_PREFIX, jwtAlg: P256_JWT_ALG, - verifySignature: operations.verifyDidSig, + verifySignature: verifyDidSig, + + compressPubkey, + decompressPubkey, } export default p256Plugin diff --git a/packages/crypto/src/secp256k1/plugin.ts b/packages/crypto/src/secp256k1/plugin.ts index 5184a3778fa..1e8c622b752 100644 --- a/packages/crypto/src/secp256k1/plugin.ts +++ b/packages/crypto/src/secp256k1/plugin.ts @@ -1,11 +1,16 @@ -import * as operations from './operations' +import { verifyDidSig } from './operations' +import { compressPubkey, decompressPubkey } from './encoding' + import { DidKeyPlugin } from '../types' import { SECP256K1_DID_PREFIX, SECP256K1_JWT_ALG } from '../const' export const secp256k1Plugin: DidKeyPlugin = { prefix: SECP256K1_DID_PREFIX, jwtAlg: SECP256K1_JWT_ALG, - verifySignature: operations.verifyDidSig, + verifySignature: verifyDidSig, + + compressPubkey, + decompressPubkey, } export default secp256k1Plugin diff --git a/packages/crypto/src/types.ts b/packages/crypto/src/types.ts index b664b6d4d30..c6f87b99bd3 100644 --- a/packages/crypto/src/types.ts +++ b/packages/crypto/src/types.ts @@ -22,6 +22,9 @@ export type DidKeyPlugin = { data: Uint8Array, opts?: VerifyOptions, ) => Promise + + compressPubkey: (uncompressed: Uint8Array) => Uint8Array + decompressPubkey: (compressed: Uint8Array) => Uint8Array } export type VerifyOptions = { From bfce7cbdb85177132bf4ff76b8d72b59df19fc80 Mon Sep 17 00:00:00 2001 From: Matthieu Sieben Date: Tue, 13 Feb 2024 14:25:13 +0100 Subject: [PATCH 3/4] fix(crypto): remove import from private file --- packages/crypto/src/p256/keypair.ts | 11 +++++------ packages/crypto/src/secp256k1/keypair.ts | 13 ++++++------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/crypto/src/p256/keypair.ts b/packages/crypto/src/p256/keypair.ts index 515d9a89ed7..4bc5a5c5e74 100644 --- a/packages/crypto/src/p256/keypair.ts +++ b/packages/crypto/src/p256/keypair.ts @@ -1,7 +1,8 @@ import { p256 } from '@noble/curves/p256' import { sha256 } from '@noble/hashes/sha256' -import * as uint8arrays from 'uint8arrays' -import { SupportedEncodings } from 'uint8arrays/util/bases' +import { SupportedEncodings } from 'uint8arrays/to-string' +import { fromString, toString } from 'uint8arrays' + import * as did from '../did' import { P256_JWT_ALG } from '../const' import { Keypair } from '../types' @@ -32,9 +33,7 @@ export class P256Keypair implements Keypair { ): Promise { const { exportable = false } = opts || {} const privKeyBytes = - typeof privKey === 'string' - ? uint8arrays.fromString(privKey, 'hex') - : privKey + typeof privKey === 'string' ? fromString(privKey, 'hex') : privKey return new P256Keypair(privKeyBytes, exportable) } @@ -43,7 +42,7 @@ export class P256Keypair implements Keypair { } publicKeyStr(encoding: SupportedEncodings = 'base64pad'): string { - return uint8arrays.toString(this.publicKey, encoding) + return toString(this.publicKey, encoding) } did(): string { diff --git a/packages/crypto/src/secp256k1/keypair.ts b/packages/crypto/src/secp256k1/keypair.ts index e5d4ad0dcc6..4fc2b10515f 100644 --- a/packages/crypto/src/secp256k1/keypair.ts +++ b/packages/crypto/src/secp256k1/keypair.ts @@ -1,9 +1,10 @@ import { secp256k1 as k256 } from '@noble/curves/secp256k1' import { sha256 } from '@noble/hashes/sha256' -import * as uint8arrays from 'uint8arrays' -import { SupportedEncodings } from 'uint8arrays/util/bases' -import * as did from '../did' +import { fromString, toString } from 'uint8arrays' +import { SupportedEncodings } from 'uint8arrays/to-string' + import { SECP256K1_JWT_ALG } from '../const' +import * as did from '../did' import { Keypair } from '../types' export type Secp256k1KeypairOptions = { @@ -32,9 +33,7 @@ export class Secp256k1Keypair implements Keypair { ): Promise { const { exportable = false } = opts || {} const privKeyBytes = - typeof privKey === 'string' - ? uint8arrays.fromString(privKey, 'hex') - : privKey + typeof privKey === 'string' ? fromString(privKey, 'hex') : privKey return new Secp256k1Keypair(privKeyBytes, exportable) } @@ -43,7 +42,7 @@ export class Secp256k1Keypair implements Keypair { } publicKeyStr(encoding: SupportedEncodings = 'base64pad'): string { - return uint8arrays.toString(this.publicKey, encoding) + return toString(this.publicKey, encoding) } did(): string { From f192f197a364cc3527b242d96846ba1bfa019d0b Mon Sep 17 00:00:00 2001 From: Matthieu Sieben Date: Tue, 20 Feb 2024 09:35:49 +0100 Subject: [PATCH 4/4] style(crypto): rename imports from uint8arrays --- packages/crypto/src/p256/keypair.ts | 9 ++++++--- packages/crypto/src/secp256k1/keypair.ts | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/crypto/src/p256/keypair.ts b/packages/crypto/src/p256/keypair.ts index 4bc5a5c5e74..a858fd77093 100644 --- a/packages/crypto/src/p256/keypair.ts +++ b/packages/crypto/src/p256/keypair.ts @@ -1,7 +1,10 @@ import { p256 } from '@noble/curves/p256' import { sha256 } from '@noble/hashes/sha256' import { SupportedEncodings } from 'uint8arrays/to-string' -import { fromString, toString } from 'uint8arrays' +import { + fromString as ui8FromString, + toString as ui8ToString, +} from 'uint8arrays' import * as did from '../did' import { P256_JWT_ALG } from '../const' @@ -33,7 +36,7 @@ export class P256Keypair implements Keypair { ): Promise { const { exportable = false } = opts || {} const privKeyBytes = - typeof privKey === 'string' ? fromString(privKey, 'hex') : privKey + typeof privKey === 'string' ? ui8FromString(privKey, 'hex') : privKey return new P256Keypair(privKeyBytes, exportable) } @@ -42,7 +45,7 @@ export class P256Keypair implements Keypair { } publicKeyStr(encoding: SupportedEncodings = 'base64pad'): string { - return toString(this.publicKey, encoding) + return ui8ToString(this.publicKey, encoding) } did(): string { diff --git a/packages/crypto/src/secp256k1/keypair.ts b/packages/crypto/src/secp256k1/keypair.ts index 4fc2b10515f..4391e01add9 100644 --- a/packages/crypto/src/secp256k1/keypair.ts +++ b/packages/crypto/src/secp256k1/keypair.ts @@ -1,6 +1,9 @@ import { secp256k1 as k256 } from '@noble/curves/secp256k1' import { sha256 } from '@noble/hashes/sha256' -import { fromString, toString } from 'uint8arrays' +import { + fromString as ui8FromString, + toString as ui8ToString, +} from 'uint8arrays' import { SupportedEncodings } from 'uint8arrays/to-string' import { SECP256K1_JWT_ALG } from '../const' @@ -33,7 +36,7 @@ export class Secp256k1Keypair implements Keypair { ): Promise { const { exportable = false } = opts || {} const privKeyBytes = - typeof privKey === 'string' ? fromString(privKey, 'hex') : privKey + typeof privKey === 'string' ? ui8FromString(privKey, 'hex') : privKey return new Secp256k1Keypair(privKeyBytes, exportable) } @@ -42,7 +45,7 @@ export class Secp256k1Keypair implements Keypair { } publicKeyStr(encoding: SupportedEncodings = 'base64pad'): string { - return toString(this.publicKey, encoding) + return ui8ToString(this.publicKey, encoding) } did(): string {