Skip to content

Commit

Permalink
feat: Add JWS signature verification; Add cose key conversions and re…
Browse files Browse the repository at this point in the history
…solution (managed and external)
  • Loading branch information
nklomp committed Aug 24, 2024
1 parent e039917 commit 9f76393
Show file tree
Hide file tree
Showing 32 changed files with 1,079 additions and 254 deletions.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@
"@veramo/remote-server": "4.2.0",
"@veramo/selective-disclosure": "4.2.0",
"@veramo/url-handler": "4.2.0",
"@sphereon/ssi-sdk.agent-config": "0.29.1-unstable.75",
"@sphereon/ssi-sdk.core": "0.29.1-unstable.75",
"@sphereon/ssi-types": "0.29.1-unstable.75",
"@sphereon/ssi-sdk.agent-config": "0.29.1-unstable.161",
"@sphereon/ssi-sdk.core": "0.29.1-unstable.161",
"@sphereon/ssi-types": "0.29.1-unstable.161",
"@digitalcredentials/ed25519-verification-key-2020": "3.2.2",
"*>@digitalcredentials/ed25519-verification-key-2020": "3.2.2",
"did-jwt": "6.11.6",
Expand Down
2 changes: 1 addition & 1 deletion packages/did-provider-jwk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"@ethersproject/random": "^5.7.0",
"@sphereon/ssi-sdk-ext.did-utils": "workspace:*",
"@sphereon/ssi-sdk-ext.key-utils": "workspace:*",
"@sphereon/ssi-types": "0.29.1-unstable.75",
"@sphereon/ssi-types": "0.29.1-unstable.161",
"@stablelib/ed25519": "^1.0.3",
"@veramo/core": "4.2.0",
"@veramo/did-manager": "4.2.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/did-provider-key/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"devDependencies": {
"@sphereon/ssi-sdk-ext.key-manager": "workspace:*",
"@sphereon/ssi-sdk-ext.kms-local": "workspace:*",
"@sphereon/ssi-sdk.dev": "0.29.1-unstable.75",
"@sphereon/ssi-sdk.dev": "0.29.1-unstable.161",
"@veramo/did-resolver": "4.2.0",
"@veramo/key-manager": "4.2.0"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/did-resolver-jwk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"build:clean": "tsc --build --clean && tsc --build"
},
"dependencies": {
"@sphereon/ssi-types": "0.29.1-unstable.75",
"@sphereon/ssi-types": "0.29.1-unstable.161",
"base64url": "^3.0.1",
"debug": "^4.3.4",
"did-resolver": "^4.1.0",
Expand Down
5 changes: 3 additions & 2 deletions packages/did-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
"@sphereon/did-uni-client": "^0.6.3",
"@sphereon/ssi-sdk-ext.key-utils": "workspace:*",
"@sphereon/ssi-sdk-ext.x509-utils": "workspace:*",
"@sphereon/ssi-sdk.agent-config": "0.29.1-unstable.75",
"@sphereon/ssi-sdk.core": "0.29.1-unstable.75",
"@sphereon/ssi-sdk.agent-config": "0.29.1-unstable.161",
"@sphereon/ssi-types": "0.29.1-unstable.161",
"@sphereon/ssi-sdk.core": "0.29.1-unstable.161",
"@stablelib/ed25519": "^1.0.3",
"@veramo/core": "4.2.0",
"@veramo/utils": "4.2.0",
Expand Down
22 changes: 12 additions & 10 deletions packages/did-utils/src/did-functions.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
import { computeAddress } from '@ethersproject/transactions'
import { UniResolver } from '@sphereon/did-uni-client'
import {
ENC_KEY_ALGS,
JWK,
JwkKeyUse,
keyTypeFromCryptographicSuite,
signatureAlgorithmFromKey,
TKeyType,
toJwk,
} from '@sphereon/ssi-sdk-ext.key-utils'
import { ENC_KEY_ALGS, JwkKeyUse, keyTypeFromCryptographicSuite, signatureAlgorithmFromKey, TKeyType, toJwk } from '@sphereon/ssi-sdk-ext.key-utils'
import { base64ToHex, hexKeyFromPEMBasedJwk } from '@sphereon/ssi-sdk-ext.x509-utils'
import { base58ToBytes, base64ToBytes, bytesToHex, hexToBytes, multibaseKeyToBytes } from '@sphereon/ssi-sdk.core'
import { JWK } from '@sphereon/ssi-types'
import { convertPublicKeyToX25519 } from '@stablelib/ed25519'
import { DIDDocument, DIDDocumentSection, DIDResolutionResult, IAgentContext, IDIDManager, IIdentifier, IKey, IResolver } from '@veramo/core'
import {
Expand Down Expand Up @@ -332,6 +325,15 @@ export async function dereferenceDidKeysWithJwkSupport(
})
}

export function jwkTtoPublicKeyHex(jwk: JWK): string {
// todo: Hacky way to convert this to a VM. Should extract the logic from the below methods
// @ts-ignore
const vm: _ExtendedVerificationMethod = {
publicKeyJwk: jwk,
}
return extractPublicKeyHexWithJwkSupport(vm)
}

/**
* Converts the publicKey of a VerificationMethod to hex encoding (publicKeyHex)
*
Expand Down Expand Up @@ -428,7 +430,7 @@ function extractPublicKeyBytes(pk: VerificationMethod): Uint8Array {
}

export function verificationMethodToJwk(vm: VerificationMethod): JWK {
let jwk: JWK | undefined = vm.publicKeyJwk
let jwk: JWK | undefined = vm.publicKeyJwk as JWK
if (!jwk) {
let publicKeyHex = vm.publicKeyHex ?? u8a.toString(extractPublicKeyBytes(vm), 'hex')
jwk = toJwk(publicKeyHex, keyTypeFromCryptographicSuite({ suite: vm.type }))
Expand Down
6 changes: 3 additions & 3 deletions packages/identifier-resolution/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"generate-plugin-schema": "sphereon dev generate-plugin-schema"
},
"dependencies": {
"@sphereon/ssi-sdk.agent-config": "0.29.1-unstable.75",
"@sphereon/ssi-types": "0.29.1-unstable.75",
"@sphereon/ssi-sdk.agent-config": "0.29.1-unstable.161",
"@sphereon/ssi-types": "0.29.1-unstable.161",
"@sphereon/ssi-sdk-ext.key-utils": "workspace:*",
"@sphereon/ssi-sdk-ext.did-utils": "workspace:*",
"@sphereon/ssi-sdk-ext.x509-utils": "workspace:*",
Expand All @@ -27,7 +27,7 @@
"debug": "^4.3.4"
},
"devDependencies": {
"@sphereon/ssi-sdk.dev": "0.29.1-unstable.75",
"@sphereon/ssi-sdk.dev": "0.29.1-unstable.161",
"@sphereon/ssi-sdk-ext.kms-local": "workspace:*",
"@sphereon/ssi-sdk-ext.key-manager": "workspace:*",
"@sphereon/ssi-sdk-ext.did-provider-jwk": "workspace:*",
Expand Down
34 changes: 33 additions & 1 deletion packages/identifier-resolution/src/agent/IdentifierResolution.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { IAgentContext, IAgentPlugin, IDIDManager, IKeyManager } from '@veramo/core'
import { ensureManagedIdentifierResult, ManagedIdentifierKeyOpts, ManagedIdentifierKeyResult, ManagedIdentifierOptsOrResult, schema } from '..'
import {
ensureManagedIdentifierResult,
ExternalIdentifierCoseKeyOpts,
ExternalIdentifierCoseKeyResult,
ExternalIdentifierJwkOpts,
ExternalIdentifierJwkResult,
ManagedIdentifierKeyOpts,
ManagedIdentifierKeyResult,
ManagedIdentifierOptsOrResult,
schema,
} from '..'
import { resolveExternalIdentifier } from '../functions'
import {
ExternalIdentifierDidOpts,
Expand All @@ -9,6 +19,8 @@ import {
ExternalIdentifierX5cOpts,
ExternalIdentifierX5cResult,
IIdentifierResolution,
ManagedIdentifierCoseKeyOpts,
ManagedIdentifierCoseKeyResult,
ManagedIdentifierDidOpts,
ManagedIdentifierDidResult,
ManagedIdentifierJwkOpts,
Expand All @@ -34,10 +46,13 @@ export class IdentifierResolution implements IAgentPlugin {
identifierManagedGetByJwk: this.identifierGetManagedByJwk.bind(this),
identifierManagedGetByX5c: this.identifierGetManagedByX5c.bind(this),
identifierManagedGetByKey: this.identifierGetManagedByKey.bind(this),
identifierManagedGetByCoseKey: this.identifierGetManagedByCoseKey.bind(this),

identifierExternalResolve: this.identifierResolveExternal.bind(this),
identifierExternalResolveByDid: this.identifierExternalResolveByDid.bind(this),
identifierExternalResolveByX5c: this.identifierExternalResolveByX5c.bind(this),
identifierExternalResolveByJwk: this.identifierExternalResolveByJwk.bind(this),
identifierExternalResolveByCoseKey: this.identifierExternalResolveByCoseKey.bind(this),

// todo: JWKSet, oidc-discovery, oid4vci-issuer etc. Anything we already can resolve and need keys of
}
Expand Down Expand Up @@ -84,6 +99,13 @@ export class IdentifierResolution implements IAgentPlugin {
return (await this.identifierGetManaged({ ...args, method: 'key' }, context)) as ManagedIdentifierKeyResult
}

private async identifierGetManagedByCoseKey(
args: ManagedIdentifierCoseKeyOpts,
context: IAgentContext<IKeyManager & IIdentifierResolution>
): Promise<ManagedIdentifierCoseKeyResult> {
return (await this.identifierGetManaged({ ...args, method: 'cose_key' }, context)) as ManagedIdentifierCoseKeyResult
}

private async identifierGetManagedByJwk(
args: ManagedIdentifierJwkOpts,
context: IAgentContext<IKeyManager & IIdentifierResolution>
Expand All @@ -109,4 +131,14 @@ export class IdentifierResolution implements IAgentPlugin {
private async identifierExternalResolveByX5c(args: ExternalIdentifierX5cOpts, context: IAgentContext<any>): Promise<ExternalIdentifierX5cResult> {
return (await this.identifierResolveExternal({ ...args, method: 'x5c' }, context)) as ExternalIdentifierX5cResult
}

private async identifierExternalResolveByCoseKey(
args: ExternalIdentifierCoseKeyOpts,
context: IAgentContext<any>
): Promise<ExternalIdentifierCoseKeyResult> {
return (await this.identifierResolveExternal({ ...args, method: 'cose_key' }, context)) as ExternalIdentifierCoseKeyResult
}
private async identifierExternalResolveByJwk(args: ExternalIdentifierJwkOpts, context: IAgentContext<any>): Promise<ExternalIdentifierJwkResult> {
return (await this.identifierResolveExternal({ ...args, method: 'jwk' }, context)) as ExternalIdentifierJwkResult
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { didDocumentToJwks, getAgentResolver } from '@sphereon/ssi-sdk-ext.did-utils'
import { calculateJwkThumbprint, JWK } from '@sphereon/ssi-sdk-ext.key-utils'
import { didDocumentToJwks, getAgentResolver, jwkTtoPublicKeyHex } from '@sphereon/ssi-sdk-ext.did-utils'
import { calculateJwkThumbprint, coseKeyToJwk } from '@sphereon/ssi-sdk-ext.key-utils'
import {
getSubjectDN,
pemOrDerToX509Certificate,
Expand All @@ -8,20 +8,26 @@ import {
X509ValidationResult,
} from '@sphereon/ssi-sdk-ext.x509-utils'
import { contextHasPlugin } from '@sphereon/ssi-sdk.agent-config'
import { IParsedDID, parseDid } from '@sphereon/ssi-types'
import { IParsedDID, JWK, parseDid } from '@sphereon/ssi-types'
import { IAgentContext, IDIDManager, IResolver } from '@veramo/core'
import { isDefined } from '@veramo/utils'
import { CryptoEngine, setEngine } from 'pkijs'
import {
ExternalIdentifierCoseKeyOpts,
ExternalIdentifierCoseKeyResult,
ExternalIdentifierDidOpts,
ExternalIdentifierDidResult,
ExternalIdentifierJwkOpts,
ExternalIdentifierJwkResult,
ExternalIdentifierMethod,
ExternalIdentifierOpts,
ExternalIdentifierResult,
ExternalIdentifierX5cOpts,
ExternalIdentifierX5cResult,
ExternalJwkInfo,
isExternalIdentifierCoseKeyOpts,
isExternalIdentifierDidOpts,
isExternalIdentifierJwkOpts,
isExternalIdentifierJwksUrlOpts,
isExternalIdentifierKidOpts,
isExternalIdentifierOidcDiscoveryOpts,
Expand All @@ -39,6 +45,10 @@ export async function resolveExternalIdentifier(
return resolveExternalDidIdentifier(opts, context)
} else if (isExternalIdentifierX5cOpts(opts)) {
return resolveExternalX5cIdentifier(opts, context)
} else if (isExternalIdentifierJwkOpts(opts)) {
return resolveExternalJwkIdentifier(opts, context)
} else if (isExternalIdentifierCoseKeyOpts(opts)) {
return resolveExternalCoseKeyIdentifier(opts, context)
} else if (isExternalIdentifierKidOpts(opts)) {
method = 'kid'
} else if (isExternalIdentifierJwksUrlOpts(opts)) {
Expand Down Expand Up @@ -82,6 +92,7 @@ export async function resolveExternalX5cIdentifier(
chain: opts.identifier,
trustAnchors: opts.trustAnchors ?? [],
verificationTime: opts.verificationTime,
opts,
})
}
if (verificationResult.certificateChain) {
Expand All @@ -90,6 +101,7 @@ export async function resolveExternalX5cIdentifier(
jwk: cert.publicKeyJWK,
kid: cert.subject.dn.DN,
jwkThumbprint: calculateJwkThumbprint({ jwk: cert.publicKeyJWK }),
publicKeyHex: jwkTtoPublicKeyHex(cert.publicKeyJWK),
} satisfies ExternalJwkInfo
})
}
Expand All @@ -108,6 +120,7 @@ export async function resolveExternalX5cIdentifier(
jwk,
kid: getSubjectDN(cert).DN,
jwkThumbprint: calculateJwkThumbprint({ jwk }),
publicKeyHex: jwkTtoPublicKeyHex(jwk),
} satisfies ExternalJwkInfo
})
)
Expand All @@ -129,6 +142,78 @@ export async function resolveExternalX5cIdentifier(
}
}

/**
* Resolves a JWK. Normally this is just returning the JWK, but in case the JWK contains a x5c the chain is validated
* @param opts
* @param context
*/
export async function resolveExternalJwkIdentifier(
opts: ExternalIdentifierJwkOpts & {
x5c?: ExternalIdentifierX5cOpts
},
context: IAgentContext<any>
): Promise<ExternalIdentifierJwkResult> {
if (!isExternalIdentifierJwkOpts(opts)) {
return Promise.reject('External JWK Identifier args need to be provided')
}
const jwk = opts.identifier
let x5c: ExternalIdentifierX5cResult | undefined = undefined
if (jwk.x5c) {
x5c = await resolveExternalX5cIdentifier({ ...opts.x5c, identifier: jwk.x5c }, context)
}
const jwkThumbprint = calculateJwkThumbprint({ jwk })
return {
method: 'jwk',
jwk,
jwks: [
{
jwk,
jwkThumbprint,
kid: jwk.kid,
publicKeyHex: jwkTtoPublicKeyHex(jwk),
},
],
x5c,
} satisfies ExternalIdentifierJwkResult
}

/**
* Resolves a JWK. Normally this is just returning the JWK, but in case the JWK contains a x5c the chain is validated
* @param opts
* @param context
*/
export async function resolveExternalCoseKeyIdentifier(
opts: ExternalIdentifierCoseKeyOpts & {
x5c?: ExternalIdentifierX5cOpts
},
context: IAgentContext<any>
): Promise<ExternalIdentifierCoseKeyResult> {
if (!isExternalIdentifierCoseKeyOpts(opts)) {
return Promise.reject('External Cose Key args need to be provided')
}
// TODO: We need to do cbor conversion here as well.
const coseKey = opts.identifier
let x5c: ExternalIdentifierX5cResult | undefined = undefined
if (coseKey.x5chain) {
x5c = await resolveExternalX5cIdentifier({ ...opts.x5c, identifier: coseKey.x5chain }, context)
}
const jwk = coseKeyToJwk(coseKey)
const jwkThumbprint = calculateJwkThumbprint({ jwk })
return {
method: 'cose_key',
coseKey,
jwks: [
{
jwk,
jwkThumbprint,
kid: coseKey.kid,
publicKeyHex: jwkTtoPublicKeyHex(jwk),
},
],
x5c,
} satisfies ExternalIdentifierCoseKeyResult
}

export async function resolveExternalDidIdentifier(
opts: ExternalIdentifierDidOpts,
context: IAgentContext<IResolver & IDIDManager>
Expand Down Expand Up @@ -163,7 +248,12 @@ export async function resolveExternalDidIdentifier(
.flatMap((jwks) => jwks)
)
).map((jwk) => {
return { jwk, jwkThumbprint: calculateJwkThumbprint({ jwk }), kid: jwk.kid }
return {
jwk,
jwkThumbprint: calculateJwkThumbprint({ jwk }),
kid: jwk.kid,
publicKeyHex: jwkTtoPublicKeyHex(jwk),
}
})
: []

Expand Down
Loading

0 comments on commit 9f76393

Please sign in to comment.