Skip to content

Commit

Permalink
feat: New JWS signature service that makes use of the managed identif…
Browse files Browse the repository at this point in the history
…ier resolution, allowing for easier and more flexible JWT signing.
  • Loading branch information
nklomp committed Aug 14, 2024
1 parent dab8be1 commit 941996e
Show file tree
Hide file tree
Showing 23 changed files with 1,917 additions and 39 deletions.
10 changes: 8 additions & 2 deletions packages/identifier-resolution/src/agent/IdentifierResolution.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IAgentContext, IAgentPlugin, IDIDManager, IKeyManager } from '@veramo/core'
import { schema } from '..'
import { ManagedIdentifierKeyOpts, ManagedIdentifierKeyResult, schema } from '..'
import { getManagedIdentifier, resolveExternalIdentifier } from '../functions'
import {
ExternalIdentifierDidOpts,
Expand Down Expand Up @@ -34,6 +34,7 @@ export class IdentifierResolution implements IAgentPlugin {
identifierManagedGetByKid: this.identifierGetManagedByKid.bind(this),
identifierManagedGetByJwk: this.identifierGetManagedByJwk.bind(this),
identifierManagedGetByX5c: this.identifierGetManagedByX5c.bind(this),
identifierManagedGetByKey: this.identifierGetManagedByKey.bind(this),

identifierExternalResolve: this.identifierResolveExternal.bind(this),
identifierExternalResolveByDid: this.identifierExternalResolveByDid.bind(this),
Expand All @@ -50,7 +51,8 @@ export class IdentifierResolution implements IAgentPlugin {
}

/**
* Main method for managed identifiers. We always go through this method (also the others) as we want to integrate a plugin for anomaly detection. Having a single method helps
* Main method for managed identifiers. We always go through this method (also the other methods below) as we want to
* integrate a plugin for anomaly detection. Having a single method helps
* @param args
* @param context
* @private
Expand All @@ -70,6 +72,10 @@ export class IdentifierResolution implements IAgentPlugin {
return (await this.identifierGetManaged({ ...args, method: 'kid' }, context)) as ManagedIdentifierKidResult
}

private async identifierGetManagedByKey(args: ManagedIdentifierKeyOpts, context: IAgentContext<IKeyManager>): Promise<ManagedIdentifierKeyResult> {
return (await this.identifierGetManaged({ ...args, method: 'key' }, context)) as ManagedIdentifierKeyResult
}

private async identifierGetManagedByJwk(args: ManagedIdentifierJwkOpts, context: IAgentContext<IKeyManager>): Promise<ManagedIdentifierJwkResult> {
return (await this.identifierGetManaged({ ...args, method: 'jwk' }, context)) as ManagedIdentifierJwkResult
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@ import { getFirstKeyWithRelation } from '@sphereon/ssi-sdk-ext.did-utils'
import { calculateJwkThumbprint, JWK, toJwk } from '@sphereon/ssi-sdk-ext.key-utils'
import { pemOrDerToX509Certificate } from '@sphereon/ssi-sdk-ext.x509-utils'
import { contextHasDidManager, contextHasKeyManager } from '@sphereon/ssi-sdk.agent-config'
import { IAgentContext, IIdentifier, IKeyManager } from '@veramo/core'
import { IAgentContext, IIdentifier, IKey, IKeyManager } from '@veramo/core'
import { CryptoEngine, setEngine } from 'pkijs'
import {
isManagedIdentifierDidOpts,
isManagedIdentifierDidResult,
isManagedIdentifierJwkOpts,
isManagedIdentifierKeyOpts,
isManagedIdentifierKidOpts,
isManagedIdentifierX5cOpts,
ManagedIdentifierDidOpts,
ManagedIdentifierDidResult,
ManagedIdentifierJwkOpts,
ManagedIdentifierJwkResult,
ManagedIdentifierKeyOpts,
ManagedIdentifierKeyResult,
ManagedIdentifierKidOpts,
ManagedIdentifierKidResult,
ManagedIdentifierOpts,
Expand Down Expand Up @@ -46,6 +49,32 @@ export async function getManagedKidIdentifier(
} satisfies ManagedIdentifierKidResult
}

/**
* This function is just a convenience function to get a common result. The user already apparently had a key, so could have called the kid version as well
* @param opts
* @param _context
*/
export async function getManagedKeyIdentifier(opts: ManagedIdentifierKeyOpts, _context?: IAgentContext<any>): Promise<ManagedIdentifierKeyResult> {
const method = 'key'
const key: IKey = opts.identifier
if (opts.kmsKeyRef && opts.kmsKeyRef !== key.kid) {
return Promise.reject(Error(`Cannot get a managed key object by providing a key and a kmsKeyRef that are different.}`))
}
const jwk = toJwk(key.publicKeyHex, key.type, { key })
const jwkThumbprint = (key.meta?.jwkThumbprint as string) ?? calculateJwkThumbprint({ jwk })
const kid = opts.kid ?? (key.meta?.verificationMethod?.id as string) ?? jwkThumbprint
const issuer = opts.issuer ?? kid // The different identifiers should set the value. Defaults to the kid
return {
method,
key,
jwk,
jwkThumbprint,
kid,
issuer,
kmsKeyRef: key.kid,
} satisfies ManagedIdentifierKeyResult
}

export async function getManagedDidIdentifier(opts: ManagedIdentifierDidOpts, context: IAgentContext<any>): Promise<ManagedIdentifierDidResult> {
const method = 'did'
if (!contextHasDidManager(context)) {
Expand Down Expand Up @@ -166,6 +195,8 @@ export async function getManagedIdentifier(
resolutionResult = await getManagedJwkIdentifier(opts, context)
} else if (isManagedIdentifierX5cOpts(opts)) {
resolutionResult = await getManagedX5cIdentifier(opts, context)
} else if (isManagedIdentifierKeyOpts(opts)) {
resolutionResult = await getManagedKeyIdentifier(opts, context)
} else {
return Promise.reject(Error(`Could not determine identifier method. Please provide explicitly`))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
ManagedIdentifierDidResult,
ManagedIdentifierJwkOpts,
ManagedIdentifierJwkResult,
ManagedIdentifierKeyOpts,
ManagedIdentifierKeyResult,
ManagedIdentifierKidOpts,
ManagedIdentifierKidResult,
ManagedIdentifierOpts,
Expand All @@ -26,6 +28,8 @@ import {
export interface IIdentifierResolution extends IPluginMethodMap {
/**
* Main method for managed identifiers. We always go through this method (also the others) as we want to integrate a plugin for anomaly detection. Having a single method helps
*
* The end result of all these methods is a common baseline response that allows to use a key from the registered KMS systems. It also provides kid and iss(uer) values that can be used in a JWT/JWS for instance
* @param args
* @param context
* @public
Expand All @@ -40,6 +44,10 @@ export interface IIdentifierResolution extends IPluginMethodMap {

identifierManagedGetByX5c(args: ManagedIdentifierX5cOpts, context: IAgentContext<IKeyManager>): Promise<ManagedIdentifierX5cResult>

identifierManagedGetByKey(args: ManagedIdentifierKeyOpts, context: IAgentContext<IKeyManager>): Promise<ManagedIdentifierKeyResult>

// TODO: We can create a custom managed identifier method allowing developers to register a callback function to get their implementation hooked up. Needs more investigation as it would also impact the KMS

/**
* Main method for external identifiers. We always go through this method (also the others) as we want to integrate a plugin for anomaly detection. Having a single method helps
* @param args
Expand Down
12 changes: 11 additions & 1 deletion packages/identifier-resolution/src/types/common.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { JWK } from '@sphereon/ssi-sdk-ext.key-utils'
import { IIdentifier } from '@veramo/core'
import { IIdentifier, IKey } from '@veramo/core'
import { ExternalIdentifierType } from './externalIdentifierTypes'
import { ManagedIdentifierType } from './managedIdentifierTypes'

Expand Down Expand Up @@ -32,6 +32,16 @@ export function isKidIdentifier(identifier: ManagedIdentifierType | ExternalIden
return typeof identifier === 'string' && !identifier.startsWith('did:')
}

export function isKeyIdentifier(identifier: ManagedIdentifierType): identifier is IKey {
return (
typeof identifier === 'string' &&
!Array.isArray(identifier) &&
typeof identifier === 'object' &&
`kid` in identifier &&
'publicKeyHex' in identifier
)
}

export function isX5cIdentifier(identifier: ManagedIdentifierType | ExternalIdentifierType): identifier is string[] {
return Array.isArray(identifier) && identifier.length > 0 // todo: Do we want to do additional validation? We know it must be DER and thus hex for instance
}
44 changes: 35 additions & 9 deletions packages/identifier-resolution/src/types/managedIdentifierTypes.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
import { JWK } from '@sphereon/ssi-sdk-ext.key-utils'
import { DIDDocumentSection, IIdentifier, IKey, TKeyType } from '@veramo/core'
import { isDidIdentifier, isJwkIdentifier, isKidIdentifier, isX5cIdentifier, JwkInfo } from './common'
import { isDidIdentifier, isJwkIdentifier, isKeyIdentifier, isKidIdentifier, isX5cIdentifier, JwkInfo } from './common'

/**
* Use whenever we need to pass in an identifier. We can pass in kids, DIDs, IIdentifier objects and x5chains
*
* The functions below can be used to check the type, and they also provide the proper runtime types
* The functions below can be used to check the type, and they also provide the proper 'runtime' types
*/
export type ManagedIdentifierType = IIdentifier /*did*/ | string /*did or kid*/ | string[] /*x5c*/ | JWK

export type ManagedIdentifierOpts = (ManagedIdentifierJwkOpts | ManagedIdentifierX5cOpts | ManagedIdentifierDidOpts | ManagedIdentifierKidOpts) &
export type ManagedIdentifierType = IIdentifier /*did*/ | string /*did or kid*/ | string[] /*x5c*/ | JWK | IKey

export type ManagedIdentifierOpts = (
| ManagedIdentifierJwkOpts
| ManagedIdentifierX5cOpts
| ManagedIdentifierDidOpts
| ManagedIdentifierKidOpts
| ManagedIdentifierKeyOpts
) &
ManagedIdentifierOptsBase

export type ManagedIdentifierOptsBase = {
method?: ManagedIdentifierMethod // If provided always takes precedences otherwise it will be inferred from the identifier
identifier: ManagedIdentifierType
kmsKeyRef?: string
kmsKeyRef?: string // The key reference for the KMS system. If provided this value will be used to determine the appropriate key. Otherwise it will be inferred
issuer?: string // can be used when a specific issuer needs to end up, for instance when signing JWTs. Will be returned or inferred if not provided
kid?: string // can be used when a specific kid value needs to be used. For instance when signing JWTs. Will be returned or inferred if not provided
}
Expand Down Expand Up @@ -45,6 +51,16 @@ export function isManagedIdentifierKidOpts(opts: ManagedIdentifierOptsBase): opt
return ('method' in opts && opts.method === 'kid') || isKidIdentifier(identifier)
}

export type ManagedIdentifierKeyOpts = Omit<ManagedIdentifierOptsBase, 'method'> & {
method?: 'key'
identifier: IKey
}

export function isManagedIdentifierKeyOpts(opts: ManagedIdentifierOptsBase): opts is ManagedIdentifierKidOpts {
const { identifier } = opts
return ('method' in opts && opts.method === 'key') || isKeyIdentifier(identifier)
}

export type ManagedIdentifierJwkOpts = Omit<ManagedIdentifierOptsBase, 'method'> & {
method?: 'jwk'
identifier: JWK
Expand Down Expand Up @@ -80,7 +96,7 @@ export function isManagedIdentifierDidResult(object: IManagedIdentifierResultBas
return object!! && typeof object === 'object' && 'method' in object && object.method === 'did'
}

export function isManagedIdentifierX5cResult(object: IManagedIdentifierResultBase): object is ManagedIdentifierDidResult {
export function isManagedIdentifierX5cResult(object: IManagedIdentifierResultBase): object is ManagedIdentifierX5cResult {
return object!! && typeof object === 'object' && 'method' in object && object.method === 'x5c'
}

Expand All @@ -92,6 +108,10 @@ export function isManagedIdentifierKidResult(object: IManagedIdentifierResultBas
return object!! && typeof object === 'object' && 'method' in object && object.method === 'kid'
}

export function isManagedIdentifierKeyResult(object: IManagedIdentifierResultBase): object is ManagedIdentifierKeyResult {
return object!! && typeof object === 'object' && 'method' in object && object.method === 'key'
}

export interface ManagedIdentifierDidResult extends IManagedIdentifierResultBase {
method: 'did'
identifier: IIdentifier
Expand All @@ -114,13 +134,19 @@ export interface ManagedIdentifierKidResult extends IManagedIdentifierResultBase
kid: string
}

export interface ManagedIdentifierKeyResult extends IManagedIdentifierResultBase {
method: 'key'
issuer: string
kid: string
}

export interface ManagedIdentifierX5cResult extends IManagedIdentifierResultBase {
method: 'x5c'
x5c: string[]
certificate: any // Certificate(JSON_, but trips schema generator. Probably want to create our own DTO
}

export type ManagedIdentifierMethod = 'did' | 'jwk' | 'x5c' | 'kid'
export type ManagedIdentifierMethod = 'did' | 'jwk' | 'x5c' | 'kid' | 'key'

export type ManagedIdentifierResult = IManagedIdentifierResultBase &
(ManagedIdentifierX5cResult | ManagedIdentifierDidResult | ManagedIdentifierJwkResult | ManagedIdentifierKidResult)
(ManagedIdentifierX5cResult | ManagedIdentifierDidResult | ManagedIdentifierJwkResult | ManagedIdentifierKidResult | ManagedIdentifierKeyResult)
138 changes: 138 additions & 0 deletions packages/jwt-service/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Change Log

All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

# [0.24.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.23.0...v0.24.0) (2024-08-01)

**Note:** Version bump only for package @sphereon/ssi-sdk-ext.mnemonic-seed-manager

# [0.23.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.22.0...v0.23.0) (2024-07-23)

**Note:** Version bump only for package @sphereon/ssi-sdk-ext.mnemonic-seed-manager

# [0.22.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.21.0...v0.22.0) (2024-07-02)

**Note:** Version bump only for package @sphereon/ssi-sdk-ext.mnemonic-seed-manager

# [0.21.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.20.0...v0.21.0) (2024-06-19)

### Bug Fixes

- Multiple DID EBSI fixes ([131faa0](https://github.com/Sphereon-Opensource/SSI-SDK/commit/131faa0b583063cb3d8d5e77a33f337a23b90536))

# [0.20.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.19.0...v0.20.0) (2024-06-13)

**Note:** Version bump only for package @sphereon/ssi-sdk-ext.mnemonic-seed-manager

# [0.19.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.18.2...v0.19.0) (2024-04-25)

**Note:** Version bump only for package @sphereon/ssi-sdk-ext.mnemonic-seed-manager

## [0.18.2](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.18.1...v0.18.2) (2024-04-24)

**Note:** Version bump only for package @sphereon/ssi-sdk-ext.mnemonic-seed-manager

## [0.18.1](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.18.0...v0.18.1) (2024-04-04)

**Note:** Version bump only for package @sphereon/ssi-sdk-ext.mnemonic-seed-manager

# [0.18.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.17.0...v0.18.0) (2024-03-19)

**Note:** Version bump only for package @sphereon/ssi-sdk-ext.mnemonic-seed-manager

# [0.17.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.16.0...v0.17.0) (2024-02-29)

**Note:** Version bump only for package @sphereon/ssi-sdk-ext.mnemonic-seed-manager

# [0.16.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.15.0...v0.16.0) (2024-01-13)

### Bug Fixes

- did:key ebsi / jcs codec value was wrong ([a71279e](https://github.com/Sphereon-Opensource/SSI-SDK/commit/a71279e3b79bff4add9fa4c889459264419accc6))

# [0.15.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.14.1...v0.15.0) (2023-09-30)

**Note:** Version bump only for package @sphereon/ssi-sdk-ext.mnemonic-seed-manager

## [0.14.1](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.14.0...v0.14.1) (2023-09-28)

### Bug Fixes

- public key mapping updates, fixing ed25519 with multibase encoding ([489d4f2](https://github.com/Sphereon-Opensource/SSI-SDK/commit/489d4f20e0f354eb50b1a16a91472d4e85588113))

# [0.14.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.13.0...v0.14.0) (2023-08-09)

### Bug Fixes

- RSA import fixes ([77704a2](https://github.com/Sphereon-Opensource/SSI-SDK/commit/77704a2064e1c1d3ffc23e580ddbb36063fc70ae))

# [0.13.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.12.1...v0.13.0) (2023-07-30)

### Features

- Add agent resolver method ([3c7b21e](https://github.com/Sphereon-Opensource/SSI-SDK/commit/3c7b21e13538fac64581c0c73d0450ef6e9b56f0))

## [0.12.1](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.12.0...v0.12.1) (2023-06-24)

### Bug Fixes

- Make sure we set the saltLength for RSA PSS ([e19ed6c](https://github.com/Sphereon-Opensource/SSI-SDK/commit/e19ed6c3a7b8454e8074111d33fc59a9c6bcc611))

# [0.12.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.11.0...v0.12.0) (2023-05-07)

### Features

- Move mnemonic seed generator to crypto extensions ([748a7f9](https://github.com/Sphereon-Opensource/SSI-SDK/commit/748a7f962d563c60aa543c0c6900aa0c0daea42d))
- Move mnemonic seed generator to crypto extensions ([173ef88](https://github.com/Sphereon-Opensource/SSI-SDK/commit/173ef883deafa4c87f0d589963fb36ccb8789d1b))

# [0.10.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.9.0...v0.10.0) (2023-04-30)

### Bug Fixes

- bbs+ fixes and updates ([ae9e903](https://github.com/Sphereon-Opensource/SSI-SDK/commit/ae9e9032b23036d44b3791da416229cd6db5b776))
- cleanup package.json files ([0cc08b6](https://github.com/Sphereon-Opensource/SSI-SDK/commit/0cc08b6acc168b838bff48b42fdabbdea4cd0899))

# [0.9.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.8.0...v0.9.0) (2023-03-09)

### Features

- Allow to relax JWT timing checks, where the JWT claim is slightly different from the VC claim. Used for issuance and expiration dates ([85bff6d](https://github.com/Sphereon-Opensource/SSI-SDK/commit/85bff6da21dea5d8f636ea1f55b41be00b18b002))

# [0.8.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.7.0...v0.8.0) (2022-09-03)

**Note:** Version bump only for package @sphereon/ssi-sdk-mnemonic-seed-manager

# [0.7.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.6.0...v0.7.0) (2022-08-05)

### Features

- Add migration support to mnemonic seed manager plugin. Fix some entity props in the process ([f7641f4](https://github.com/Sphereon-Opensource/SSI-SDK/commit/f7641f4f56ebe99894ddad6c6827681406d21d2e))

# [0.6.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.5.1...v0.6.0) (2022-07-01)

**Note:** Version bump only for package @sphereon/ssi-sdk-mnemonic-seed-manager

# [0.5.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.4.0...v0.5.0) (2022-02-23)

**Note:** Version bump only for package @sphereon/ssi-sdk-mnemonic-seed-manager

# [0.4.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.3.4...v0.4.0) (2022-02-11)

**Note:** Version bump only for package @sphereon/ssi-sdk-mnemonic-seed-manager

## [0.3.4](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.3.3...v0.3.4) (2022-02-11)

**Note:** Version bump only for package @sphereon/ssi-sdk-mnemonic-seed-manager

## [0.3.1](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.3.0...v0.3.1) (2022-01-28)

**Note:** Version bump only for package @sphereon/ssi-sdk-mnemonic-seed-manager

# [0.3.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.2.0...v0.3.0) (2022-01-16)

**Note:** Version bump only for package @sphereon/ssi-sdk-mnemonic-seed-manager

# [0.2.0](https://github.com/Sphereon-Opensource/SSI-SDK/compare/v0.1.0...v0.2.0) (2021-12-16)

**Note:** Version bump only for package @sphereon/ssi-sdk-mnemonic-info-generator
Loading

0 comments on commit 941996e

Please sign in to comment.