Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(crypto): remove circular dependency #1942

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 13 additions & 35 deletions packages/crypto/src/did.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,23 @@
import * as uint8arrays from 'uint8arrays'
import * as p256 from './p256/encoding'
import * as secp from './secp256k1/encoding'

import { BASE58_MULTIBASE_PREFIX, DID_KEY_PREFIX } from './const'
import plugins from './plugins'
import {
BASE58_MULTIBASE_PREFIX,
DID_KEY_PREFIX,
P256_JWT_ALG,
SECP256K1_JWT_ALG,
} from './const'
import { extractMultikey, extractPrefixedBytes, hasPrefix } from './utils'

export type ParsedMultikey = {
jwtAlg: string
keyBytes: Uint8Array
}

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')
}
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,
Expand All @@ -46,28 +32,20 @@ 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')
)
}

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))
}
11 changes: 5 additions & 6 deletions packages/crypto/src/p256/keypair.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -32,9 +33,7 @@ export class P256Keypair implements Keypair {
): Promise<P256Keypair> {
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)
}

Expand All @@ -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 {
Expand Down
14 changes: 8 additions & 6 deletions packages/crypto/src/p256/operations.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
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,
data: Uint8Array,
sig: Uint8Array,
opts?: VerifyOptions,
): Promise<boolean> => {
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)
}

Expand All @@ -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
}
Expand Down
9 changes: 7 additions & 2 deletions packages/crypto/src/p256/plugin.ts
Original file line number Diff line number Diff line change
@@ -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
13 changes: 6 additions & 7 deletions packages/crypto/src/secp256k1/keypair.ts
Original file line number Diff line number Diff line change
@@ -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 = {
Expand Down Expand Up @@ -32,9 +33,7 @@ export class Secp256k1Keypair implements Keypair {
): Promise<Secp256k1Keypair> {
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)
}

Expand All @@ -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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason to import this as just toString? if so I think it'd be nice to import it as something like ui8ToString or similar. otherwise toString is pretty generic & gotta check imports to see what it's coming from

}

did(): string {
Expand Down
10 changes: 6 additions & 4 deletions packages/crypto/src/secp256k1/operations.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
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,
data: Uint8Array,
sig: Uint8Array,
opts?: VerifyOptions,
): Promise<boolean> => {
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)
}

Expand Down
9 changes: 7 additions & 2 deletions packages/crypto/src/secp256k1/plugin.ts
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions packages/crypto/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ export type DidKeyPlugin = {
data: Uint8Array,
opts?: VerifyOptions,
) => Promise<boolean>

compressPubkey: (uncompressed: Uint8Array) => Uint8Array
decompressPubkey: (compressed: Uint8Array) => Uint8Array
}

export type VerifyOptions = {
Expand Down
23 changes: 23 additions & 0 deletions packages/crypto/src/utils.ts
Original file line number Diff line number Diff line change
@@ -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))
}
Loading