@veramo/credential-ld 3.1.1-next.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +6 -0
- package/build/action-handler.d.ts +28 -0
- package/build/action-handler.d.ts.map +1 -0
- package/build/action-handler.js +155 -0
- package/build/action-handler.js.map +1 -0
- package/build/index.d.ts +15 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +31 -0
- package/build/index.js.map +1 -0
- package/build/ld-context-loader.d.ts +16 -0
- package/build/ld-context-loader.d.ts.map +1 -0
- package/build/ld-context-loader.js +33 -0
- package/build/ld-context-loader.js.map +1 -0
- package/build/ld-credential-module.d.ts +25 -0
- package/build/ld-credential-module.d.ts.map +1 -0
- package/build/ld-credential-module.js +148 -0
- package/build/ld-credential-module.js.map +1 -0
- package/build/ld-default-contexts.d.ts +2 -0
- package/build/ld-default-contexts.d.ts.map +1 -0
- package/build/ld-default-contexts.js +42 -0
- package/build/ld-default-contexts.js.map +1 -0
- package/build/ld-suite-loader.d.ts +15 -0
- package/build/ld-suite-loader.d.ts.map +1 -0
- package/build/ld-suite-loader.js +28 -0
- package/build/ld-suite-loader.js.map +1 -0
- package/build/ld-suites.d.ts +13 -0
- package/build/ld-suites.d.ts.map +1 -0
- package/build/ld-suites.js +15 -0
- package/build/ld-suites.js.map +1 -0
- package/build/suites/EcdsaSecp256k1RecoverySignature2020.d.ts +11 -0
- package/build/suites/EcdsaSecp256k1RecoverySignature2020.d.ts.map +1 -0
- package/build/suites/EcdsaSecp256k1RecoverySignature2020.js +100 -0
- package/build/suites/EcdsaSecp256k1RecoverySignature2020.js.map +1 -0
- package/build/suites/Ed25519Signature2018.d.ts +11 -0
- package/build/suites/Ed25519Signature2018.d.ts.map +1 -0
- package/build/suites/Ed25519Signature2018.js +92 -0
- package/build/suites/Ed25519Signature2018.js.map +1 -0
- package/build/tsdoc-metadata.json +11 -0
- package/build/types.d.ts +174 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +3 -0
- package/build/types.js.map +1 -0
- package/package.json +50 -0
- package/plugin.schema.json +398 -0
- package/src/action-handler.ts +229 -0
- package/src/index.ts +14 -0
- package/src/ld-context-loader.ts +29 -0
- package/src/ld-credential-module.ts +170 -0
- package/src/ld-default-contexts.ts +21 -0
- package/src/ld-suite-loader.ts +29 -0
- package/src/ld-suites.ts +45 -0
- package/src/suites/EcdsaSecp256k1RecoverySignature2020.ts +83 -0
- package/src/suites/Ed25519Signature2018.ts +75 -0
- package/src/types.ts +208 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CredentialPayload,
|
|
3
|
+
IAgentContext,
|
|
4
|
+
IAgentPlugin,
|
|
5
|
+
IIdentifier,
|
|
6
|
+
IKey,
|
|
7
|
+
IResolver,
|
|
8
|
+
PresentationPayload,
|
|
9
|
+
VerifiableCredential,
|
|
10
|
+
VerifiablePresentation,
|
|
11
|
+
} from '@veramo/core'
|
|
12
|
+
import { schema, VeramoLdSignature } from './'
|
|
13
|
+
import Debug from 'debug'
|
|
14
|
+
import { LdContextLoader } from './ld-context-loader'
|
|
15
|
+
import {
|
|
16
|
+
_ExtendedIKey,
|
|
17
|
+
extractIssuer,
|
|
18
|
+
isDefined,
|
|
19
|
+
MANDATORY_CREDENTIAL_CONTEXT,
|
|
20
|
+
mapIdentifierKeysToDoc,
|
|
21
|
+
OrPromise,
|
|
22
|
+
processEntryToArray,
|
|
23
|
+
RecordLike,
|
|
24
|
+
} from '@veramo/utils'
|
|
25
|
+
|
|
26
|
+
import { LdCredentialModule } from './ld-credential-module'
|
|
27
|
+
import { LdSuiteLoader } from './ld-suite-loader'
|
|
28
|
+
import {
|
|
29
|
+
ContextDoc,
|
|
30
|
+
ICreateVerifiableCredentialLDArgs,
|
|
31
|
+
ICreateVerifiablePresentationLDArgs,
|
|
32
|
+
ICredentialIssuerLD,
|
|
33
|
+
IRequiredContext,
|
|
34
|
+
IVerifyCredentialLDArgs,
|
|
35
|
+
IVerifyPresentationLDArgs,
|
|
36
|
+
} from './types'
|
|
37
|
+
|
|
38
|
+
const debug = Debug('veramo:w3c:action-handler')
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A Veramo plugin that implements the {@link ICredentialIssuerLD} methods.
|
|
42
|
+
*
|
|
43
|
+
* @public
|
|
44
|
+
*/
|
|
45
|
+
export class CredentialIssuerLD implements IAgentPlugin {
|
|
46
|
+
readonly methods: ICredentialIssuerLD
|
|
47
|
+
readonly schema = schema.ICredentialIssuer
|
|
48
|
+
|
|
49
|
+
private ldCredentialModule: LdCredentialModule
|
|
50
|
+
|
|
51
|
+
constructor(options: { contextMaps: RecordLike<OrPromise<ContextDoc>>[]; suites: VeramoLdSignature[] }) {
|
|
52
|
+
this.ldCredentialModule = new LdCredentialModule({
|
|
53
|
+
ldContextLoader: new LdContextLoader({ contextsPaths: options.contextMaps }),
|
|
54
|
+
ldSuiteLoader: new LdSuiteLoader({ veramoLdSignatures: options.suites }),
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
this.methods = {
|
|
58
|
+
createVerifiablePresentationLD: this.createVerifiablePresentationLD.bind(this),
|
|
59
|
+
createVerifiableCredentialLD: this.createVerifiableCredentialLD.bind(this),
|
|
60
|
+
verifyCredentialLD: this.verifyCredentialLD.bind(this),
|
|
61
|
+
verifyPresentationLD: this.verifyPresentationLD.bind(this),
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** {@inheritdoc ICredentialIssuerLD.createVerifiablePresentationLD} */
|
|
66
|
+
public async createVerifiablePresentationLD(
|
|
67
|
+
args: ICreateVerifiablePresentationLDArgs,
|
|
68
|
+
context: IRequiredContext,
|
|
69
|
+
): Promise<VerifiablePresentation> {
|
|
70
|
+
const presentationContext = processEntryToArray(
|
|
71
|
+
args?.presentation?.['@context'],
|
|
72
|
+
MANDATORY_CREDENTIAL_CONTEXT,
|
|
73
|
+
)
|
|
74
|
+
const presentationType = processEntryToArray(args?.presentation?.type, 'VerifiablePresentation')
|
|
75
|
+
|
|
76
|
+
const presentation: PresentationPayload = {
|
|
77
|
+
...args?.presentation,
|
|
78
|
+
'@context': presentationContext,
|
|
79
|
+
type: presentationType,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!isDefined(presentation.holder)) {
|
|
83
|
+
throw new Error('invalid_argument: args.presentation.holder must not be empty')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (args.presentation.verifiableCredential) {
|
|
87
|
+
const credentials = args.presentation.verifiableCredential.map((cred) => {
|
|
88
|
+
if (typeof cred !== 'string' && cred.proof.jwt) {
|
|
89
|
+
return cred.proof.jwt
|
|
90
|
+
} else {
|
|
91
|
+
return cred
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
presentation.verifiableCredential = credentials
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
//issuanceDate must not be present for presentations because it is not defined in a @context
|
|
98
|
+
delete presentation.issuanceDate
|
|
99
|
+
|
|
100
|
+
let identifier: IIdentifier
|
|
101
|
+
try {
|
|
102
|
+
identifier = await context.agent.didManagerGet({ did: presentation.holder })
|
|
103
|
+
} catch (e) {
|
|
104
|
+
throw new Error('invalid_argument: args.presentation.holder must be a DID managed by this agent')
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const { signingKey, verificationMethodId } = await this.findSigningKeyWithId(
|
|
108
|
+
context,
|
|
109
|
+
identifier,
|
|
110
|
+
args.keyRef,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
return await this.ldCredentialModule.signLDVerifiablePresentation(
|
|
114
|
+
presentation,
|
|
115
|
+
identifier.did,
|
|
116
|
+
signingKey,
|
|
117
|
+
verificationMethodId,
|
|
118
|
+
args.challenge,
|
|
119
|
+
args.domain,
|
|
120
|
+
context,
|
|
121
|
+
)
|
|
122
|
+
} catch (error) {
|
|
123
|
+
debug(error)
|
|
124
|
+
return Promise.reject(error)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** {@inheritdoc ICredentialIssuerLD.createVerifiableCredentialLD} */
|
|
129
|
+
public async createVerifiableCredentialLD(
|
|
130
|
+
args: ICreateVerifiableCredentialLDArgs,
|
|
131
|
+
context: IRequiredContext,
|
|
132
|
+
): Promise<VerifiableCredential> {
|
|
133
|
+
const credentialContext = processEntryToArray(
|
|
134
|
+
args?.credential?.['@context'],
|
|
135
|
+
MANDATORY_CREDENTIAL_CONTEXT,
|
|
136
|
+
)
|
|
137
|
+
const credentialType = processEntryToArray(args?.credential?.type, 'VerifiableCredential')
|
|
138
|
+
let issuanceDate = args?.credential?.issuanceDate || new Date().toISOString()
|
|
139
|
+
if (issuanceDate instanceof Date) {
|
|
140
|
+
issuanceDate = issuanceDate.toISOString()
|
|
141
|
+
}
|
|
142
|
+
const credential: CredentialPayload = {
|
|
143
|
+
...args?.credential,
|
|
144
|
+
'@context': credentialContext,
|
|
145
|
+
type: credentialType,
|
|
146
|
+
issuanceDate,
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const issuer = extractIssuer(credential)
|
|
150
|
+
if (!issuer || typeof issuer === 'undefined') {
|
|
151
|
+
throw new Error('invalid_argument: args.credential.issuer must not be empty')
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let identifier: IIdentifier
|
|
155
|
+
try {
|
|
156
|
+
identifier = await context.agent.didManagerGet({ did: issuer })
|
|
157
|
+
} catch (e) {
|
|
158
|
+
throw new Error(`invalid_argument: args.credential.issuer must be a DID managed by this agent. ${e}`)
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
const { signingKey, verificationMethodId } = await this.findSigningKeyWithId(
|
|
162
|
+
context,
|
|
163
|
+
identifier,
|
|
164
|
+
args.keyRef,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
return await this.ldCredentialModule.issueLDVerifiableCredential(
|
|
168
|
+
credential,
|
|
169
|
+
identifier.did,
|
|
170
|
+
signingKey,
|
|
171
|
+
verificationMethodId,
|
|
172
|
+
context,
|
|
173
|
+
)
|
|
174
|
+
} catch (error) {
|
|
175
|
+
debug(error)
|
|
176
|
+
return Promise.reject(error)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** {@inheritdoc ICredentialIssuerLD.verifyCredentialLD} */
|
|
181
|
+
public async verifyCredentialLD(
|
|
182
|
+
args: IVerifyCredentialLDArgs,
|
|
183
|
+
context: IRequiredContext,
|
|
184
|
+
): Promise<boolean> {
|
|
185
|
+
const credential = args.credential
|
|
186
|
+
return this.ldCredentialModule.verifyCredential(credential, context)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** {@inheritdoc ICredentialIssuerLD.verifyPresentationLD} */
|
|
190
|
+
public async verifyPresentationLD(
|
|
191
|
+
args: IVerifyPresentationLDArgs,
|
|
192
|
+
context: IRequiredContext,
|
|
193
|
+
): Promise<boolean> {
|
|
194
|
+
const presentation = args.presentation
|
|
195
|
+
return this.ldCredentialModule.verifyPresentation(presentation, args.challenge, args.domain, context)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private async findSigningKeyWithId(
|
|
199
|
+
context: IAgentContext<IResolver>,
|
|
200
|
+
identifier: IIdentifier,
|
|
201
|
+
keyRef?: string,
|
|
202
|
+
): Promise<{ signingKey: IKey; verificationMethodId: string }> {
|
|
203
|
+
const extendedKeys: _ExtendedIKey[] = await mapIdentifierKeysToDoc(identifier, 'assertionMethod', context)
|
|
204
|
+
let supportedTypes = this.ldCredentialModule.ldSuiteLoader.getAllSignatureSuiteTypes()
|
|
205
|
+
let signingKey: _ExtendedIKey | undefined
|
|
206
|
+
let verificationMethodId: string
|
|
207
|
+
if (keyRef) {
|
|
208
|
+
signingKey = extendedKeys.find((k) => k.kid === keyRef)
|
|
209
|
+
}
|
|
210
|
+
if (signingKey && !supportedTypes.includes(signingKey.meta.verificationMethod.type)) {
|
|
211
|
+
debug(
|
|
212
|
+
'WARNING: requested signing key DOES NOT correspond to a supported Signature suite type. Looking for the next best key.',
|
|
213
|
+
)
|
|
214
|
+
signingKey = undefined
|
|
215
|
+
}
|
|
216
|
+
if (!signingKey) {
|
|
217
|
+
if (keyRef) {
|
|
218
|
+
debug(
|
|
219
|
+
'WARNING: no signing key was found that matches the reference provided. Searching for the first available signing key.',
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
signingKey = extendedKeys.find((k) => supportedTypes.includes(k.meta.verificationMethod.type))
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (!signingKey) throw Error(`key_not_found: No suitable signing key found for ${identifier.did}`)
|
|
226
|
+
verificationMethodId = signingKey.meta.verificationMethod.id
|
|
227
|
+
return { signingKey, verificationMethodId }
|
|
228
|
+
}
|
|
229
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provides a {@link @veramo/credential-ld#CredentialIssuerLD | plugin} for the {@link @veramo/core#Agent} that implements
|
|
3
|
+
* {@link @veramo/credential-ld#ICredentialIssuerLD} interface.
|
|
4
|
+
*
|
|
5
|
+
* @packageDocumentation
|
|
6
|
+
*/
|
|
7
|
+
export { CredentialIssuerLD } from './action-handler'
|
|
8
|
+
export * from './types'
|
|
9
|
+
export { LdDefaultContexts } from './ld-default-contexts'
|
|
10
|
+
export { VeramoLdSignature } from './ld-suites'
|
|
11
|
+
export * from './suites/EcdsaSecp256k1RecoverySignature2020'
|
|
12
|
+
export * from './suites/Ed25519Signature2018'
|
|
13
|
+
const schema = require('../plugin.schema.json')
|
|
14
|
+
export { schema }
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The LdContextLoader is initialized with a List of Map<string, ContextDoc>
|
|
3
|
+
* that it unifies into a single Map to provide to the documentLoader within
|
|
4
|
+
* the w3c credential module.
|
|
5
|
+
*/
|
|
6
|
+
import { OrPromise, RecordLike } from '@veramo/utils'
|
|
7
|
+
import { ContextDoc } from './types'
|
|
8
|
+
|
|
9
|
+
export class LdContextLoader {
|
|
10
|
+
private contexts: Record<string, OrPromise<ContextDoc>>
|
|
11
|
+
|
|
12
|
+
constructor(options: { contextsPaths: RecordLike<OrPromise<ContextDoc>>[] }) {
|
|
13
|
+
this.contexts = {}
|
|
14
|
+
// generate-plugin-schema is failing unless we use the cast to `any[]`
|
|
15
|
+
Array.from(options.contextsPaths as any[], (mapItem) => {
|
|
16
|
+
for (const [key, value] of mapItem) {
|
|
17
|
+
this.contexts[key] = value
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
has(url: string): boolean {
|
|
23
|
+
return this.contexts[url] !== null && typeof this.contexts[url] !== 'undefined'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async get(url: string): Promise<ContextDoc> {
|
|
27
|
+
return this.contexts[url]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CredentialPayload,
|
|
3
|
+
IAgentContext,
|
|
4
|
+
IKey,
|
|
5
|
+
IResolver,
|
|
6
|
+
PresentationPayload,
|
|
7
|
+
VerifiableCredential,
|
|
8
|
+
VerifiablePresentation,
|
|
9
|
+
} from '@veramo/core'
|
|
10
|
+
import Debug from 'debug'
|
|
11
|
+
import { extendContextLoader, purposes } from 'jsonld-signatures'
|
|
12
|
+
import * as vc from 'vc-js'
|
|
13
|
+
import { LdContextLoader } from './ld-context-loader'
|
|
14
|
+
import { LdSuiteLoader } from './ld-suite-loader'
|
|
15
|
+
import { RequiredAgentMethods } from './ld-suites'
|
|
16
|
+
|
|
17
|
+
const AssertionProofPurpose = purposes.AssertionProofPurpose
|
|
18
|
+
|
|
19
|
+
const debug = Debug('veramo:w3c:ld-credential-module')
|
|
20
|
+
|
|
21
|
+
export class LdCredentialModule {
|
|
22
|
+
/**
|
|
23
|
+
* TODO: General Implementation Notes
|
|
24
|
+
* - (SOLVED) EcdsaSecp256k1Signature2019 (Signature) and EcdsaSecp256k1VerificationKey2019 (Key)
|
|
25
|
+
* are not useable right now, since they are not able to work with blockChainId and ECRecover.
|
|
26
|
+
* - DID Fragment Resolution.
|
|
27
|
+
* - Key Manager and Verification Methods: Veramo currently implements no link between those.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
private ldContextLoader: LdContextLoader
|
|
31
|
+
ldSuiteLoader: LdSuiteLoader
|
|
32
|
+
constructor(options: { ldContextLoader: LdContextLoader; ldSuiteLoader: LdSuiteLoader }) {
|
|
33
|
+
this.ldContextLoader = options.ldContextLoader
|
|
34
|
+
this.ldSuiteLoader = options.ldSuiteLoader
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getDocumentLoader(context: IAgentContext<IResolver>) {
|
|
38
|
+
return extendContextLoader(async (url: string) => {
|
|
39
|
+
// console.log(`resolving context for: ${url}`)
|
|
40
|
+
|
|
41
|
+
// did resolution
|
|
42
|
+
if (url.toLowerCase().startsWith('did:')) {
|
|
43
|
+
const resolutionResult = await context.agent.resolveDid({ didUrl: url })
|
|
44
|
+
const didDoc = resolutionResult.didDocument
|
|
45
|
+
|
|
46
|
+
if (!didDoc) return
|
|
47
|
+
|
|
48
|
+
// currently Veramo LD suites can modify the resolution response for DIDs from
|
|
49
|
+
// the document Loader. This allows to fix incompatibilities between DID Documents
|
|
50
|
+
// and LD suites to be fixed specifically within the Veramo LD Suites definition
|
|
51
|
+
this.ldSuiteLoader.getAllSignatureSuites().forEach((x) => x.preDidResolutionModification(url, didDoc))
|
|
52
|
+
|
|
53
|
+
// console.log(`Returning from Documentloader: ${JSON.stringify(returnDocument)}`)
|
|
54
|
+
return {
|
|
55
|
+
contextUrl: null,
|
|
56
|
+
documentUrl: url,
|
|
57
|
+
document: didDoc,
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (this.ldContextLoader.has(url)) {
|
|
62
|
+
const contextDoc = await this.ldContextLoader.get(url)
|
|
63
|
+
return {
|
|
64
|
+
contextUrl: null,
|
|
65
|
+
documentUrl: url,
|
|
66
|
+
document: contextDoc,
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
debug('WARNING: Possible unknown context/identifier for', url)
|
|
71
|
+
console.log(`WARNING: Possible unknown context/identifier for: ${url}`)
|
|
72
|
+
|
|
73
|
+
return vc.defaultDocumentLoader(url)
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async issueLDVerifiableCredential(
|
|
78
|
+
credential: CredentialPayload,
|
|
79
|
+
issuerDid: string,
|
|
80
|
+
key: IKey,
|
|
81
|
+
verificationMethodId: string,
|
|
82
|
+
context: IAgentContext<RequiredAgentMethods>,
|
|
83
|
+
): Promise<VerifiableCredential> {
|
|
84
|
+
const suite = this.ldSuiteLoader.getSignatureSuiteForKeyType(key.type)
|
|
85
|
+
const documentLoader = this.getDocumentLoader(context)
|
|
86
|
+
|
|
87
|
+
// some suites can modify the incoming credential (e.g. add required contexts)W
|
|
88
|
+
suite.preSigningCredModification(credential)
|
|
89
|
+
|
|
90
|
+
return await vc.issue({
|
|
91
|
+
credential,
|
|
92
|
+
suite: suite.getSuiteForSigning(key, issuerDid, verificationMethodId, context),
|
|
93
|
+
documentLoader,
|
|
94
|
+
compactProof: false,
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async signLDVerifiablePresentation(
|
|
99
|
+
presentation: PresentationPayload,
|
|
100
|
+
holderDid: string,
|
|
101
|
+
key: IKey,
|
|
102
|
+
verificationMethodId: string,
|
|
103
|
+
challenge: string | undefined,
|
|
104
|
+
domain: string | undefined,
|
|
105
|
+
context: IAgentContext<RequiredAgentMethods>,
|
|
106
|
+
): Promise<VerifiablePresentation> {
|
|
107
|
+
const suite = this.ldSuiteLoader.getSignatureSuiteForKeyType(key.type)
|
|
108
|
+
const documentLoader = this.getDocumentLoader(context)
|
|
109
|
+
|
|
110
|
+
suite.preSigningPresModification(presentation)
|
|
111
|
+
|
|
112
|
+
return await vc.signPresentation({
|
|
113
|
+
presentation,
|
|
114
|
+
suite: suite.getSuiteForSigning(key, holderDid, verificationMethodId, context),
|
|
115
|
+
challenge,
|
|
116
|
+
domain,
|
|
117
|
+
documentLoader,
|
|
118
|
+
purpose: new AssertionProofPurpose(),
|
|
119
|
+
compactProof: false,
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async verifyCredential(
|
|
124
|
+
credential: VerifiableCredential,
|
|
125
|
+
context: IAgentContext<IResolver>,
|
|
126
|
+
): Promise<boolean> {
|
|
127
|
+
const result = await vc.verifyCredential({
|
|
128
|
+
credential,
|
|
129
|
+
suite: this.ldSuiteLoader.getAllSignatureSuites().map((x) => x.getSuiteForVerification()),
|
|
130
|
+
documentLoader: this.getDocumentLoader(context),
|
|
131
|
+
purpose: new AssertionProofPurpose(),
|
|
132
|
+
compactProof: false,
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
if (result.verified) return true
|
|
136
|
+
|
|
137
|
+
// NOT verified.
|
|
138
|
+
|
|
139
|
+
// result can include raw Error
|
|
140
|
+
debug(`Error verifying LD Verifiable Credential: ${JSON.stringify(result, null, 2)}`)
|
|
141
|
+
// console.log(JSON.stringify(result, null, 2));
|
|
142
|
+
throw Error('Error verifying LD Verifiable Credential')
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async verifyPresentation(
|
|
146
|
+
presentation: VerifiablePresentation,
|
|
147
|
+
challenge: string | undefined,
|
|
148
|
+
domain: string | undefined,
|
|
149
|
+
context: IAgentContext<IResolver>,
|
|
150
|
+
): Promise<boolean> {
|
|
151
|
+
const result = await vc.verify({
|
|
152
|
+
presentation,
|
|
153
|
+
suite: this.ldSuiteLoader.getAllSignatureSuites().map((x) => x.getSuiteForVerification()),
|
|
154
|
+
documentLoader: this.getDocumentLoader(context),
|
|
155
|
+
challenge,
|
|
156
|
+
domain,
|
|
157
|
+
purpose: new AssertionProofPurpose(),
|
|
158
|
+
compactProof: false,
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
if (result.verified) return true
|
|
162
|
+
|
|
163
|
+
// NOT verified.
|
|
164
|
+
|
|
165
|
+
// result can include raw Error
|
|
166
|
+
console.log(`Error verifying LD Verifiable Presentation`)
|
|
167
|
+
console.log(JSON.stringify(result, null, 2))
|
|
168
|
+
throw Error('Error verifying LD Verifiable Presentation')
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as fs from 'fs'
|
|
2
|
+
import * as path from 'path'
|
|
3
|
+
|
|
4
|
+
function _read(_path: string) {
|
|
5
|
+
return JSON.parse(fs.readFileSync(path.join(__dirname, '../contexts', _path), { encoding: 'utf8' }))
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const LdDefaultContexts = new Map([
|
|
9
|
+
['https://veramo.io/contexts/socialmedia/v1', _read('socialmedia-v1.jsonld')],
|
|
10
|
+
['https://veramo.io/contexts/kyc/v1', _read('kyc-v1.jsonld')],
|
|
11
|
+
['https://veramo.io/contexts/profile/v1', _read('profile-v1.jsonld')],
|
|
12
|
+
['https://www.w3.org/ns/did/v1', _read('security_context_v1.jsonld')],
|
|
13
|
+
['https://w3id.org/did/v0.11', _read('did_v0.11.jsonld')],
|
|
14
|
+
['https://ns.did.ai/transmute/v1', _read('transmute_v1.jsonld')],
|
|
15
|
+
[
|
|
16
|
+
'https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld',
|
|
17
|
+
_read('lds-ecdsa-secp256k1-recovery2020-0.0.jsonld'),
|
|
18
|
+
],
|
|
19
|
+
['https://w3id.org/security/suites/ed25519-2018/v1', _read('ed25519-signature-2018-v1.jsonld')],
|
|
20
|
+
['https://w3id.org/security/suites/x25519-2019/v1', _read('X25519KeyAgreementKey2019.jsonld')],
|
|
21
|
+
])
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { VeramoLdSignature } from './ld-suites'
|
|
2
|
+
import { TKeyType } from '@veramo/core'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Initializes a list of Veramo-wrapped LD Signature suites and exposes those to the Agent Module
|
|
6
|
+
*/
|
|
7
|
+
export class LdSuiteLoader {
|
|
8
|
+
constructor(options: { veramoLdSignatures: VeramoLdSignature[] }) {
|
|
9
|
+
options.veramoLdSignatures.forEach((obj) => {
|
|
10
|
+
this.signatureMap[obj.getSupportedVeramoKeyType()] = obj
|
|
11
|
+
})
|
|
12
|
+
}
|
|
13
|
+
private signatureMap: Record<string, VeramoLdSignature> = {}
|
|
14
|
+
|
|
15
|
+
getSignatureSuiteForKeyType(type: TKeyType) {
|
|
16
|
+
const suite = this.signatureMap[type]
|
|
17
|
+
if (suite) return suite
|
|
18
|
+
|
|
19
|
+
throw new Error('No Veramo LD Signature Suite for ' + type)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
getAllSignatureSuites() {
|
|
23
|
+
return Object.values(this.signatureMap)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
getAllSignatureSuiteTypes() {
|
|
27
|
+
return Object.values(this.signatureMap).map((x) => x.getSupportedVerificationType())
|
|
28
|
+
}
|
|
29
|
+
}
|
package/src/ld-suites.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CredentialPayload,
|
|
3
|
+
IAgentContext,
|
|
4
|
+
IKey,
|
|
5
|
+
IKeyManager,
|
|
6
|
+
IResolver,
|
|
7
|
+
PresentationPayload,
|
|
8
|
+
TKeyType,
|
|
9
|
+
} from '@veramo/core'
|
|
10
|
+
import { DIDDocument } from 'did-resolver/src/resolver'
|
|
11
|
+
|
|
12
|
+
export type RequiredAgentMethods = IResolver & Pick<IKeyManager, 'keyManagerGet' | 'keyManagerSign'>
|
|
13
|
+
|
|
14
|
+
export abstract class VeramoLdSignature {
|
|
15
|
+
// LinkedDataSignature Suites according to
|
|
16
|
+
// https://github.com/digitalbazaar/jsonld-signatures/blob/main/lib/suites/LinkedDataSignature.js
|
|
17
|
+
// Add type definition as soon as https://github.com/digitalbazaar/jsonld-signatures
|
|
18
|
+
// supports those.
|
|
19
|
+
|
|
20
|
+
abstract getSupportedVerificationType(): string
|
|
21
|
+
|
|
22
|
+
abstract getSupportedVeramoKeyType(): TKeyType
|
|
23
|
+
|
|
24
|
+
abstract getSuiteForSigning(
|
|
25
|
+
key: IKey,
|
|
26
|
+
issuerDid: string,
|
|
27
|
+
verificationMethodId: string,
|
|
28
|
+
context: IAgentContext<RequiredAgentMethods>,
|
|
29
|
+
): any
|
|
30
|
+
|
|
31
|
+
abstract getSuiteForVerification(): any
|
|
32
|
+
|
|
33
|
+
abstract preDidResolutionModification(didUrl: string, didDoc: DIDDocument): void
|
|
34
|
+
|
|
35
|
+
abstract preSigningCredModification(credential: CredentialPayload): void
|
|
36
|
+
|
|
37
|
+
preSigningPresModification(presentation: PresentationPayload): void {
|
|
38
|
+
// TODO: Remove invalid field 'verifiers' from Presentation. Needs to be adapted for LD credentials
|
|
39
|
+
// Only remove empty array (vc.signPresentation will throw then)
|
|
40
|
+
const sanitizedPresentation = presentation as any
|
|
41
|
+
if (sanitizedPresentation.verifier.length == 0) {
|
|
42
|
+
delete sanitizedPresentation.verifier
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { RequiredAgentMethods, VeramoLdSignature } from '../ld-suites'
|
|
2
|
+
import { CredentialPayload, DIDDocument, IAgentContext, IKey, TKeyType } from '@veramo/core'
|
|
3
|
+
import {
|
|
4
|
+
EcdsaSecp256k1RecoveryMethod2020,
|
|
5
|
+
EcdsaSecp256k1RecoverySignature2020,
|
|
6
|
+
} from '@transmute/lds-ecdsa-secp256k1-recovery2020'
|
|
7
|
+
|
|
8
|
+
import * as u8a from 'uint8arrays'
|
|
9
|
+
import { asArray, encodeJoseBlob } from '@veramo/utils'
|
|
10
|
+
|
|
11
|
+
export class VeramoEcdsaSecp256k1RecoverySignature2020 extends VeramoLdSignature {
|
|
12
|
+
getSupportedVerificationType(): string {
|
|
13
|
+
return 'EcdsaSecp256k1RecoveryMethod2020'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
getSupportedVeramoKeyType(): TKeyType {
|
|
17
|
+
return 'Secp256k1'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getSuiteForSigning(
|
|
21
|
+
key: IKey,
|
|
22
|
+
did: string,
|
|
23
|
+
verifiableMethodId: string,
|
|
24
|
+
context: IAgentContext<RequiredAgentMethods>,
|
|
25
|
+
): any {
|
|
26
|
+
const controller = did
|
|
27
|
+
const signer = {
|
|
28
|
+
//returns a JWS detached
|
|
29
|
+
sign: async (args: { data: Uint8Array }): Promise<string> => {
|
|
30
|
+
const header = {
|
|
31
|
+
alg: 'ES256K-R',
|
|
32
|
+
b64: false,
|
|
33
|
+
crit: ['b64'],
|
|
34
|
+
}
|
|
35
|
+
const headerString = encodeJoseBlob(header)
|
|
36
|
+
const messageBuffer = u8a.concat([u8a.fromString(`${headerString}.`, 'utf-8'), args.data])
|
|
37
|
+
const messageString = u8a.toString(messageBuffer, 'base64')
|
|
38
|
+
const signature = await context.agent.keyManagerSign({
|
|
39
|
+
keyRef: key.kid,
|
|
40
|
+
algorithm: 'ES256K-R',
|
|
41
|
+
data: messageString,
|
|
42
|
+
encoding: 'base64',
|
|
43
|
+
})
|
|
44
|
+
return `${headerString}..${signature}`
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return new EcdsaSecp256k1RecoverySignature2020({
|
|
49
|
+
// signer,
|
|
50
|
+
key: new EcdsaSecp256k1RecoveryMethod2020({
|
|
51
|
+
publicKeyHex: key.publicKeyHex,
|
|
52
|
+
signer: () => signer,
|
|
53
|
+
type: this.getSupportedVerificationType(),
|
|
54
|
+
controller,
|
|
55
|
+
id: verifiableMethodId,
|
|
56
|
+
}),
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
getSuiteForVerification(): any {
|
|
61
|
+
return new EcdsaSecp256k1RecoverySignature2020()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
preSigningCredModification(credential: CredentialPayload): void {
|
|
65
|
+
credential['@context'] = [
|
|
66
|
+
...asArray(credential['@context'] || []),
|
|
67
|
+
'https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld',
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
preDidResolutionModification(didUrl: string, didDoc: DIDDocument): void {
|
|
72
|
+
// did:ethr
|
|
73
|
+
if (didUrl.toLowerCase().startsWith('did:ethr')) {
|
|
74
|
+
// TODO: EcdsaSecp256k1RecoveryMethod2020 does not support blockchainAccountId
|
|
75
|
+
// blockchainAccountId to ethereumAddress
|
|
76
|
+
didDoc.verificationMethod?.forEach((x) => {
|
|
77
|
+
if (x.blockchainAccountId) {
|
|
78
|
+
x.ethereumAddress = x.blockchainAccountId.substring(0, x.blockchainAccountId.lastIndexOf('@'))
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|