Skip to content

Commit

Permalink
Merge branch 'main' into feat/pickup-live-mode
Browse files Browse the repository at this point in the history
  • Loading branch information
genaris committed Jan 31, 2024
2 parents 180695c + 22d5bff commit b22dd3e
Show file tree
Hide file tree
Showing 15 changed files with 195 additions and 8 deletions.
6 changes: 5 additions & 1 deletion packages/askar/src/utils/askarKeyTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ const keyTypeToAskarAlg = {
},
[KeyType.P256]: {
keyAlg: KeyAlgs.EcSecp256r1,
purposes: [AskarKeyTypePurpose.KeyManagement],
purposes: [AskarKeyTypePurpose.KeyManagement, AskarKeyTypePurpose.Signing],
},
[KeyType.K256]: {
keyAlg: KeyAlgs.EcSecp256k1,
purposes: [AskarKeyTypePurpose.KeyManagement, AskarKeyTypePurpose.Signing],
},
}

Expand Down
1 change: 1 addition & 0 deletions packages/askar/src/wallet/__tests__/AskarWallet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ describe('AskarWallet basic operations', () => {
KeyType.Bls12381g2,
KeyType.Bls12381g1g2,
KeyType.P256,
KeyType.K256,
])
})

Expand Down
1 change: 1 addition & 0 deletions packages/core/src/crypto/KeyType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export enum KeyType {
P256 = 'p256',
P384 = 'p384',
P521 = 'p521',
K256 = 'k256',
}
1 change: 1 addition & 0 deletions packages/core/src/crypto/jose/jwa/alg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export enum JwaSignatureAlgorithm {
PS384 = 'PS384',
PS512 = 'PS512',
EdDSA = 'EdDSA',
ES256K = 'ES256K',
None = 'none',
}

Expand Down
1 change: 1 addition & 0 deletions packages/core/src/crypto/jose/jwa/crv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export enum JwaCurve {
P521 = 'P-521',
Ed25519 = 'Ed25519',
X25519 = 'X25519',
Secp256k1 = 'secp256k1',
}
112 changes: 112 additions & 0 deletions packages/core/src/crypto/jose/jwk/K256Jwk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import type { JwkJson } from './Jwk'
import type { JwaEncryptionAlgorithm } from '../jwa/alg'

import { TypedArrayEncoder, Buffer } from '../../../utils'
import { KeyType } from '../../KeyType'
import { JwaCurve, JwaKeyType } from '../jwa'
import { JwaSignatureAlgorithm } from '../jwa/alg'

import { Jwk } from './Jwk'
import { compress, expand } from './ecCompression'
import { hasKty, hasCrv, hasX, hasY, hasValidUse } from './validate'

export class K256Jwk extends Jwk {
public static readonly supportedEncryptionAlgorithms: JwaEncryptionAlgorithm[] = []
public static readonly supportedSignatureAlgorithms: JwaSignatureAlgorithm[] = [JwaSignatureAlgorithm.ES256K]
public static readonly keyType = KeyType.K256

public readonly x: string
public readonly y: string

public constructor({ x, y }: { x: string; y: string }) {
super()

this.x = x
this.y = y
}

public get kty() {
return JwaKeyType.EC as const
}

public get crv() {
return JwaCurve.Secp256k1 as const
}

/**
* Returns the public key of the K-256 JWK.
*
* NOTE: this is the compressed variant. We still need to add support for the
* uncompressed variant.
*/
public get publicKey() {
const publicKeyBuffer = Buffer.concat([TypedArrayEncoder.fromBase64(this.x), TypedArrayEncoder.fromBase64(this.y)])
const compressedPublicKey = compress(publicKeyBuffer)

return Buffer.from(compressedPublicKey)
}

public get keyType() {
return K256Jwk.keyType
}

public get supportedEncryptionAlgorithms() {
return K256Jwk.supportedEncryptionAlgorithms
}

public get supportedSignatureAlgorithms() {
return K256Jwk.supportedSignatureAlgorithms
}

public toJson() {
return {
...super.toJson(),
crv: this.crv,
x: this.x,
y: this.y,
} as K256JwkJson
}

public static fromJson(jwkJson: JwkJson) {
if (!isValidP256JwkPublicKey(jwkJson)) {
throw new Error("Invalid 'K-256' JWK.")
}

return new K256Jwk({
x: jwkJson.x,
y: jwkJson.y,
})
}

public static fromPublicKey(publicKey: Buffer) {
const expanded = expand(publicKey, JwaCurve.Secp256k1)
const x = expanded.slice(0, expanded.length / 2)
const y = expanded.slice(expanded.length / 2)

return new K256Jwk({
x: TypedArrayEncoder.toBase64URL(x),
y: TypedArrayEncoder.toBase64URL(y),
})
}
}

export interface K256JwkJson extends JwkJson {
kty: JwaKeyType.EC
crv: JwaCurve.Secp256k1
x: string
y: string
use?: 'sig' | 'enc'
}

export function isValidP256JwkPublicKey(jwk: JwkJson): jwk is K256JwkJson {
return (
hasKty(jwk, JwaKeyType.EC) &&
hasCrv(jwk, JwaCurve.Secp256k1) &&
hasX(jwk) &&
hasY(jwk) &&
hasValidUse(jwk, {
supportsEncrypting: true,
supportsSigning: true,
})
)
}
36 changes: 31 additions & 5 deletions packages/core/src/crypto/jose/jwk/ecCompression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@
import bigInt from 'big-integer'

import { Buffer } from '../../../utils/buffer'
import { JwaCurve } from '../jwa'

const curveToPointLength = {
'P-256': 64,
'P-384': 96,
'P-521': 132,
[JwaCurve.P256]: 64,
[JwaCurve.P384]: 96,
[JwaCurve.P521]: 132,
[JwaCurve.Secp256k1]: 64,
}

function getConstantsForCurve(curve: 'P-256' | 'P-384' | 'P-521') {
function getConstantsForCurve(curve: 'P-256' | 'P-384' | 'P-521' | 'secp256k1') {
let two, prime, b, pIdent

if (curve === 'P-256') {
Expand Down Expand Up @@ -43,6 +45,24 @@ function getConstantsForCurve(curve: 'P-256' | 'P-384' | 'P-521') {
pIdent = prime.add(1).divide(4)
}

// https://en.bitcoin.it/wiki/Secp256k1
// p = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F
// P = 2256 - 232 - 29 - 28 - 27 - 26 - 24 - 1
if (curve === JwaCurve.Secp256k1) {
two = bigInt(2)
prime = two
.pow(256)
.subtract(two.pow(32))
.subtract(two.pow(9))
.subtract(two.pow(8))
.subtract(two.pow(7))
.subtract(two.pow(6))
.subtract(two.pow(4))
.subtract(1)
b = bigInt(7)
pIdent = prime.add(1).divide(4)
}

if (!prime || !b || !pIdent) {
throw new Error(`Unsupported curve ${curve}`)
}
Expand Down Expand Up @@ -80,14 +100,20 @@ export function compress(publicKey: Uint8Array): Uint8Array {
return compressECPoint(xOctet, yOctet)
}

export function expand(publicKey: Uint8Array, curve: 'P-256' | 'P-384' | 'P-521'): Uint8Array {
export function expand(publicKey: Uint8Array, curve: 'P-256' | 'P-384' | 'P-521' | 'secp256k1'): Uint8Array {
const publicKeyComponent = Buffer.from(publicKey).toString('hex')
const { prime, b, pIdent } = getConstantsForCurve(curve)
const signY = new Number(publicKeyComponent[1]).valueOf() - 2
const x = bigInt(publicKeyComponent.substring(2), 16)

// y^2 = x^3 - 3x + b
let y = x.pow(3).subtract(x.multiply(3)).add(b).modPow(pIdent, prime)

if (curve === 'secp256k1') {
// y^2 = x^3 + 7
y = x.pow(3).add(7).modPow(pIdent, prime)
}

// If the parity doesn't match it's the *other* root
if (y.mod(2).toJSNumber() !== signY) {
// y = prime - y
Expand Down
6 changes: 5 additions & 1 deletion packages/core/src/crypto/jose/jwk/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ import { KeyType } from '../../KeyType'
import { JwaCurve, JwaKeyType } from '../jwa'

import { Ed25519Jwk } from './Ed25519Jwk'
import { K256Jwk } from './K256Jwk'
import { P256Jwk } from './P256Jwk'
import { P384Jwk } from './P384Jwk'
import { P521Jwk } from './P521Jwk'
import { X25519Jwk } from './X25519Jwk'
import { hasCrv } from './validate'

const JwkClasses = [Ed25519Jwk, P256Jwk, P384Jwk, P521Jwk, X25519Jwk] as const
const JwkClasses = [Ed25519Jwk, P256Jwk, P384Jwk, P521Jwk, X25519Jwk, K256Jwk] as const

export function getJwkFromJson(jwkJson: JwkJson): Jwk {
if (jwkJson.kty === JwaKeyType.OKP) {
Expand All @@ -25,6 +26,7 @@ export function getJwkFromJson(jwkJson: JwkJson): Jwk {
if (hasCrv(jwkJson, JwaCurve.P256)) return P256Jwk.fromJson(jwkJson)
if (hasCrv(jwkJson, JwaCurve.P384)) return P384Jwk.fromJson(jwkJson)
if (hasCrv(jwkJson, JwaCurve.P521)) return P521Jwk.fromJson(jwkJson)
if (hasCrv(jwkJson, JwaCurve.Secp256k1)) return K256Jwk.fromJson(jwkJson)
}

throw new Error(`Cannot create JWK from JSON. Unsupported JWK with kty '${jwkJson.kty}'.`)
Expand All @@ -38,6 +40,8 @@ export function getJwkFromKey(key: Key) {
if (key.keyType === KeyType.P384) return P384Jwk.fromPublicKey(key.publicKey)
if (key.keyType === KeyType.P521) return P521Jwk.fromPublicKey(key.publicKey)

if (key.keyType === KeyType.K256) return K256Jwk.fromPublicKey(key.publicKey)

throw new AriesFrameworkError(`Cannot create JWK from key. Unsupported key with type '${key.keyType}'.`)
}

Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/crypto/keyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export function isValidSeed(seed: Buffer, keyType: KeyType): boolean {
[KeyType.P256]: 64,
[KeyType.P384]: 64,
[KeyType.P521]: 64,
[KeyType.K256]: 64,
} as const

return Buffer.isBuffer(seed) && seed.length >= minimumSeedLength[keyType]
Expand All @@ -27,6 +28,7 @@ export function isValidPrivateKey(privateKey: Buffer, keyType: KeyType): boolean
[KeyType.P256]: 32,
[KeyType.P384]: 48,
[KeyType.P521]: 66,
[KeyType.K256]: 32,
} as const

return Buffer.isBuffer(privateKey) && privateKey.length === privateKeyLength[keyType]
Expand All @@ -42,6 +44,7 @@ export function isSigningSupportedForKeyType(keyType: KeyType): boolean {
[KeyType.Bls12381g1]: true,
[KeyType.Bls12381g2]: true,
[KeyType.Bls12381g1g2]: true,
[KeyType.K256]: true,
} as const

return keyTypeSigningSupportedMapping[keyType]
Expand All @@ -57,6 +60,7 @@ export function isEncryptionSupportedForKeyType(keyType: KeyType): boolean {
[KeyType.Bls12381g1]: false,
[KeyType.Bls12381g2]: false,
[KeyType.Bls12381g1g2]: false,
[KeyType.K256]: true,
} as const

return keyTypeEncryptionSupportedMapping[keyType]
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/crypto/multiCodecKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const multiCodecPrefixMap: Record<string, KeyType> = {
4608: KeyType.P256,
4609: KeyType.P384,
4610: KeyType.P521,
231: KeyType.K256,
}

export function getKeyTypeByMultiCodecPrefix(multiCodecPrefix: number): KeyType {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"@context": ["https://w3id.org/did/v1", "https://w3id.org/security/suites/jws-2020/v1"],
"id": "did:key:zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp",
"verificationMethod": [
{
"id": "did:key:zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp#zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp",
"type": "JsonWebKey2020",
"controller": "did:key:zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp",
"publicKeyJwk": {
"kty": "EC",
"crv": "secp256k1",
"x": "RwiZITTa2Dcmq-V1j-5tgPUshOLO31FbsnhVS-7lskc",
"y": "3o1-UCc3ABh757P58gDISSc4hOj9qyfSGl3SGGA7xdc"
}
}
],
"authentication": [
"did:key:zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp#zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp"
],
"assertionMethod": [
"did:key:zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp#zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp"
],
"capabilityInvocation": [
"did:key:zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp#zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp"
],
"capabilityDelegation": [
"did:key:zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp#zQ3shjRPgHQQbTtXyofk1ygghRJ75RZpXmWBMY1BKnhyz7zKp"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const keyDidMapping: Record<KeyType, KeyDidMapping> = {
[KeyType.P256]: keyDidJsonWebKey,
[KeyType.P384]: keyDidJsonWebKey,
[KeyType.P521]: keyDidJsonWebKey,
[KeyType.K256]: keyDidJsonWebKey,
}

/**
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/modules/dids/domain/keyDidDocument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const didDocumentKeyTypeMapping: Record<KeyType, (did: string, key: Key) => DidD
[KeyType.P256]: getJsonWebKey2020DidDocument,
[KeyType.P384]: getJsonWebKey2020DidDocument,
[KeyType.P521]: getJsonWebKey2020DidDocument,
[KeyType.K256]: getJsonWebKey2020DidDocument,
}

export function getDidDocumentForKey(did: string, key: Key) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import didKeyBls12381g1 from '../../../__tests__/__fixtures__/didKeyBls12381g1.j
import didKeyBls12381g1g2 from '../../../__tests__/__fixtures__/didKeyBls12381g1g2.json'
import didKeyBls12381g2 from '../../../__tests__/__fixtures__/didKeyBls12381g2.json'
import didKeyEd25519 from '../../../__tests__/__fixtures__/didKeyEd25519.json'
import didKeyK256 from '../../../__tests__/__fixtures__/didKeyK256.json'
import didKeyP256 from '../../../__tests__/__fixtures__/didKeyP256.json'
import didKeyP384 from '../../../__tests__/__fixtures__/didKeyP384.json'
import didKeyP521 from '../../../__tests__/__fixtures__/didKeyP521.json'
Expand All @@ -21,6 +22,7 @@ describe('DidKey', () => {
didKeyP256,
didKeyP384,
didKeyP521,
didKeyK256,
]

for (const documentType of documentTypes) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ const recipientOptions = getAgentOptions(
const mediatorOptions = getAgentOptions(
'Mediation Pickup Loop Mediator',
{
// Agent is shutdown during test, so we can't use in-memory wallet
endpoints: ['wss://mediator'],
},
{
Expand Down

0 comments on commit b22dd3e

Please sign in to comment.