@twin.org/immutable-proof-service 0.0.1-next.1
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 +21 -0
- package/dist/cjs/index.cjs +722 -0
- package/dist/esm/index.mjs +713 -0
- package/dist/types/entities/immutableProof.d.ts +33 -0
- package/dist/types/immutableProofRoutes.d.ts +37 -0
- package/dist/types/immutableProofService.d.ts +66 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/models/IImmutableProofServiceConfig.d.ts +15 -0
- package/dist/types/restEntryPoints.d.ts +2 -0
- package/dist/types/schema.d.ts +4 -0
- package/docs/changelog.md +5 -0
- package/docs/examples.md +1 -0
- package/docs/open-api/spec.json +827 -0
- package/docs/reference/classes/ImmutableProof.md +69 -0
- package/docs/reference/classes/ImmutableProofService.md +197 -0
- package/docs/reference/functions/generateRestRoutesImmutableProof.md +21 -0
- package/docs/reference/functions/immutableProofCreate.md +25 -0
- package/docs/reference/functions/immutableProofGet.md +25 -0
- package/docs/reference/functions/immutableProofVerify.md +25 -0
- package/docs/reference/functions/initSchema.md +9 -0
- package/docs/reference/index.md +23 -0
- package/docs/reference/interfaces/IImmutableProofServiceConfig.md +31 -0
- package/docs/reference/variables/restEntryPoints.md +3 -0
- package/docs/reference/variables/tagsImmutableProof.md +5 -0
- package/locales/en.json +12 -0
- package/package.json +51 -0
|
@@ -0,0 +1,722 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var core = require('@twin.org/core');
|
|
4
|
+
var immutableProofModels = require('@twin.org/immutable-proof-models');
|
|
5
|
+
var standardsW3cDid = require('@twin.org/standards-w3c-did');
|
|
6
|
+
var web = require('@twin.org/web');
|
|
7
|
+
var crypto = require('@twin.org/crypto');
|
|
8
|
+
var dataJsonLd = require('@twin.org/data-json-ld');
|
|
9
|
+
var dataSchemaOrg = require('@twin.org/data-schema-org');
|
|
10
|
+
var entity = require('@twin.org/entity');
|
|
11
|
+
var entityStorageModels = require('@twin.org/entity-storage-models');
|
|
12
|
+
var identityModels = require('@twin.org/identity-models');
|
|
13
|
+
var immutableStorageModels = require('@twin.org/immutable-storage-models');
|
|
14
|
+
var vaultModels = require('@twin.org/vault-models');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The source used when communicating about these routes.
|
|
18
|
+
*/
|
|
19
|
+
const ROUTES_SOURCE = "immutableProofRoutes";
|
|
20
|
+
/**
|
|
21
|
+
* The tag to associate with the routes.
|
|
22
|
+
*/
|
|
23
|
+
const tagsImmutableProof = [
|
|
24
|
+
{
|
|
25
|
+
name: "Immutable Proof",
|
|
26
|
+
description: "Endpoints which are modelled to access an immutable proof contract."
|
|
27
|
+
}
|
|
28
|
+
];
|
|
29
|
+
/**
|
|
30
|
+
* The REST routes for immutable proof.
|
|
31
|
+
* @param baseRouteName Prefix to prepend to the paths.
|
|
32
|
+
* @param componentName The name of the component to use in the routes stored in the ComponentFactory.
|
|
33
|
+
* @returns The generated routes.
|
|
34
|
+
*/
|
|
35
|
+
function generateRestRoutesImmutableProof(baseRouteName, componentName) {
|
|
36
|
+
const createRoute = {
|
|
37
|
+
operationId: "immutableProofCreate",
|
|
38
|
+
summary: "Create a proof",
|
|
39
|
+
tag: tagsImmutableProof[0].name,
|
|
40
|
+
method: "POST",
|
|
41
|
+
path: `${baseRouteName}/`,
|
|
42
|
+
handler: async (httpRequestContext, request) => immutableProofCreate(httpRequestContext, componentName, request),
|
|
43
|
+
requestType: {
|
|
44
|
+
type: "IImmutableProofCreateRequest",
|
|
45
|
+
examples: [
|
|
46
|
+
{
|
|
47
|
+
id: "immutableProofCreateRequestExample",
|
|
48
|
+
request: {
|
|
49
|
+
body: {
|
|
50
|
+
proofObject: {
|
|
51
|
+
"@context": "http://schema.org",
|
|
52
|
+
type: "Person",
|
|
53
|
+
name: "John Smith"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
responseType: [
|
|
61
|
+
{
|
|
62
|
+
type: "ICreatedResponse",
|
|
63
|
+
examples: [
|
|
64
|
+
{
|
|
65
|
+
id: "immutableProofCreateResponseExample",
|
|
66
|
+
response: {
|
|
67
|
+
statusCode: web.HttpStatusCode.created,
|
|
68
|
+
headers: {
|
|
69
|
+
[web.HeaderTypes.Location]: "test:1234567890"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
type: "INotFoundResponse"
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
};
|
|
80
|
+
const getRoute = {
|
|
81
|
+
operationId: "immutableProofGet",
|
|
82
|
+
summary: "Get a proof",
|
|
83
|
+
tag: tagsImmutableProof[0].name,
|
|
84
|
+
method: "GET",
|
|
85
|
+
path: `${baseRouteName}/:id`,
|
|
86
|
+
handler: async (httpRequestContext, request) => immutableProofGet(httpRequestContext, componentName, request),
|
|
87
|
+
requestType: {
|
|
88
|
+
type: "IImmutableProofGetRequest",
|
|
89
|
+
examples: [
|
|
90
|
+
{
|
|
91
|
+
id: "immutableProofGetRequestExample",
|
|
92
|
+
request: {
|
|
93
|
+
headers: {
|
|
94
|
+
[web.HeaderTypes.Accept]: web.MimeTypes.Json
|
|
95
|
+
},
|
|
96
|
+
pathParams: {
|
|
97
|
+
id: "ais:1234567890"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
responseType: [
|
|
104
|
+
{
|
|
105
|
+
type: "IImmutableProofGetResponse",
|
|
106
|
+
examples: [
|
|
107
|
+
{
|
|
108
|
+
id: "immutableProofGetResponseExample",
|
|
109
|
+
response: {
|
|
110
|
+
body: {
|
|
111
|
+
"@context": immutableProofModels.ImmutableProofTypes.ContextRoot,
|
|
112
|
+
type: immutableProofModels.ImmutableProofTypes.ImmutableProof,
|
|
113
|
+
id: "ais:1234567890",
|
|
114
|
+
userIdentity: "user-1",
|
|
115
|
+
proofObjectId: "test:1234567890",
|
|
116
|
+
proofObjectHash: "EAOKyDN0mYQbBh91eMdVeroxQx1H4GfnRbmt6n/2L/Y=",
|
|
117
|
+
proof: {
|
|
118
|
+
"@context": standardsW3cDid.DidContexts.ContextVCDataIntegrity,
|
|
119
|
+
type: standardsW3cDid.DidTypes.DataIntegrityProof,
|
|
120
|
+
cryptosuite: standardsW3cDid.DidCryptoSuites.EdDSAJcs2022,
|
|
121
|
+
created: "2024-08-22T11:56:56.272Z",
|
|
122
|
+
proofPurpose: "assertionMethod",
|
|
123
|
+
proofValue: "7DdiPPYtxLjCD3wA1po2rv..."
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
type: "IImmutableProofGetResponse",
|
|
132
|
+
mimeType: web.MimeTypes.JsonLd,
|
|
133
|
+
examples: [
|
|
134
|
+
{
|
|
135
|
+
id: "immutableProofJsonLdGetResponseExample",
|
|
136
|
+
response: {
|
|
137
|
+
headers: {
|
|
138
|
+
[web.HeaderTypes.ContentType]: web.MimeTypes.JsonLd
|
|
139
|
+
},
|
|
140
|
+
body: {
|
|
141
|
+
"@context": immutableProofModels.ImmutableProofTypes.ContextRoot,
|
|
142
|
+
type: immutableProofModels.ImmutableProofTypes.ImmutableProof,
|
|
143
|
+
id: "ais:1234567890",
|
|
144
|
+
userIdentity: "user-1",
|
|
145
|
+
proofObjectId: "test:1234567890",
|
|
146
|
+
proofObjectHash: "EAOKyDN0mYQbBh91eMdVeroxQx1H4GfnRbmt6n/2L/Y=",
|
|
147
|
+
proof: {
|
|
148
|
+
"@context": standardsW3cDid.DidContexts.ContextVCDataIntegrity,
|
|
149
|
+
type: standardsW3cDid.DidTypes.DataIntegrityProof,
|
|
150
|
+
cryptosuite: standardsW3cDid.DidCryptoSuites.EdDSAJcs2022,
|
|
151
|
+
created: "2024-08-22T11:56:56.272Z",
|
|
152
|
+
proofPurpose: "assertionMethod",
|
|
153
|
+
proofValue: "7DdiPPYtxLjCD3wA1po2rv..."
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
]
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
type: "INotFoundResponse"
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
};
|
|
165
|
+
const verifyRoute = {
|
|
166
|
+
operationId: "immutableProofVerify",
|
|
167
|
+
summary: "Verify a proof",
|
|
168
|
+
tag: tagsImmutableProof[0].name,
|
|
169
|
+
method: "POST",
|
|
170
|
+
path: `${baseRouteName}/:id`,
|
|
171
|
+
handler: async (httpRequestContext, request) => immutableProofVerify(httpRequestContext, componentName, request),
|
|
172
|
+
requestType: {
|
|
173
|
+
type: "IImmutableProofVerifyRequest",
|
|
174
|
+
examples: [
|
|
175
|
+
{
|
|
176
|
+
id: "immutableProofVerifyRequestExample",
|
|
177
|
+
request: {
|
|
178
|
+
pathParams: {
|
|
179
|
+
id: "ais:1234567890"
|
|
180
|
+
},
|
|
181
|
+
body: {
|
|
182
|
+
proofObject: {
|
|
183
|
+
"@context": "http://schema.org",
|
|
184
|
+
type: "Person",
|
|
185
|
+
name: "John Smith"
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
]
|
|
191
|
+
},
|
|
192
|
+
responseType: [
|
|
193
|
+
{
|
|
194
|
+
type: "IImmutableProofVerifyResponse",
|
|
195
|
+
examples: [
|
|
196
|
+
{
|
|
197
|
+
id: "immutableProofVerifyResponseExample",
|
|
198
|
+
response: {
|
|
199
|
+
body: {
|
|
200
|
+
verified: true
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
]
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
type: "IImmutableProofVerifyResponse",
|
|
208
|
+
examples: [
|
|
209
|
+
{
|
|
210
|
+
id: "immutableProofVerifyResponseFailExample",
|
|
211
|
+
response: {
|
|
212
|
+
body: {
|
|
213
|
+
verified: false,
|
|
214
|
+
failure: immutableProofModels.ImmutableProofFailure.ProofTypeMismatch
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
]
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
type: "INotFoundResponse"
|
|
222
|
+
}
|
|
223
|
+
]
|
|
224
|
+
};
|
|
225
|
+
return [createRoute, getRoute, verifyRoute];
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Create a proof.
|
|
229
|
+
* @param httpRequestContext The request context for the API.
|
|
230
|
+
* @param componentName The name of the component to use in the routes.
|
|
231
|
+
* @param request The request.
|
|
232
|
+
* @returns The response object with additional http response properties.
|
|
233
|
+
*/
|
|
234
|
+
async function immutableProofCreate(httpRequestContext, componentName, request) {
|
|
235
|
+
core.Guards.object(ROUTES_SOURCE, "request", request);
|
|
236
|
+
core.Guards.object(ROUTES_SOURCE, "request.body.proofObject", request.body.proofObject);
|
|
237
|
+
const component = core.ComponentFactory.get(componentName);
|
|
238
|
+
const result = await component.create(request.body.proofObject);
|
|
239
|
+
return {
|
|
240
|
+
statusCode: web.HttpStatusCode.created,
|
|
241
|
+
headers: {
|
|
242
|
+
[web.HeaderTypes.Location]: result
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Get the proof.
|
|
248
|
+
* @param httpRequestContext The request context for the API.
|
|
249
|
+
* @param componentName The name of the component to use in the routes.
|
|
250
|
+
* @param request The request.
|
|
251
|
+
* @returns The response object with additional http response properties.
|
|
252
|
+
*/
|
|
253
|
+
async function immutableProofGet(httpRequestContext, componentName, request) {
|
|
254
|
+
core.Guards.object(ROUTES_SOURCE, "request", request);
|
|
255
|
+
core.Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
|
|
256
|
+
core.Guards.stringValue(ROUTES_SOURCE, "request.pathParams.id", request.pathParams.id);
|
|
257
|
+
const mimeType = request.headers?.[web.HeaderTypes.Accept] === web.MimeTypes.JsonLd ? "jsonld" : "json";
|
|
258
|
+
const component = core.ComponentFactory.get(componentName);
|
|
259
|
+
const result = await component.get(request.pathParams.id);
|
|
260
|
+
return {
|
|
261
|
+
headers: {
|
|
262
|
+
[web.HeaderTypes.ContentType]: mimeType === "json" ? web.MimeTypes.Json : web.MimeTypes.JsonLd
|
|
263
|
+
},
|
|
264
|
+
body: result
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Verify the proof.
|
|
269
|
+
* @param httpRequestContext The request context for the API.
|
|
270
|
+
* @param componentName The name of the component to use in the routes.
|
|
271
|
+
* @param request The request.
|
|
272
|
+
* @returns The response object with additional http response properties.
|
|
273
|
+
*/
|
|
274
|
+
async function immutableProofVerify(httpRequestContext, componentName, request) {
|
|
275
|
+
core.Guards.object(ROUTES_SOURCE, "request", request);
|
|
276
|
+
core.Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
|
|
277
|
+
core.Guards.stringValue(ROUTES_SOURCE, "request.pathParams.id", request.pathParams.id);
|
|
278
|
+
core.Guards.object(ROUTES_SOURCE, "request.body.proofObject", request.body.proofObject);
|
|
279
|
+
const component = core.ComponentFactory.get(componentName);
|
|
280
|
+
const result = await component.verify(request.pathParams.id, request.body.proofObject);
|
|
281
|
+
return {
|
|
282
|
+
body: result
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Copyright 2024 IOTA Stiftung.
|
|
287
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
288
|
+
/**
|
|
289
|
+
* Class for performing immutable proof operations.
|
|
290
|
+
*/
|
|
291
|
+
class ImmutableProofService {
|
|
292
|
+
/**
|
|
293
|
+
* The namespace for the service.
|
|
294
|
+
*/
|
|
295
|
+
static NAMESPACE = "immutable-proof";
|
|
296
|
+
/**
|
|
297
|
+
* Runtime name for the class.
|
|
298
|
+
*/
|
|
299
|
+
CLASS_NAME = "ImmutableProofService";
|
|
300
|
+
/**
|
|
301
|
+
* The configuration for the connector.
|
|
302
|
+
* @internal
|
|
303
|
+
*/
|
|
304
|
+
_config;
|
|
305
|
+
/**
|
|
306
|
+
* The vault connector.
|
|
307
|
+
* @internal
|
|
308
|
+
*/
|
|
309
|
+
_vaultConnector;
|
|
310
|
+
/**
|
|
311
|
+
* The identity connector.
|
|
312
|
+
* @internal
|
|
313
|
+
*/
|
|
314
|
+
_identityConnector;
|
|
315
|
+
/**
|
|
316
|
+
* The entity storage for proofs.
|
|
317
|
+
* @internal
|
|
318
|
+
*/
|
|
319
|
+
_proofStorage;
|
|
320
|
+
/**
|
|
321
|
+
* The immutable storage for the credentials.
|
|
322
|
+
* @internal
|
|
323
|
+
*/
|
|
324
|
+
_immutableStorage;
|
|
325
|
+
/**
|
|
326
|
+
* The assertion method id to use for the proofs.
|
|
327
|
+
* @internal
|
|
328
|
+
*/
|
|
329
|
+
_assertionMethodId;
|
|
330
|
+
/**
|
|
331
|
+
* The proof config key id to use for the proofs.
|
|
332
|
+
* @internal
|
|
333
|
+
*/
|
|
334
|
+
_proofConfigKeyId;
|
|
335
|
+
/**
|
|
336
|
+
* Are we currently processing proofs.
|
|
337
|
+
* @internal
|
|
338
|
+
*/
|
|
339
|
+
_processing;
|
|
340
|
+
/**
|
|
341
|
+
* Create a new instance of ImmutableProofService.
|
|
342
|
+
* @param options The dependencies for the immutable proof connector.
|
|
343
|
+
* @param options.config The configuration for the connector.
|
|
344
|
+
* @param options.vaultConnectorType The vault connector type, defaults to "vault".
|
|
345
|
+
* @param options.immutableProofEntityStorageType The entity storage for proofs, defaults to "immutable-proof".
|
|
346
|
+
* @param options.immutableStorageType The immutable storage, defaults to "immutable-proof".
|
|
347
|
+
* @param options.identityConnectorType The identity connector type, defaults to "identity".
|
|
348
|
+
*/
|
|
349
|
+
constructor(options) {
|
|
350
|
+
this._vaultConnector = vaultModels.VaultConnectorFactory.get(options?.vaultConnectorType ?? "vault");
|
|
351
|
+
this._proofStorage = entityStorageModels.EntityStorageConnectorFactory.get(options?.immutableProofEntityStorageType ?? core.StringHelper.kebabCase("ImmutableProof"));
|
|
352
|
+
this._immutableStorage = immutableStorageModels.ImmutableStorageConnectorFactory.get(options?.immutableStorageType ?? "immutable-proof");
|
|
353
|
+
this._identityConnector = identityModels.IdentityConnectorFactory.get(options?.identityConnectorType ?? "identity");
|
|
354
|
+
this._config = options?.config ?? {};
|
|
355
|
+
this._assertionMethodId = this._config.assertionMethodId ?? "immutable-proof";
|
|
356
|
+
this._proofConfigKeyId = this._config.proofConfigKeyId ?? "immutable-proof";
|
|
357
|
+
dataSchemaOrg.SchemaOrgDataTypes.registerRedirects();
|
|
358
|
+
this._processing = false;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Create a new authentication proof.
|
|
362
|
+
* @param proofObject The object for the proof as JSON-LD.
|
|
363
|
+
* @param userIdentity The identity to create the immutable proof operation with.
|
|
364
|
+
* @param nodeIdentity The node identity to use for vault operations.
|
|
365
|
+
* @returns The id of the new authentication proof.
|
|
366
|
+
*/
|
|
367
|
+
async create(proofObject, userIdentity, nodeIdentity) {
|
|
368
|
+
core.Guards.object(this.CLASS_NAME, "proofObject", proofObject);
|
|
369
|
+
core.Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
|
|
370
|
+
core.Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
|
|
371
|
+
try {
|
|
372
|
+
const validationFailures = [];
|
|
373
|
+
await dataJsonLd.JsonLdHelper.validate(proofObject, validationFailures);
|
|
374
|
+
core.Validation.asValidationError(this.CLASS_NAME, "proofObject", validationFailures);
|
|
375
|
+
const id = core.Converter.bytesToHex(core.RandomHelper.generate(32), false);
|
|
376
|
+
const dateCreated = new Date(Date.now()).toISOString();
|
|
377
|
+
const proofObjectId = core.ObjectHelper.extractProperty(proofObject, ["@id", "id"], false);
|
|
378
|
+
const hash = this.calculateHash(id, dateCreated, nodeIdentity, userIdentity, proofObject);
|
|
379
|
+
const proofEntity = {
|
|
380
|
+
id,
|
|
381
|
+
nodeIdentity,
|
|
382
|
+
userIdentity,
|
|
383
|
+
dateCreated,
|
|
384
|
+
proofObjectId,
|
|
385
|
+
proofObjectHash: core.Converter.bytesToBase64(hash)
|
|
386
|
+
};
|
|
387
|
+
await this._proofStorage.set(proofEntity);
|
|
388
|
+
this.startProcessingProofs();
|
|
389
|
+
return new core.Urn(ImmutableProofService.NAMESPACE, id).toString();
|
|
390
|
+
}
|
|
391
|
+
catch (error) {
|
|
392
|
+
throw new core.GeneralError(this.CLASS_NAME, "createFailed", undefined, error);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Get an authentication proof.
|
|
397
|
+
* @param id The id of the proof to get.
|
|
398
|
+
* @returns The proof.
|
|
399
|
+
* @throws NotFoundError if the proof is not found.
|
|
400
|
+
*/
|
|
401
|
+
async get(id) {
|
|
402
|
+
core.Guards.stringValue(this.CLASS_NAME, "id", id);
|
|
403
|
+
const urnParsed = core.Urn.fromValidString(id);
|
|
404
|
+
if (urnParsed.namespaceIdentifier() !== ImmutableProofService.NAMESPACE) {
|
|
405
|
+
throw new core.GeneralError(this.CLASS_NAME, "namespaceMismatch", {
|
|
406
|
+
namespace: ImmutableProofService.NAMESPACE,
|
|
407
|
+
id
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
try {
|
|
411
|
+
const { immutableProof } = await this.internalGet(id);
|
|
412
|
+
const compacted = await dataJsonLd.JsonLdProcessor.compact(immutableProof, immutableProof["@context"]);
|
|
413
|
+
return compacted;
|
|
414
|
+
}
|
|
415
|
+
catch (error) {
|
|
416
|
+
throw new core.GeneralError(this.CLASS_NAME, "getFailed", undefined, error);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Verify an authentication proof.
|
|
421
|
+
* @param id The id of the proof to verify.
|
|
422
|
+
* @param proofObject The object to verify as JSON-LD.
|
|
423
|
+
* @returns The result of the verification and any failures.
|
|
424
|
+
* @throws NotFoundError if the proof is not found.
|
|
425
|
+
*/
|
|
426
|
+
async verify(id, proofObject) {
|
|
427
|
+
core.Guards.stringValue(this.CLASS_NAME, "id", id);
|
|
428
|
+
const urnParsed = core.Urn.fromValidString(id);
|
|
429
|
+
if (urnParsed.namespaceIdentifier() !== ImmutableProofService.NAMESPACE) {
|
|
430
|
+
throw new core.GeneralError(this.CLASS_NAME, "namespaceMismatch", {
|
|
431
|
+
namespace: ImmutableProofService.NAMESPACE,
|
|
432
|
+
id
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
try {
|
|
436
|
+
const { verified, failure } = await this.internalGet(id, proofObject);
|
|
437
|
+
return {
|
|
438
|
+
verified,
|
|
439
|
+
failure
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
catch (error) {
|
|
443
|
+
throw new core.GeneralError(this.CLASS_NAME, "verifyFailed", undefined, error);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Remove the immutable storage for the proof.
|
|
448
|
+
* @param id The id of the proof to remove the storage from.
|
|
449
|
+
* @param nodeIdentity The node identity to use for vault operations.
|
|
450
|
+
* @returns Nothing.
|
|
451
|
+
* @throws NotFoundError if the proof is not found.
|
|
452
|
+
*/
|
|
453
|
+
async removeImmutable(id, nodeIdentity) {
|
|
454
|
+
core.Guards.stringValue(this.CLASS_NAME, "id", id);
|
|
455
|
+
core.Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
|
|
456
|
+
const urnParsed = core.Urn.fromValidString(id);
|
|
457
|
+
if (urnParsed.namespaceIdentifier() !== ImmutableProofService.NAMESPACE) {
|
|
458
|
+
throw new core.GeneralError(this.CLASS_NAME, "namespaceMismatch", {
|
|
459
|
+
namespace: ImmutableProofService.NAMESPACE,
|
|
460
|
+
id
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
try {
|
|
464
|
+
const streamId = urnParsed.namespaceSpecific(0);
|
|
465
|
+
const streamEntity = await this._proofStorage.get(streamId);
|
|
466
|
+
if (core.Is.empty(streamEntity)) {
|
|
467
|
+
throw new core.NotFoundError(this.CLASS_NAME, "proofNotFound", id);
|
|
468
|
+
}
|
|
469
|
+
if (core.Is.stringValue(streamEntity.immutableStorageId)) {
|
|
470
|
+
await this._immutableStorage.remove(nodeIdentity, streamEntity.immutableStorageId);
|
|
471
|
+
delete streamEntity.immutableStorageId;
|
|
472
|
+
await this._proofStorage.set(streamEntity);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
catch (error) {
|
|
476
|
+
throw new core.GeneralError(this.CLASS_NAME, "removeImmutableFailed", undefined, error);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Calculate the object hash.
|
|
481
|
+
* @param object The entry to calculate the hash for.
|
|
482
|
+
* @returns The hash.
|
|
483
|
+
* @internal
|
|
484
|
+
*/
|
|
485
|
+
calculateHash(id, dateCreated, nodeIdentity, userIdentity, proofObject) {
|
|
486
|
+
const b2b = new crypto.Blake2b(crypto.Blake2b.SIZE_256);
|
|
487
|
+
b2b.update(core.Converter.utf8ToBytes(id));
|
|
488
|
+
b2b.update(core.Converter.utf8ToBytes(dateCreated));
|
|
489
|
+
b2b.update(core.Converter.utf8ToBytes(nodeIdentity));
|
|
490
|
+
b2b.update(core.Converter.utf8ToBytes(userIdentity));
|
|
491
|
+
b2b.update(core.ObjectHelper.toBytes(proofObject));
|
|
492
|
+
return b2b.digest();
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Map the stream entity to a model.
|
|
496
|
+
* @param proofEntity The stream entity.
|
|
497
|
+
* @returns The model.
|
|
498
|
+
* @internal
|
|
499
|
+
*/
|
|
500
|
+
proofEntityToModel(proofEntity) {
|
|
501
|
+
const model = {
|
|
502
|
+
"@context": [
|
|
503
|
+
immutableProofModels.ImmutableProofTypes.ContextRoot,
|
|
504
|
+
dataSchemaOrg.SchemaOrgTypes.ContextRoot,
|
|
505
|
+
standardsW3cDid.DidContexts.ContextVCDataIntegrity
|
|
506
|
+
],
|
|
507
|
+
type: immutableProofModels.ImmutableProofTypes.ImmutableProof,
|
|
508
|
+
id: proofEntity.id,
|
|
509
|
+
userIdentity: proofEntity.userIdentity,
|
|
510
|
+
proofObjectId: proofEntity.proofObjectId,
|
|
511
|
+
proofObjectHash: proofEntity.proofObjectHash
|
|
512
|
+
};
|
|
513
|
+
return model;
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Start processing proofs.
|
|
517
|
+
* @returns Nothing.
|
|
518
|
+
* @internal
|
|
519
|
+
*/
|
|
520
|
+
startProcessingProofs() {
|
|
521
|
+
if (!this._processing) {
|
|
522
|
+
setTimeout(async () => {
|
|
523
|
+
await this.processProofs();
|
|
524
|
+
}, 0);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Process the proofs.
|
|
529
|
+
* @internal
|
|
530
|
+
*/
|
|
531
|
+
async processProofs() {
|
|
532
|
+
// Get the oldest pending proof, plus one more, we can then determine whether to
|
|
533
|
+
// trigger another process after this one
|
|
534
|
+
const pendingProofs = await this._proofStorage.query({
|
|
535
|
+
property: "immutableStorageId",
|
|
536
|
+
comparison: entity.ComparisonOperator.Equals,
|
|
537
|
+
value: undefined
|
|
538
|
+
}, [
|
|
539
|
+
{
|
|
540
|
+
property: "dateCreated",
|
|
541
|
+
sortDirection: entity.SortDirection.Ascending
|
|
542
|
+
}
|
|
543
|
+
], undefined, undefined, 2);
|
|
544
|
+
if (pendingProofs.entities.length > 0) {
|
|
545
|
+
const proofEntity = pendingProofs.entities[0];
|
|
546
|
+
const immutableProof = this.proofEntityToModel(proofEntity);
|
|
547
|
+
const hashData = await this.generateHashData(proofEntity.nodeIdentity, immutableProof);
|
|
548
|
+
immutableProof.proof = await this._identityConnector.createProof(proofEntity.nodeIdentity, `${proofEntity.nodeIdentity}#${this._assertionMethodId}`, hashData);
|
|
549
|
+
proofEntity.dateCreated = immutableProof.proof.created ?? new Date(Date.now()).toISOString();
|
|
550
|
+
const compacted = await dataJsonLd.JsonLdProcessor.compact(immutableProof, immutableProof["@context"]);
|
|
551
|
+
proofEntity.immutableStorageId = await this._immutableStorage.store(proofEntity.nodeIdentity, core.ObjectHelper.toBytes(compacted));
|
|
552
|
+
await this._proofStorage.set(proofEntity);
|
|
553
|
+
}
|
|
554
|
+
// If there are still remaining proofs, start the timer again
|
|
555
|
+
this._processing = false;
|
|
556
|
+
if (pendingProofs.entities.length > 1) {
|
|
557
|
+
this.startProcessingProofs();
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Verify an authentication proof.
|
|
562
|
+
* @param id The id of the proof to verify.
|
|
563
|
+
* @param proofObject The object to verify as JSON-LD.
|
|
564
|
+
* @returns The result of the verification and any failures.
|
|
565
|
+
* @throws NotFoundError if the proof is not found.
|
|
566
|
+
* @internal
|
|
567
|
+
*/
|
|
568
|
+
async internalGet(id, proofObject) {
|
|
569
|
+
const urnParsed = core.Urn.fromValidString(id);
|
|
570
|
+
const proofId = urnParsed.namespaceSpecific(0);
|
|
571
|
+
const proofEntity = await this._proofStorage.get(proofId);
|
|
572
|
+
if (core.Is.empty(proofEntity)) {
|
|
573
|
+
throw new core.NotFoundError(this.CLASS_NAME, "proofNotFound", id);
|
|
574
|
+
}
|
|
575
|
+
let proofModel = await this.proofEntityToModel(proofEntity);
|
|
576
|
+
let verified = false;
|
|
577
|
+
let failure = immutableProofModels.ImmutableProofFailure.NotIssued;
|
|
578
|
+
if (core.Is.stringValue(proofEntity.immutableStorageId)) {
|
|
579
|
+
failure = immutableProofModels.ImmutableProofFailure.ProofMissing;
|
|
580
|
+
const immutableData = await this._immutableStorage.get(proofEntity.immutableStorageId);
|
|
581
|
+
if (core.Is.uint8Array(immutableData)) {
|
|
582
|
+
proofModel = core.ObjectHelper.fromBytes(immutableData);
|
|
583
|
+
if (core.Is.object(proofModel.proof) && core.Is.object(proofObject)) {
|
|
584
|
+
if (proofModel.proof.cryptosuite !== standardsW3cDid.DidCryptoSuites.EdDSAJcs2022) {
|
|
585
|
+
failure = immutableProofModels.ImmutableProofFailure.CryptoSuiteMismatch;
|
|
586
|
+
}
|
|
587
|
+
else if (proofModel.proof.type !== standardsW3cDid.DidTypes.DataIntegrityProof) {
|
|
588
|
+
failure = immutableProofModels.ImmutableProofFailure.ProofTypeMismatch;
|
|
589
|
+
}
|
|
590
|
+
else {
|
|
591
|
+
const hashData = await this.generateHashData(proofEntity.nodeIdentity, proofModel);
|
|
592
|
+
const isVerified = await this._identityConnector.verifyProof(hashData, proofModel.proof);
|
|
593
|
+
if (isVerified) {
|
|
594
|
+
verified = true;
|
|
595
|
+
failure = undefined;
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
failure = immutableProofModels.ImmutableProofFailure.SignatureMismatch;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
return {
|
|
605
|
+
immutableProof: proofModel,
|
|
606
|
+
verified,
|
|
607
|
+
failure
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Generate the hash data for the proof.
|
|
612
|
+
* Conforms to https://www.w3.org/TR/vc-di-eddsa/#create-proof-eddsa-jcs-2022
|
|
613
|
+
* @param nodeIdentity The node identity to use for vault operations.
|
|
614
|
+
* @param immutableProof The immutable proof to generate the hash data for.
|
|
615
|
+
* @returns The hash data.
|
|
616
|
+
* @internal
|
|
617
|
+
*/
|
|
618
|
+
async generateHashData(nodeIdentity, immutableProof) {
|
|
619
|
+
const canonicalDocument = core.JsonHelper.canonicalize(core.ObjectHelper.omit(immutableProof, ["proof"]));
|
|
620
|
+
const proofConfigKey = await this._vaultConnector.getKey(`${nodeIdentity}/${this._proofConfigKeyId}`);
|
|
621
|
+
const proofConfigHash = crypto.Sha256.sum256(proofConfigKey.privateKey);
|
|
622
|
+
const transformedDocumentHash = crypto.Sha256.sum256(core.Converter.utf8ToBytes(canonicalDocument));
|
|
623
|
+
const hashData = new Uint8Array(proofConfigHash.length + transformedDocumentHash.length);
|
|
624
|
+
hashData.set(proofConfigHash);
|
|
625
|
+
hashData.set(transformedDocumentHash, proofConfigHash.length);
|
|
626
|
+
return hashData;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// Copyright 2024 IOTA Stiftung.
|
|
631
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
632
|
+
/**
|
|
633
|
+
* Class describing the immutable proof.
|
|
634
|
+
*/
|
|
635
|
+
exports.ImmutableProof = class ImmutableProof {
|
|
636
|
+
/**
|
|
637
|
+
* The id of the proof.
|
|
638
|
+
*/
|
|
639
|
+
id;
|
|
640
|
+
/**
|
|
641
|
+
* The identity of the node which controls the proof.
|
|
642
|
+
*/
|
|
643
|
+
nodeIdentity;
|
|
644
|
+
/**
|
|
645
|
+
* The identity of the user which created the proof.
|
|
646
|
+
*/
|
|
647
|
+
userIdentity;
|
|
648
|
+
/**
|
|
649
|
+
* The date/time of when the proof was created.
|
|
650
|
+
*/
|
|
651
|
+
dateCreated;
|
|
652
|
+
/**
|
|
653
|
+
* The associated id for the item.
|
|
654
|
+
*/
|
|
655
|
+
proofObjectId;
|
|
656
|
+
/**
|
|
657
|
+
* The associated hash for the item.
|
|
658
|
+
*/
|
|
659
|
+
proofObjectHash;
|
|
660
|
+
/**
|
|
661
|
+
* The immutable storage id.
|
|
662
|
+
*/
|
|
663
|
+
immutableStorageId;
|
|
664
|
+
};
|
|
665
|
+
__decorate([
|
|
666
|
+
entity.property({ type: "string", isPrimary: true }),
|
|
667
|
+
__metadata("design:type", String)
|
|
668
|
+
], exports.ImmutableProof.prototype, "id", void 0);
|
|
669
|
+
__decorate([
|
|
670
|
+
entity.property({ type: "string" }),
|
|
671
|
+
__metadata("design:type", String)
|
|
672
|
+
], exports.ImmutableProof.prototype, "nodeIdentity", void 0);
|
|
673
|
+
__decorate([
|
|
674
|
+
entity.property({ type: "string" }),
|
|
675
|
+
__metadata("design:type", String)
|
|
676
|
+
], exports.ImmutableProof.prototype, "userIdentity", void 0);
|
|
677
|
+
__decorate([
|
|
678
|
+
entity.property({ type: "string", format: "date-time", sortDirection: entity.SortDirection.Descending }),
|
|
679
|
+
__metadata("design:type", String)
|
|
680
|
+
], exports.ImmutableProof.prototype, "dateCreated", void 0);
|
|
681
|
+
__decorate([
|
|
682
|
+
entity.property({ type: "string" }),
|
|
683
|
+
__metadata("design:type", String)
|
|
684
|
+
], exports.ImmutableProof.prototype, "proofObjectId", void 0);
|
|
685
|
+
__decorate([
|
|
686
|
+
entity.property({ type: "string" }),
|
|
687
|
+
__metadata("design:type", String)
|
|
688
|
+
], exports.ImmutableProof.prototype, "proofObjectHash", void 0);
|
|
689
|
+
__decorate([
|
|
690
|
+
entity.property({ type: "string" }),
|
|
691
|
+
__metadata("design:type", String)
|
|
692
|
+
], exports.ImmutableProof.prototype, "immutableStorageId", void 0);
|
|
693
|
+
exports.ImmutableProof = __decorate([
|
|
694
|
+
entity.entity()
|
|
695
|
+
], exports.ImmutableProof);
|
|
696
|
+
|
|
697
|
+
const restEntryPoints = [
|
|
698
|
+
{
|
|
699
|
+
name: "immutable-proof",
|
|
700
|
+
defaultBaseRoute: "immutable-proof",
|
|
701
|
+
tags: tagsImmutableProof,
|
|
702
|
+
generateRoutes: generateRestRoutesImmutableProof
|
|
703
|
+
}
|
|
704
|
+
];
|
|
705
|
+
|
|
706
|
+
// Copyright 2024 IOTA Stiftung.
|
|
707
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
708
|
+
/**
|
|
709
|
+
* Initialize the schema for the immutable proof entity storage connector.
|
|
710
|
+
*/
|
|
711
|
+
function initSchema() {
|
|
712
|
+
entity.EntitySchemaFactory.register("ImmutableProof", () => entity.EntitySchemaHelper.getSchema(exports.ImmutableProof));
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
exports.ImmutableProofService = ImmutableProofService;
|
|
716
|
+
exports.generateRestRoutesImmutableProof = generateRestRoutesImmutableProof;
|
|
717
|
+
exports.immutableProofCreate = immutableProofCreate;
|
|
718
|
+
exports.immutableProofGet = immutableProofGet;
|
|
719
|
+
exports.immutableProofVerify = immutableProofVerify;
|
|
720
|
+
exports.initSchema = initSchema;
|
|
721
|
+
exports.restEntryPoints = restEntryPoints;
|
|
722
|
+
exports.tagsImmutableProof = tagsImmutableProof;
|