@verii/verii-issuing 1.0.0-pre.1754543687

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.
@@ -0,0 +1,48 @@
1
+ /*
2
+ * Copyright 2025 Velocity Team
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ */
17
+
18
+ /** @import { Issuer, Context } from "../../types/types" */
19
+
20
+ const { hexFromJwk } = require('@verii/jwt');
21
+ const { initRevocationRegistry } = require('@verii/metadata-registration');
22
+ const { initCallWithKmsKey } = require('@verii/crypto');
23
+
24
+ /**
25
+ * Get revocation registry
26
+ * @param {Issuer} issuer the issuer's dlt account
27
+ * @param {Context} context the context
28
+ * @returns {Promise<unknown>} the revocation registry contract
29
+ */
30
+ const getRevocationRegistry = (issuer, context) => {
31
+ const {
32
+ config: { revocationContractAddress },
33
+ rpcProvider,
34
+ } = context;
35
+
36
+ return initCallWithKmsKey(context)(issuer.dltOperatorKMSKeyId, (key) =>
37
+ initRevocationRegistry(
38
+ {
39
+ contractAddress: revocationContractAddress,
40
+ rpcProvider,
41
+ privateKey: hexFromJwk(key.privateJwk),
42
+ },
43
+ context
44
+ )
45
+ );
46
+ };
47
+
48
+ module.exports = { getRevocationRegistry };
@@ -0,0 +1,102 @@
1
+ /*
2
+ * Copyright 2024 Velocity Team
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ */
17
+
18
+ const {
19
+ initMetadataRegistry,
20
+ ALG_TYPE,
21
+ } = require('@verii/metadata-registration');
22
+ const { jsonLdToUnsignedVcJwtContent, hexFromJwk } = require('@verii/jwt');
23
+ const { initCallWithKmsKey } = require('@verii/crypto');
24
+ const { KeyAlgorithms } = require('@verii/crypto/src/constants');
25
+ const { buildIssuerVcUrl } = require('./build-issuer-vc-url');
26
+
27
+ /** @import { Issuer, CredentialMetadata, Context } from "../../types/types" */
28
+ /**
29
+ * Creates a createCredentialMetadataEntry function
30
+ * @param {Issuer} issuer the issuer
31
+ * @param {Context} context the context
32
+ * @returns {Promise<{
33
+ * addEntry: function(CredentialMetadata): Promise<void>,
34
+ * createList: function(number): Promise<boolean>
35
+ * }>} the contract interface to create metadata
36
+ */
37
+ const initCredentialMetadataContract = async (issuer, context) => {
38
+ const { config, rpcProvider, caoDid } = context;
39
+
40
+ const credentialMetadataRegistry = await initCallWithKmsKey(context)(
41
+ issuer.dltOperatorKMSKeyId,
42
+ ({ privateJwk: dltJwk }) =>
43
+ initMetadataRegistry(
44
+ {
45
+ privateKey: hexFromJwk(dltJwk),
46
+ contractAddress: config.metadataRegistryContractAddress,
47
+ rpcProvider,
48
+ },
49
+ context
50
+ )
51
+ );
52
+
53
+ return {
54
+ /**
55
+ * Anchor credential metadata to the dlt
56
+ * @param {CredentialMetadata} metadata the credential metadata
57
+ * @returns {Promise<boolean>} true if entry is set
58
+ */
59
+ addEntry: (metadata) =>
60
+ credentialMetadataRegistry.addCredentialMetadataEntry(
61
+ metadata,
62
+ metadata.contentHash,
63
+ caoDid,
64
+ ALG_TYPE.COSEKEY_AES_256
65
+ ),
66
+ /**
67
+ * List to create on the dlt
68
+ * @param {number} listId list id to create
69
+ * @returns {Promise<boolean>} true if a list was created, false if it already existed
70
+ */
71
+ createList: async (listId) => {
72
+ const accountId = issuer.dltPrimaryAddress;
73
+ const { payload, header } = jsonLdToUnsignedVcJwtContent(
74
+ {
75
+ id: buildIssuerVcUrl(listId, issuer, context),
76
+ type: ['CredentialMetadataListHeader'],
77
+ issuer: issuer.did,
78
+ issuanceDate: new Date().toISOString(),
79
+ credentialSubject: { listId, accountId },
80
+ },
81
+ KeyAlgorithms.SECP256K1,
82
+ issuer.issuingServiceDIDKeyId
83
+ );
84
+
85
+ const issuerVC = await context.kms.signJwt(
86
+ payload,
87
+ issuer.issuingServiceKMSKeyId,
88
+ header
89
+ );
90
+
91
+ return credentialMetadataRegistry.createCredentialMetadataList(
92
+ accountId,
93
+ listId,
94
+ issuerVC,
95
+ caoDid,
96
+ ALG_TYPE.COSEKEY_AES_256
97
+ );
98
+ },
99
+ };
100
+ };
101
+
102
+ module.exports = { initCredentialMetadataContract };
@@ -0,0 +1,123 @@
1
+ /*
2
+ * Copyright 2024 Velocity Team
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ */
17
+ /** @import { Issuer, AllocationListEntry, AllocationListQueries, Context } from "../../types/types" */
18
+
19
+ const { toEthereumAddress } = require('@verii/blockchain-functions');
20
+ const { hexFromJwk } = require('@verii/jwt');
21
+ /**
22
+ * Returns the queries needed for allocating to DLT Lists
23
+ * @param {unknown} db the db connection
24
+ * @param {string} collectionName the collection name
25
+ * @returns {AllocationListQueries} queries to allocate to lists on a db
26
+ */
27
+ const mongoAllocationListQueries = (db, collectionName) => {
28
+ /**
29
+ * Gets the next entry out of the list
30
+ * @param {string} entityName the name of the collection or table for the list
31
+ * @param {Issuer} issuer the issuer
32
+ * @param {Context} context the context
33
+ * @returns {AllocationListEntry} the next entry of the list
34
+ */
35
+ const allocateNextEntry = async (entityName, issuer, context) => {
36
+ const operatorAddress = await getOperatorAddress(issuer, context);
37
+ const result = await db.collection(collectionName).findOneAndUpdate(
38
+ {
39
+ tenantId: issuer.id,
40
+ operatorAddress,
41
+ entityName,
42
+ $and: [
43
+ { freeIndexes: { $exists: true } },
44
+ { freeIndexes: { $not: { $size: 0 } } },
45
+ ],
46
+ },
47
+ {
48
+ $pop: { freeIndexes: -1 },
49
+ $set: {
50
+ updatedAt: new Date(),
51
+ },
52
+ },
53
+ {
54
+ upsert: false,
55
+ returnNewDocument: false,
56
+ includeResultMetadata: true,
57
+ }
58
+ );
59
+ return {
60
+ listId: result.value.currentListId,
61
+ index: result.value.freeIndexes?.[0],
62
+ isNewList: false,
63
+ };
64
+ };
65
+
66
+ /**
67
+ * Create a new allocation list on ledger
68
+ * @param {string} entityName the name of the collection or table for the list
69
+ * @param {Issuer} issuer the issuer
70
+ * @param {number} newListId the new allocation list id
71
+ * @param {number[]} allocations the new allocations to use
72
+ * @param {Context} context the context
73
+ * @returns {AllocationListEntry} the first entry of the list
74
+ */
75
+ const createNewAllocationList = async (
76
+ entityName,
77
+ issuer,
78
+ newListId,
79
+ allocations,
80
+ context
81
+ ) => {
82
+ const operatorAddress = await getOperatorAddress(issuer, context);
83
+ await db.collection(collectionName).insertOne({
84
+ tenantId: issuer.id,
85
+ entityName,
86
+ freeIndexes: allocations.slice(1),
87
+ currentListId: newListId,
88
+ operatorAddress,
89
+ createdAt: new Date(),
90
+ updatedAt: new Date(),
91
+ });
92
+
93
+ return {
94
+ listId: newListId,
95
+ index: allocations[0],
96
+ isNewList: true,
97
+ };
98
+ };
99
+
100
+ return {
101
+ createNewAllocationList,
102
+ allocateNextEntry,
103
+ };
104
+ };
105
+
106
+ /**
107
+ * Gets the operator address
108
+ * @param {Issuer} issuer the issuer
109
+ * @param {Context} context the context
110
+ * @returns {string} the operator address
111
+ */
112
+ const getOperatorAddress = async (issuer, { kms }) => {
113
+ if (issuer.dltOperatorAddress != null) {
114
+ return issuer.dltOperatorAddress;
115
+ }
116
+
117
+ const key = await kms.exportKeyOrSecret(issuer.dltOperatorKMSKeyId);
118
+ return toEthereumAddress(hexFromJwk(key.privateJwk));
119
+ };
120
+
121
+ module.exports = {
122
+ mongoAllocationListQueries,
123
+ };
@@ -0,0 +1,76 @@
1
+ /*
2
+ * Copyright 2024 Velocity Team
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ */
17
+
18
+ /** @import { Context, Issuer, AllocationListEntry, AllocationListQueries } from "../../types/types" */
19
+
20
+ const { allocateArray } = require('./utils/allocate-array');
21
+ const { generateListIndex } = require('./utils/generate-list-index');
22
+ /**
23
+ * Allocates list entries
24
+ * @param {number} total the number of entries required (typically the number of offers)
25
+ * @param {Issuer} issuer the issuer
26
+ * @param {string} entityName the entity name
27
+ * @param {number} listSize the list size
28
+ * @param {Context} context the context
29
+ * @returns {Promise<AllocationListEntry[]>} the allocated entries
30
+ */
31
+ const allocateListEntries = async (
32
+ total,
33
+ issuer,
34
+ entityName,
35
+ listSize,
36
+ context
37
+ ) => {
38
+ const entries = [];
39
+ for (let i = 0; i < total; i += 1) {
40
+ entries.push(
41
+ // eslint-disable-next-line no-await-in-loop
42
+ await allocateListEntry(issuer, entityName, listSize, context)
43
+ );
44
+ }
45
+ return entries;
46
+ };
47
+
48
+ /**
49
+ * Gets the next list entry
50
+ * @param {Issuer} issuer the issuer
51
+ * @param {string} entityName the entity name
52
+ * @param {number} listSize the list size
53
+ * @param {Context} context the context
54
+ * @returns {Promise<AllocationListEntry>} the entry
55
+ */
56
+ const allocateListEntry = async (issuer, entityName, listSize, context) => {
57
+ const { allocationListQueries: queries } = context;
58
+ try {
59
+ return await queries.allocateNextEntry(entityName, issuer, context);
60
+ } catch (error) {
61
+ const allocations = allocateArray(listSize);
62
+ const newListId = generateListIndex();
63
+ return queries.createNewAllocationList(
64
+ entityName,
65
+ issuer,
66
+ newListId,
67
+ allocations,
68
+ context
69
+ );
70
+ }
71
+ };
72
+
73
+ module.exports = {
74
+ allocateListEntries,
75
+ allocateListEntry,
76
+ };
@@ -0,0 +1,116 @@
1
+ /*
2
+ * Copyright 2024 Velocity Team
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ */
17
+
18
+ const { toLower } = require('lodash/fp');
19
+ const { mapWithIndex } = require('@verii/common-functions');
20
+ const {
21
+ generateJWAKeyPair,
22
+ get2BytesHash,
23
+ KeyAlgorithms,
24
+ } = require('@verii/crypto');
25
+ const { jsonLdToUnsignedVcJwtContent, jwtSign } = require('@verii/jwt');
26
+ const { extractCredentialType } = require('@verii/vc-checks');
27
+ const { hashOffer } = require('./hash-offer');
28
+ const { buildRevocationUrl } = require('../adapters/build-revocation-url');
29
+ const { prepareJsonLdCredential } = require('./prepare-jsonld-credential');
30
+
31
+ /** @import { Issuer, AllocationListEntry, CredentialOffer, CredentialMetadata, CredentialTypeMetadata, Context } from "../types/types" */
32
+
33
+ /**
34
+ * Builds the VCs
35
+ * @param {CredentialOffer[]} offers array of offers
36
+ * @param {string} credentialSubjectId optional field if credential subject needs to be bound into the offer
37
+ * @param {Issuer} issuer the issuer
38
+ * @param {AllocationListEntry[]} metadataEntries metadata entries
39
+ * @param {AllocationListEntry[]} revocationListEntries revocation list entries
40
+ * @param {{[Name: string]: CredentialTypeMetadata}} credentialTypesMap the credential types
41
+ * @param {Context} context the context
42
+ * @returns {Promise<{metadata: CredentialMetadata, vcJwt: string}[]>} the vc and its metadata
43
+ */
44
+ const buildVerifiableCredentials = async (
45
+ offers,
46
+ credentialSubjectId,
47
+ issuer,
48
+ metadataEntries,
49
+ revocationListEntries,
50
+ credentialTypesMap,
51
+ context
52
+ ) => {
53
+ return Promise.all(
54
+ mapWithIndex(async (offer, i) => {
55
+ const metadataEntry = metadataEntries[i];
56
+ const credentialType = extractCredentialType(offer);
57
+ const digitalSignatureAlgorithm =
58
+ credentialTypesMap[credentialType].defaultSignatureAlgorithm ??
59
+ KeyAlgorithms.SECP256K1;
60
+
61
+ const keyPair = generateJWAKeyPair(digitalSignatureAlgorithm);
62
+
63
+ const metadata = {
64
+ ...metadataEntry,
65
+ credentialType,
66
+ credentialTypeEncoded: get2BytesHash(credentialType), // TODO replace with bytes encoding from credentialMetadata
67
+ contentHash: hashOffer(offer),
68
+ publicKey: keyPair.publicKey,
69
+ };
70
+
71
+ const credentialId = buildVelocityCredentialMetadataDID(
72
+ metadataEntry,
73
+ issuer,
74
+ metadata.contentHash
75
+ );
76
+ const revocationUrl = buildRevocationUrl(
77
+ revocationListEntries[i],
78
+ issuer,
79
+ context
80
+ );
81
+ const jsonLdCredential = prepareJsonLdCredential(
82
+ issuer,
83
+ credentialSubjectId,
84
+ offer,
85
+ credentialId,
86
+ metadata.contentHash, // TODO remove June 2026
87
+ credentialTypesMap[metadata.credentialType],
88
+ revocationUrl,
89
+ context
90
+ );
91
+
92
+ const { header, payload } = jsonLdToUnsignedVcJwtContent(
93
+ jsonLdCredential,
94
+ digitalSignatureAlgorithm,
95
+ `${credentialId}#key-1`
96
+ );
97
+ const vcJwt = await jwtSign(payload, keyPair.privateKey, header);
98
+
99
+ return { metadata, jsonLdCredential, vcJwt };
100
+ }, offers)
101
+ );
102
+ };
103
+
104
+ /**
105
+ * Builds a credential metadata DID URI
106
+ * @param {AllocationListEntry} entry the list entry
107
+ * @param {Issuer} issuer the issuer
108
+ * @param {string} contentHash the content hash of the credential
109
+ * @returns {string} the DID URI for the location on the credential metadata list
110
+ */
111
+ const buildVelocityCredentialMetadataDID = (entry, issuer, contentHash) =>
112
+ `did:velocity:v2:${toLower(issuer.dltPrimaryAddress)}:${entry.listId}:${
113
+ entry.index
114
+ }:${contentHash}`;
115
+
116
+ module.exports = { buildVerifiableCredentials };
@@ -0,0 +1,35 @@
1
+ /*
2
+ * Copyright 2024 Velocity Team
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ */
17
+
18
+ const { flow, pick } = require('lodash/fp');
19
+ const canonicalize = require('canonicalize');
20
+ const { hashAndEncodeHex } = require('@verii/crypto');
21
+ /** @import { CredentialOffer } from "../types/types" */
22
+
23
+ /**
24
+ * The hex encoded hash of the CredentialOffer
25
+ * @param {CredentialOffer} offer the offer to hash
26
+ * @returns {string} the hash of the offer encoded in hex
27
+ */
28
+ const hashOffer = (offer) =>
29
+ flow(
30
+ pick(['credentialSubject', 'validFrom', 'expirationDate', 'validUntil']),
31
+ canonicalize,
32
+ hashAndEncodeHex
33
+ )(offer);
34
+
35
+ module.exports = { hashOffer };