@credo-ts/node 0.7.2-pr-2704-20260918194459 → 0.7.2
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/build/NodeFileSystem.d.mts +1 -3
- package/build/NodeFileSystem.d.mts.map +1 -1
- package/build/NodeFileSystem.mjs +1 -2
- package/build/NodeFileSystem.mjs.map +1 -1
- package/build/_virtual/{_@oxc-project_runtime@0.112.0/helpers → _@oxc-project_runtime@0.151.0/helpers/esm}/assertClassBrand.mjs +2 -3
- package/build/_virtual/{_@oxc-project_runtime@0.112.0/helpers → _@oxc-project_runtime@0.151.0/helpers/esm}/checkPrivateRedeclaration.mjs +2 -3
- package/build/_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/classPrivateFieldGet2.mjs +7 -0
- package/build/_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/classPrivateFieldInitSpec.mjs +7 -0
- package/build/_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/classPrivateFieldSet2.mjs +7 -0
- package/build/index.d.mts +0 -1
- package/build/index.d.mts.map +1 -1
- package/build/index.mjs +3 -4
- package/build/index.mjs.map +1 -1
- package/build/kms/NodeInMemoryKeyManagementStorage.d.mts +1 -3
- package/build/kms/NodeInMemoryKeyManagementStorage.d.mts.map +1 -1
- package/build/kms/NodeInMemoryKeyManagementStorage.mjs +3 -4
- package/build/kms/NodeInMemoryKeyManagementStorage.mjs.map +1 -1
- package/build/kms/NodeKeyManagementService.d.mts +1 -3
- package/build/kms/NodeKeyManagementService.d.mts.map +1 -1
- package/build/kms/NodeKeyManagementService.mjs +7 -9
- package/build/kms/NodeKeyManagementService.mjs.map +1 -1
- package/build/kms/NodeKeyManagementStorage.d.mts +1 -3
- package/build/kms/NodeKeyManagementStorage.d.mts.map +1 -1
- package/build/kms/crypto/createKey.mjs +1 -2
- package/build/kms/crypto/createKey.mjs.map +1 -1
- package/build/kms/crypto/decrypt.mjs +8 -6
- package/build/kms/crypto/decrypt.mjs.map +1 -1
- package/build/kms/crypto/deriveKey.mjs +2 -83
- package/build/kms/crypto/deriveKey.mjs.map +1 -1
- package/build/kms/crypto/encrypt.mjs +1 -2
- package/build/kms/crypto/encrypt.mjs.map +1 -1
- package/build/kms/crypto/hpke.mjs +1 -2
- package/build/kms/crypto/hpke.mjs.map +1 -1
- package/build/kms/crypto/sign.mjs +9 -7
- package/build/kms/crypto/sign.mjs.map +1 -1
- package/build/kms/crypto/verify.mjs +13 -8
- package/build/kms/crypto/verify.mjs.map +1 -1
- package/build/transport/DidCommHttpInboundTransport.d.mts +3 -11
- package/build/transport/DidCommHttpInboundTransport.d.mts.map +1 -1
- package/build/transport/DidCommHttpInboundTransport.mjs +2 -3
- package/build/transport/DidCommHttpInboundTransport.mjs.map +1 -1
- package/build/transport/DidCommWsInboundTransport.d.mts +3 -8
- package/build/transport/DidCommWsInboundTransport.d.mts.map +1 -1
- package/build/transport/DidCommWsInboundTransport.mjs +5 -6
- package/build/transport/DidCommWsInboundTransport.mjs.map +1 -1
- package/package.json +6 -6
- package/build/_virtual/_@oxc-project_runtime@0.112.0/helpers/classPrivateFieldGet2.mjs +0 -9
- package/build/_virtual/_@oxc-project_runtime@0.112.0/helpers/classPrivateFieldInitSpec.mjs +0 -9
- package/build/_virtual/_@oxc-project_runtime@0.112.0/helpers/classPrivateFieldSet2.mjs +0 -9
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Kms, TypedArrayEncoder } from "@credo-ts/core";
|
|
2
2
|
import { createECDH, createHash, getRandomValues, subtle } from "node:crypto";
|
|
3
3
|
import { Buffer } from "node:buffer";
|
|
4
|
-
|
|
5
4
|
//#region src/kms/crypto/deriveKey.ts
|
|
6
5
|
const nodeSupportedEcdhKeyDerivationEcCrv = [
|
|
7
6
|
"P-256",
|
|
@@ -13,18 +12,13 @@ const nodeSupportedKeyAgreementAlgorithms = [
|
|
|
13
12
|
"ECDH-ES",
|
|
14
13
|
"ECDH-ES+A128KW",
|
|
15
14
|
"ECDH-ES+A192KW",
|
|
16
|
-
"ECDH-ES+A256KW"
|
|
17
|
-
"ECDH-1PU+A256KW"
|
|
15
|
+
"ECDH-ES+A256KW"
|
|
18
16
|
];
|
|
19
17
|
function assertNodeSupportedEcdhKeyDerivationCrv(jwk) {
|
|
20
18
|
if (jwk.kty === "OKP" && jwk.crv !== "X25519" || jwk.kty === "EC" && !nodeSupportedEcdhKeyDerivationEcCrv.includes(jwk.crv)) throw new Kms.KeyManagementAlgorithmNotSupportedError(`key derivation with crv '${jwk.crv}' for kty '${jwk.kty}'`, "node");
|
|
21
19
|
}
|
|
22
20
|
async function deriveEncryptionKey(options) {
|
|
23
21
|
const { keyAgreement, encryption, privateJwk } = options;
|
|
24
|
-
if (keyAgreement.algorithm === "ECDH-1PU+A256KW") return deriveEncryptionKeyEcdh1Pu({
|
|
25
|
-
keyAgreement,
|
|
26
|
-
privateJwk
|
|
27
|
-
});
|
|
28
22
|
assertNodeSupportedEcdhKeyDerivationCrv(keyAgreement.externalPublicJwk);
|
|
29
23
|
assertNodeSupportedEcdhKeyDerivationCrv(privateJwk);
|
|
30
24
|
const derivedKeyBytes = await deriveKeyEcdhEs({
|
|
@@ -51,55 +45,8 @@ async function deriveEncryptionKey(options) {
|
|
|
51
45
|
}
|
|
52
46
|
};
|
|
53
47
|
}
|
|
54
|
-
async function deriveEncryptionKeyEcdh1Pu(options) {
|
|
55
|
-
const { keyAgreement, privateJwk } = options;
|
|
56
|
-
if (privateJwk.crv !== "X25519" || keyAgreement.externalPublicJwk.crv !== "X25519") throw new Kms.KeyManagementAlgorithmNotSupportedError("ECDH-1PU+A256KW requires X25519 keys", "node");
|
|
57
|
-
const ecdh = createECDH("x25519");
|
|
58
|
-
const ephemeralPrivate = getRandomValues(new Uint8Array(32));
|
|
59
|
-
ecdh.setPrivateKey(Buffer.from(ephemeralPrivate));
|
|
60
|
-
const recipientPub = Kms.PublicJwk.fromPublicJwk(keyAgreement.externalPublicJwk).publicKey;
|
|
61
|
-
if (recipientPub.kty !== "OKP") throw new Kms.KeyManagementError("X25519 expected");
|
|
62
|
-
const z1 = ecdh.computeSecret(recipientPub.publicKey);
|
|
63
|
-
const senderEcdh = createECDH("x25519");
|
|
64
|
-
senderEcdh.setPrivateKey(TypedArrayEncoder.fromBase64(privateJwk.d));
|
|
65
|
-
const z2 = senderEcdh.computeSecret(recipientPub.publicKey);
|
|
66
|
-
const Z = Buffer.concat([Buffer.from(z1), Buffer.from(z2)]);
|
|
67
|
-
const algorithmId = Buffer.from("ECDH-1PU+A256KW");
|
|
68
|
-
const kek = concatKDF(Z, 256, 256, Buffer.concat([
|
|
69
|
-
numberTo4ByteUint8Array(algorithmId.length),
|
|
70
|
-
algorithmId,
|
|
71
|
-
numberTo4ByteUint8Array(0),
|
|
72
|
-
numberTo4ByteUint8Array(0),
|
|
73
|
-
numberTo4ByteUint8Array(256),
|
|
74
|
-
Buffer.alloc(0)
|
|
75
|
-
]));
|
|
76
|
-
const derivedKey = await subtle.importKey("raw", kek, "AES-KW", true, ["wrapKey"]);
|
|
77
|
-
const cekBytes = Buffer.from(getRandomValues(new Uint8Array(32)));
|
|
78
|
-
const cek = await subtle.importKey("raw", cekBytes, "AES-KW", true, ["wrapKey"]);
|
|
79
|
-
const encryptedCek = await subtle.wrapKey("raw", cek, derivedKey, "AES-KW");
|
|
80
|
-
const epkPub = ecdh.getPublicKey();
|
|
81
|
-
const epkJwk = {
|
|
82
|
-
kty: "OKP",
|
|
83
|
-
crv: "X25519",
|
|
84
|
-
x: TypedArrayEncoder.toBase64Url(epkPub)
|
|
85
|
-
};
|
|
86
|
-
return {
|
|
87
|
-
encryptedContentEncryptionKey: {
|
|
88
|
-
encrypted: Buffer.from(encryptedCek),
|
|
89
|
-
ephemeralPublicKey: epkJwk
|
|
90
|
-
},
|
|
91
|
-
contentEncryptionKey: {
|
|
92
|
-
kty: "oct",
|
|
93
|
-
k: TypedArrayEncoder.toBase64Url(cekBytes)
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
48
|
async function deriveDecryptionKey(options) {
|
|
98
49
|
const { keyAgreement, decryption, privateJwk } = options;
|
|
99
|
-
if (keyAgreement.algorithm === "ECDH-1PU+A256KW") return deriveDecryptionKeyEcdh1Pu({
|
|
100
|
-
keyAgreement,
|
|
101
|
-
privateJwk
|
|
102
|
-
});
|
|
103
50
|
assertNodeSupportedEcdhKeyDerivationCrv(keyAgreement.externalPublicJwk);
|
|
104
51
|
assertNodeSupportedEcdhKeyDerivationCrv(privateJwk);
|
|
105
52
|
const derivedKeyBytes = await deriveKeyEcdhEs({
|
|
@@ -121,34 +68,6 @@ async function deriveDecryptionKey(options) {
|
|
|
121
68
|
}, true, ["decrypt"]);
|
|
122
69
|
return { contentEncryptionKey: await subtle.exportKey("jwk", contentEncryptionKey) };
|
|
123
70
|
}
|
|
124
|
-
async function deriveDecryptionKeyEcdh1Pu(options) {
|
|
125
|
-
const { keyAgreement, privateJwk } = options;
|
|
126
|
-
const { ephemeralPublicJwk, senderPublicJwk } = keyAgreement;
|
|
127
|
-
if (privateJwk.crv !== "X25519") throw new Kms.KeyManagementAlgorithmNotSupportedError("ECDH-1PU+A256KW requires X25519", "node");
|
|
128
|
-
const recipientEcdh = createECDH("x25519");
|
|
129
|
-
recipientEcdh.setPrivateKey(TypedArrayEncoder.fromBase64(privateJwk.d));
|
|
130
|
-
const epk = Kms.PublicJwk.fromPublicJwk(ephemeralPublicJwk).publicKey;
|
|
131
|
-
const senderPub = Kms.PublicJwk.fromPublicJwk(senderPublicJwk).publicKey;
|
|
132
|
-
if (epk.kty !== "OKP" || senderPub.kty !== "OKP") throw new Kms.KeyManagementError("X25519 keys expected");
|
|
133
|
-
const z1 = recipientEcdh.computeSecret(epk.publicKey);
|
|
134
|
-
const z2 = recipientEcdh.computeSecret(senderPub.publicKey);
|
|
135
|
-
const Z = Buffer.concat([Buffer.from(z1), Buffer.from(z2)]);
|
|
136
|
-
const algorithmId = Buffer.from("ECDH-1PU+A256KW");
|
|
137
|
-
const kek = concatKDF(Z, 256, 256, Buffer.concat([
|
|
138
|
-
numberTo4ByteUint8Array(algorithmId.length),
|
|
139
|
-
algorithmId,
|
|
140
|
-
numberTo4ByteUint8Array(0),
|
|
141
|
-
numberTo4ByteUint8Array(0),
|
|
142
|
-
numberTo4ByteUint8Array(256),
|
|
143
|
-
Buffer.alloc(0)
|
|
144
|
-
]));
|
|
145
|
-
const derivedKey = await subtle.importKey("raw", kek, "AES-KW", true, ["unwrapKey"]);
|
|
146
|
-
const contentEncryptionKey = await subtle.unwrapKey("raw", keyAgreement.encryptedKey.encrypted, derivedKey, "AES-KW", {
|
|
147
|
-
hash: "SHA-256",
|
|
148
|
-
name: "HMAC"
|
|
149
|
-
}, true, ["decrypt"]);
|
|
150
|
-
return { contentEncryptionKey: await subtle.exportKey("jwk", contentEncryptionKey) };
|
|
151
|
-
}
|
|
152
71
|
/**
|
|
153
72
|
* Derive a key using ECDH and Concat KDF
|
|
154
73
|
*/
|
|
@@ -233,7 +152,7 @@ function mapContentEncryptionAlgorithmToKeyLength(encryptionAlgorithm) {
|
|
|
233
152
|
case "XSALSA20-POLY1305": return 256;
|
|
234
153
|
}
|
|
235
154
|
}
|
|
236
|
-
|
|
237
155
|
//#endregion
|
|
238
156
|
export { deriveDecryptionKey, deriveEncryptionKey, nodeSupportedKeyAgreementAlgorithms };
|
|
157
|
+
|
|
239
158
|
//# sourceMappingURL=deriveKey.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deriveKey.mjs","names":[],"sources":["../../../src/kms/crypto/deriveKey.ts"],"sourcesContent":["import { Buffer } from 'node:buffer'\nimport { createECDH, createHash, getRandomValues, subtle } from 'node:crypto'\nimport { Kms, TypedArrayEncoder } from '@credo-ts/core'\nimport type { NodeKmsSupportedEcCrvs } from './createKey'\n\nconst nodeSupportedEcdhKeyDerivationEcCrv = [\n 'P-256',\n 'P-384',\n 'P-521',\n 'secp256k1',\n] as const satisfies NodeKmsSupportedEcCrvs[]\n\nexport const nodeSupportedKeyAgreementAlgorithms = [\n 'ECDH-ES',\n 'ECDH-ES+A128KW',\n 'ECDH-ES+A192KW',\n 'ECDH-ES+A256KW',\n 'ECDH-1PU+A256KW',\n] satisfies Kms.KnownJwaKeyAgreementAlgorithm[]\n\nfunction assertNodeSupportedEcdhKeyDerivationCrv<Jwk extends Kms.KmsJwkPrivateAsymmetric | Kms.KmsJwkPublicAsymmetric>(\n jwk: Jwk\n): asserts jwk is Jwk & { kty: 'OKP' | 'EC'; crv: (typeof nodeSupportedEcdhKeyDerivationEcCrv)[number] | 'X25519' } {\n if (\n (jwk.kty === 'OKP' && jwk.crv !== 'X25519') ||\n (jwk.kty === 'EC' && !(nodeSupportedEcdhKeyDerivationEcCrv as string[]).includes(jwk.crv))\n ) {\n throw new Kms.KeyManagementAlgorithmNotSupportedError(\n `key derivation with crv '${jwk.crv}' for kty '${jwk.kty}'`,\n 'node'\n )\n }\n}\n\ntype NodeSupportedKeyAgreementDecryptOptions = Kms.KmsKeyAgreementDecryptOptions & {\n algorithm: (typeof nodeSupportedKeyAgreementAlgorithms)[number]\n}\ntype NodeSupportedKeyAgreementEncryptOptions = Kms.KmsKeyAgreementEncryptOptions & {\n algorithm: (typeof nodeSupportedKeyAgreementAlgorithms)[number]\n}\n\nexport async function deriveEncryptionKey(options: {\n keyAgreement: NodeSupportedKeyAgreementEncryptOptions\n privateJwk: Kms.KmsJwkPrivateAsymmetric\n encryption: Kms.KmsEncryptDataContentEncryption\n}) {\n const { keyAgreement, encryption, privateJwk } = options\n\n if (keyAgreement.algorithm === 'ECDH-1PU+A256KW') {\n return deriveEncryptionKeyEcdh1Pu({\n keyAgreement,\n privateJwk: privateJwk as Kms.KmsJwkPrivateOkp,\n })\n }\n\n assertNodeSupportedEcdhKeyDerivationCrv(keyAgreement.externalPublicJwk)\n assertNodeSupportedEcdhKeyDerivationCrv(privateJwk)\n\n const keyLength =\n keyAgreement.algorithm === 'ECDH-ES'\n ? mapContentEncryptionAlgorithmToKeyLength(encryption.algorithm)\n : keyAgreement.algorithm === 'ECDH-ES+A128KW'\n ? 128\n : keyAgreement.algorithm === 'ECDH-ES+A192KW'\n ? 192\n : 256\n\n const derivedKeyBytes = await deriveKeyEcdhEs({\n keyLength,\n usageAlgorithm: keyAgreement.algorithm === 'ECDH-ES' ? encryption.algorithm : keyAgreement.algorithm,\n privateJwk,\n publicJwk: keyAgreement.externalPublicJwk,\n apu: keyAgreement.apu,\n apv: keyAgreement.apv,\n })\n\n if (keyAgreement.algorithm === 'ECDH-ES') {\n return {\n contentEncryptionKey: {\n kty: 'oct',\n k: derivedKeyBytes.toString('base64url'),\n } as const,\n }\n }\n\n const derivedKey = await subtle.importKey('raw', derivedKeyBytes, 'AES-KW', true, ['wrapKey'])\n const contentEncryptionKeyBytes = getRandomValues(\n new Uint8Array(mapContentEncryptionAlgorithmToKeyLength(encryption.algorithm) >> 3)\n )\n const contentEncryptionKey = await subtle.importKey('raw', contentEncryptionKeyBytes, 'AES-KW', true, ['wrapKey'])\n const encryptedContentEncryptionKey = await subtle.wrapKey('raw', contentEncryptionKey, derivedKey, 'AES-KW')\n\n return {\n encryptedContentEncryptionKey: {\n encrypted: new Uint8Array(encryptedContentEncryptionKey),\n } satisfies Kms.KmsEncryptedKey,\n contentEncryptionKey: {\n kty: 'oct',\n k: TypedArrayEncoder.toBase64Url(contentEncryptionKeyBytes),\n } as const,\n }\n}\n\nasync function deriveEncryptionKeyEcdh1Pu(options: {\n keyAgreement: Kms.KmsKeyAgreementEncryptOptions & { algorithm: 'ECDH-1PU+A256KW' }\n privateJwk: Kms.KmsJwkPrivateOkp\n}) {\n const { keyAgreement, privateJwk } = options\n if (privateJwk.crv !== 'X25519' || keyAgreement.externalPublicJwk.crv !== 'X25519') {\n throw new Kms.KeyManagementAlgorithmNotSupportedError('ECDH-1PU+A256KW requires X25519 keys', 'node')\n }\n\n const ecdh = createECDH('x25519')\n const ephemeralPrivate = getRandomValues(new Uint8Array(32))\n ecdh.setPrivateKey(Buffer.from(ephemeralPrivate))\n const recipientPub = Kms.PublicJwk.fromPublicJwk(keyAgreement.externalPublicJwk).publicKey\n if (recipientPub.kty !== 'OKP') throw new Kms.KeyManagementError('X25519 expected')\n const z1 = ecdh.computeSecret(recipientPub.publicKey)\n\n const senderEcdh = createECDH('x25519')\n senderEcdh.setPrivateKey(TypedArrayEncoder.fromBase64(privateJwk.d))\n const z2 = senderEcdh.computeSecret(recipientPub.publicKey)\n\n const Z = Buffer.concat([Buffer.from(z1), Buffer.from(z2)])\n const algorithmId = Buffer.from('ECDH-1PU+A256KW')\n const otherInfo = Buffer.concat([\n numberTo4ByteUint8Array(algorithmId.length),\n algorithmId,\n numberTo4ByteUint8Array(0),\n numberTo4ByteUint8Array(0),\n numberTo4ByteUint8Array(256),\n Buffer.alloc(0),\n ])\n const kek = concatKDF(Z, 256, 256, otherInfo)\n\n const derivedKey = await subtle.importKey('raw', kek, 'AES-KW', true, ['wrapKey'])\n const cekBytes = Buffer.from(getRandomValues(new Uint8Array(32)))\n const cek = await subtle.importKey('raw', cekBytes, 'AES-KW', true, ['wrapKey'])\n const encryptedCek = await subtle.wrapKey('raw', cek, derivedKey, 'AES-KW')\n\n const epkPub = ecdh.getPublicKey()\n const epkJwk = {\n kty: 'OKP' as const,\n crv: 'X25519' as const,\n x: TypedArrayEncoder.toBase64Url(epkPub),\n }\n\n return {\n encryptedContentEncryptionKey: {\n encrypted: Buffer.from(encryptedCek),\n ephemeralPublicKey: epkJwk,\n } satisfies Kms.KmsEncryptedKey,\n contentEncryptionKey: {\n kty: 'oct' as const,\n k: TypedArrayEncoder.toBase64Url(cekBytes),\n },\n }\n}\n\nexport async function deriveDecryptionKey(options: {\n keyAgreement: NodeSupportedKeyAgreementDecryptOptions\n privateJwk: Kms.KmsJwkPrivateAsymmetric\n decryption: Kms.KmsDecryptDataContentDecryption\n}) {\n const { keyAgreement, decryption, privateJwk } = options\n\n if (keyAgreement.algorithm === 'ECDH-1PU+A256KW') {\n return deriveDecryptionKeyEcdh1Pu({\n keyAgreement,\n privateJwk: privateJwk as Kms.KmsJwkPrivateOkp,\n })\n }\n\n assertNodeSupportedEcdhKeyDerivationCrv(keyAgreement.externalPublicJwk)\n assertNodeSupportedEcdhKeyDerivationCrv(privateJwk)\n\n const keyLength =\n keyAgreement.algorithm === 'ECDH-ES'\n ? mapContentEncryptionAlgorithmToKeyLength(decryption.algorithm)\n : keyAgreement.algorithm === 'ECDH-ES+A128KW'\n ? 128\n : keyAgreement.algorithm === 'ECDH-ES+A192KW'\n ? 192\n : 256\n\n const derivedKeyBytes = await deriveKeyEcdhEs({\n keyLength,\n usageAlgorithm: keyAgreement.algorithm === 'ECDH-ES' ? decryption.algorithm : keyAgreement.algorithm,\n privateJwk: privateJwk,\n publicJwk: keyAgreement.externalPublicJwk,\n apu: keyAgreement.apu,\n apv: keyAgreement.apv,\n })\n\n if (keyAgreement.algorithm === 'ECDH-ES') {\n return {\n // TODO: will be more efficient to return node key instance\n contentEncryptionKey: {\n kty: 'oct',\n k: derivedKeyBytes.toString('base64url'),\n } as const,\n }\n }\n\n // Key wrapping\n const derivedKey = await subtle.importKey('raw', derivedKeyBytes, 'AES-KW', true, ['wrapKey'])\n\n const contentEncryptionKey = await subtle.unwrapKey(\n 'raw',\n keyAgreement.encryptedKey.encrypted,\n derivedKey,\n 'AES-KW',\n { hash: 'SHA-256', name: 'HMAC' },\n true,\n ['decrypt']\n )\n\n return {\n contentEncryptionKey: (await subtle.exportKey('jwk', contentEncryptionKey)) as Kms.KmsJwkPrivate,\n }\n}\n\nasync function deriveDecryptionKeyEcdh1Pu(options: {\n keyAgreement: Kms.KmsKeyAgreementDecryptOptions & { algorithm: 'ECDH-1PU+A256KW' }\n privateJwk: Kms.KmsJwkPrivateOkp\n}) {\n const { keyAgreement, privateJwk } = options\n const { ephemeralPublicJwk, senderPublicJwk } = keyAgreement\n if (privateJwk.crv !== 'X25519') {\n throw new Kms.KeyManagementAlgorithmNotSupportedError('ECDH-1PU+A256KW requires X25519', 'node')\n }\n\n const recipientEcdh = createECDH('x25519')\n recipientEcdh.setPrivateKey(TypedArrayEncoder.fromBase64(privateJwk.d))\n const epk = Kms.PublicJwk.fromPublicJwk(ephemeralPublicJwk).publicKey\n const senderPub = Kms.PublicJwk.fromPublicJwk(senderPublicJwk).publicKey\n if (epk.kty !== 'OKP' || senderPub.kty !== 'OKP') {\n throw new Kms.KeyManagementError('X25519 keys expected')\n }\n\n const z1 = recipientEcdh.computeSecret(epk.publicKey)\n const z2 = recipientEcdh.computeSecret(senderPub.publicKey)\n const Z = Buffer.concat([Buffer.from(z1), Buffer.from(z2)])\n\n const algorithmId = Buffer.from('ECDH-1PU+A256KW')\n const otherInfo = Buffer.concat([\n numberTo4ByteUint8Array(algorithmId.length),\n algorithmId,\n numberTo4ByteUint8Array(0),\n numberTo4ByteUint8Array(0),\n numberTo4ByteUint8Array(256),\n Buffer.alloc(0),\n ])\n const kek = concatKDF(Z, 256, 256, otherInfo)\n const derivedKey = await subtle.importKey('raw', kek, 'AES-KW', true, ['unwrapKey'])\n\n const contentEncryptionKey = await subtle.unwrapKey(\n 'raw',\n keyAgreement.encryptedKey.encrypted,\n derivedKey,\n 'AES-KW',\n { hash: 'SHA-256', name: 'HMAC' },\n true,\n ['decrypt']\n )\n return {\n contentEncryptionKey: (await subtle.exportKey('jwk', contentEncryptionKey)) as Kms.KmsJwkPrivate,\n }\n}\n\n/**\n * Derive a key using ECDH and Concat KDF\n */\nasync function deriveKeyEcdhEs(options: {\n keyLength: number\n /**\n * This is only used for the AlgorithmID in KDF\n */\n usageAlgorithm: string\n apv?: Uint8Array\n apu?: Uint8Array\n privateJwk: Kms.KmsJwkPrivateEc | Kms.KmsJwkPrivateOkp\n publicJwk: Kms.KmsJwkPublicEc | Kms.KmsJwkPublicOkp\n}): Promise<Buffer> {\n // const privateKey = createPrivateKey({ format: 'jwk', key: options.privateJwk })\n // const publicKey = createPublicKey({ format: 'jwk', key: options.publicJwk })\n\n // Create ECDH instance based on curve\n const nodeEcdhCurveName = mapCrvToNodeEcdhCurveName(options.privateJwk.crv)\n const nodeConcatKdfHash = mapCrvToHashLength(options.publicJwk.crv)\n\n const ecdh = createECDH(nodeEcdhCurveName)\n\n // Set private key\n ecdh.setPrivateKey(TypedArrayEncoder.fromBase64Url(options.privateJwk.d))\n\n const publicKey = Kms.PublicJwk.fromPublicJwk(options.publicJwk).publicKey\n if (publicKey.kty === 'RSA') {\n throw new Kms.KeyManagementError('Key type RSA is not supported for ECDH-ES')\n }\n\n // Compute shared secret\n const sharedSecret = ecdh.computeSecret(publicKey.publicKey)\n\n // Prepare AlgorithmID for KDF (Datalen || Data)\n const algorithmData = TypedArrayEncoder.fromUtf8String(options.usageAlgorithm) // ASCII representation of alg\n const algorithmID = TypedArrayEncoder.concat([\n numberTo4ByteUint8Array(algorithmData.length), // Datalen: 32-bit big-endian counter\n algorithmData, // Data: ASCII representation of algorithm\n ])\n\n // Prepare PartyUInfo with proper length prefix\n const apu = options.apu || Buffer.alloc(0)\n const partyUInfo = Buffer.concat([\n numberTo4ByteUint8Array(apu.length), // Datalen: 32-bit big-endian counter\n apu, // Data: PartyUInfo value\n ])\n\n // Prepare PartyVInfo with proper length prefix\n const apv = options.apv || Buffer.alloc(0)\n const partyVInfo = Buffer.concat([\n numberTo4ByteUint8Array(apv.length), // Datalen: 32-bit big-endian counter\n apv, // Data: PartyVInfo value\n ])\n\n // Prepare otherInfo for KDF\n const otherInfo = Buffer.concat([\n algorithmID, // AlgorithmID: Datalen || Data\n partyUInfo, // PartyUInfo: Datalen || Data\n partyVInfo, // PartyVInfo: Datalen || Data\n numberTo4ByteUint8Array(options.keyLength), // SuppPubInfo: 32-bit big-endian rep of keydatalen\n Buffer.alloc(0), // SuppPrivInfo (empty octet sequence)\n ])\n\n // Derive final key using Concat KDF\n return concatKDF(sharedSecret, options.keyLength, nodeConcatKdfHash, otherInfo)\n}\n\nfunction numberTo4ByteUint8Array(number: number) {\n const buffer = new ArrayBuffer(4)\n const view = new DataView(buffer)\n view.setUint32(0, number)\n return new Uint8Array(buffer)\n}\n\n/**\n * Implements Concat KDF as per NIST SP 800-56A\n */\nfunction concatKDF(secret: Buffer, length: number, hashLength: ConcatKdfHashLength, otherInfo: Buffer): Buffer {\n const reps = Math.ceil((length >> 3) / (hashLength >> 3))\n const output = Buffer.alloc(reps * (hashLength >> 3))\n\n for (let i = 0; i < reps; i++) {\n const counter = Buffer.alloc(4 + secret.length + otherInfo.length)\n counter.writeUInt32BE(i + 1)\n counter.set(secret, 4)\n counter.set(otherInfo, 4 + secret.length)\n\n createHash(`sha${hashLength}`)\n .update(counter)\n .digest()\n .copy(output, (i * hashLength) >> 3)\n }\n\n return output.subarray(0, length >> 3)\n}\n\nfunction mapCrvToNodeEcdhCurveName(crv: Kms.KmsJwkPublicEc['crv'] | Kms.KmsJwkPublicOkp['crv']) {\n switch (crv) {\n case 'P-256':\n return 'prime256v1'\n case 'P-384':\n return 'secp384r1'\n case 'P-521':\n return 'secp521r1'\n case 'secp256k1':\n return 'secp256k1'\n case 'X25519':\n return 'x25519'\n default:\n throw new Kms.KeyManagementAlgorithmNotSupportedError(`crv '${crv}' for ECDH-ES`, 'node')\n }\n}\n\ntype ConcatKdfHashLength = ReturnType<typeof mapCrvToHashLength>\nfunction mapCrvToHashLength(crv: Kms.KmsJwkPublicEc['crv'] | Kms.KmsJwkPublicOkp['crv']) {\n switch (crv) {\n case 'secp256k1':\n case 'X25519':\n case 'P-256':\n return 256\n case 'P-384':\n return 384\n case 'P-521':\n return 512\n default:\n throw new Kms.KeyManagementAlgorithmNotSupportedError(`crv '${crv}' for ECDH-ES`, 'node')\n }\n}\n\n// TODO: might be worthwhile to add this to core?\n// TODO: we might want to have a separate definition per algorithm\n// defines things such as required key length.\nfunction mapContentEncryptionAlgorithmToKeyLength(\n encryptionAlgorithm: Kms.KnownJwaContentEncryptionAlgorithm | Kms.KnownJwaKeyEncryptionAlgorithm\n): number {\n switch (encryptionAlgorithm) {\n case 'A128CBC':\n case 'A128GCM':\n case 'A128KW':\n return 128\n case 'A192KW':\n return 192\n case 'A128CBC-HS256':\n case 'A256CBC':\n case 'A256GCM':\n case 'C20P':\n case 'XC20P':\n case 'A256KW':\n return 256\n\n case 'A192CBC-HS384':\n case 'A192GCM':\n return 384\n case 'A256CBC-HS512':\n return 512\n case 'XSALSA20-POLY1305':\n return 256\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"deriveKey.mjs","names":[],"sources":["../../../src/kms/crypto/deriveKey.ts"],"sourcesContent":["import { Buffer } from 'node:buffer'\nimport { createECDH, createHash, getRandomValues, subtle } from 'node:crypto'\nimport { Kms, TypedArrayEncoder } from '@credo-ts/core'\nimport type { NodeKmsSupportedEcCrvs } from './createKey'\n\nconst nodeSupportedEcdhKeyDerivationEcCrv = [\n 'P-256',\n 'P-384',\n 'P-521',\n 'secp256k1',\n] as const satisfies NodeKmsSupportedEcCrvs[]\n\nexport const nodeSupportedKeyAgreementAlgorithms = [\n 'ECDH-ES',\n 'ECDH-ES+A128KW',\n 'ECDH-ES+A192KW',\n 'ECDH-ES+A256KW',\n] satisfies Kms.KnownJwaKeyAgreementAlgorithm[]\n\nfunction assertNodeSupportedEcdhKeyDerivationCrv<Jwk extends Kms.KmsJwkPrivateAsymmetric | Kms.KmsJwkPublicAsymmetric>(\n jwk: Jwk\n): asserts jwk is Jwk & { kty: 'OKP' | 'EC'; crv: (typeof nodeSupportedEcdhKeyDerivationEcCrv)[number] | 'X25519' } {\n if (\n (jwk.kty === 'OKP' && jwk.crv !== 'X25519') ||\n (jwk.kty === 'EC' && !(nodeSupportedEcdhKeyDerivationEcCrv as string[]).includes(jwk.crv))\n ) {\n throw new Kms.KeyManagementAlgorithmNotSupportedError(\n `key derivation with crv '${jwk.crv}' for kty '${jwk.kty}'`,\n 'node'\n )\n }\n}\n\ntype NodeSupportedKeyAgreementDecryptOptions = Kms.KmsKeyAgreementDecryptOptions & {\n algorithm: (typeof nodeSupportedKeyAgreementAlgorithms)[number]\n}\ntype NodeSupportedKeyAgreementEncryptOptions = Kms.KmsKeyAgreementEncryptOptions & {\n algorithm: (typeof nodeSupportedKeyAgreementAlgorithms)[number]\n}\n\nexport async function deriveEncryptionKey(options: {\n keyAgreement: NodeSupportedKeyAgreementEncryptOptions\n privateJwk: Kms.KmsJwkPrivateAsymmetric\n encryption: Kms.KmsEncryptDataContentEncryption\n}) {\n const { keyAgreement, encryption, privateJwk } = options\n\n assertNodeSupportedEcdhKeyDerivationCrv(keyAgreement.externalPublicJwk)\n assertNodeSupportedEcdhKeyDerivationCrv(privateJwk)\n\n const keyLength =\n keyAgreement.algorithm === 'ECDH-ES'\n ? mapContentEncryptionAlgorithmToKeyLength(encryption.algorithm)\n : keyAgreement.algorithm === 'ECDH-ES+A128KW'\n ? 128\n : keyAgreement.algorithm === 'ECDH-ES+A192KW'\n ? 192\n : 256\n\n const derivedKeyBytes = await deriveKeyEcdhEs({\n keyLength,\n usageAlgorithm: keyAgreement.algorithm === 'ECDH-ES' ? encryption.algorithm : keyAgreement.algorithm,\n privateJwk,\n publicJwk: keyAgreement.externalPublicJwk,\n apu: keyAgreement.apu,\n apv: keyAgreement.apv,\n })\n\n if (keyAgreement.algorithm === 'ECDH-ES') {\n return {\n // TODO: will be more efficient to return node key instance\n contentEncryptionKey: {\n kty: 'oct',\n k: derivedKeyBytes.toString('base64url'),\n } as const,\n }\n }\n\n // Key wrapping\n const derivedKey = await subtle.importKey('raw', derivedKeyBytes, 'AES-KW', true, ['wrapKey'])\n const contentEncryptionKeyBytes = getRandomValues(\n new Uint8Array(mapContentEncryptionAlgorithmToKeyLength(encryption.algorithm) >> 3)\n )\n const contentEncryptionKey = await subtle.importKey('raw', contentEncryptionKeyBytes, 'AES-KW', true, ['wrapKey'])\n const encryptedContentEncryptionKey = await subtle.wrapKey('raw', contentEncryptionKey, derivedKey, 'AES-KW')\n\n return {\n encryptedContentEncryptionKey: {\n encrypted: new Uint8Array(encryptedContentEncryptionKey),\n } satisfies Kms.KmsEncryptedKey,\n contentEncryptionKey: {\n kty: 'oct',\n k: TypedArrayEncoder.toBase64Url(contentEncryptionKeyBytes),\n } as const,\n }\n}\n\nexport async function deriveDecryptionKey(options: {\n keyAgreement: NodeSupportedKeyAgreementDecryptOptions\n privateJwk: Kms.KmsJwkPrivateAsymmetric\n decryption: Kms.KmsDecryptDataContentDecryption\n}) {\n const { keyAgreement, decryption, privateJwk } = options\n\n assertNodeSupportedEcdhKeyDerivationCrv(keyAgreement.externalPublicJwk)\n assertNodeSupportedEcdhKeyDerivationCrv(privateJwk)\n\n const keyLength =\n keyAgreement.algorithm === 'ECDH-ES'\n ? mapContentEncryptionAlgorithmToKeyLength(decryption.algorithm)\n : keyAgreement.algorithm === 'ECDH-ES+A128KW'\n ? 128\n : keyAgreement.algorithm === 'ECDH-ES+A192KW'\n ? 192\n : 256\n\n const derivedKeyBytes = await deriveKeyEcdhEs({\n keyLength,\n usageAlgorithm: keyAgreement.algorithm === 'ECDH-ES' ? decryption.algorithm : keyAgreement.algorithm,\n privateJwk: privateJwk,\n publicJwk: keyAgreement.externalPublicJwk,\n apu: keyAgreement.apu,\n apv: keyAgreement.apv,\n })\n\n if (keyAgreement.algorithm === 'ECDH-ES') {\n return {\n // TODO: will be more efficient to return node key instance\n contentEncryptionKey: {\n kty: 'oct',\n k: derivedKeyBytes.toString('base64url'),\n } as const,\n }\n }\n\n // Key wrapping\n const derivedKey = await subtle.importKey('raw', derivedKeyBytes, 'AES-KW', true, ['wrapKey'])\n\n const contentEncryptionKey = await subtle.unwrapKey(\n 'raw',\n keyAgreement.encryptedKey.encrypted,\n derivedKey,\n 'AES-KW',\n // algorithm used is irrelevant\n { hash: 'SHA-256', name: 'HMAC' },\n true,\n ['decrypt']\n )\n\n return {\n contentEncryptionKey: (await subtle.exportKey('jwk', contentEncryptionKey)) as Kms.KmsJwkPrivate,\n }\n}\n\n/**\n * Derive a key using ECDH and Concat KDF\n */\nasync function deriveKeyEcdhEs(options: {\n keyLength: number\n /**\n * This is only used for the AlgorithmID in KDF\n */\n usageAlgorithm: string\n apv?: Uint8Array\n apu?: Uint8Array\n privateJwk: Kms.KmsJwkPrivateEc | Kms.KmsJwkPrivateOkp\n publicJwk: Kms.KmsJwkPublicEc | Kms.KmsJwkPublicOkp\n}): Promise<Buffer> {\n // const privateKey = createPrivateKey({ format: 'jwk', key: options.privateJwk })\n // const publicKey = createPublicKey({ format: 'jwk', key: options.publicJwk })\n\n // Create ECDH instance based on curve\n const nodeEcdhCurveName = mapCrvToNodeEcdhCurveName(options.privateJwk.crv)\n const nodeConcatKdfHash = mapCrvToHashLength(options.publicJwk.crv)\n\n const ecdh = createECDH(nodeEcdhCurveName)\n\n // Set private key\n ecdh.setPrivateKey(TypedArrayEncoder.fromBase64Url(options.privateJwk.d))\n\n const publicKey = Kms.PublicJwk.fromPublicJwk(options.publicJwk).publicKey\n if (publicKey.kty === 'RSA') {\n throw new Kms.KeyManagementError('Key type RSA is not supported for ECDH-ES')\n }\n\n // Compute shared secret\n const sharedSecret = ecdh.computeSecret(publicKey.publicKey)\n\n // Prepare AlgorithmID for KDF (Datalen || Data)\n const algorithmData = TypedArrayEncoder.fromUtf8String(options.usageAlgorithm) // ASCII representation of alg\n const algorithmID = TypedArrayEncoder.concat([\n numberTo4ByteUint8Array(algorithmData.length), // Datalen: 32-bit big-endian counter\n algorithmData, // Data: ASCII representation of algorithm\n ])\n\n // Prepare PartyUInfo with proper length prefix\n const apu = options.apu || Buffer.alloc(0)\n const partyUInfo = Buffer.concat([\n numberTo4ByteUint8Array(apu.length), // Datalen: 32-bit big-endian counter\n apu, // Data: PartyUInfo value\n ])\n\n // Prepare PartyVInfo with proper length prefix\n const apv = options.apv || Buffer.alloc(0)\n const partyVInfo = Buffer.concat([\n numberTo4ByteUint8Array(apv.length), // Datalen: 32-bit big-endian counter\n apv, // Data: PartyVInfo value\n ])\n\n // Prepare otherInfo for KDF\n const otherInfo = Buffer.concat([\n algorithmID, // AlgorithmID: Datalen || Data\n partyUInfo, // PartyUInfo: Datalen || Data\n partyVInfo, // PartyVInfo: Datalen || Data\n numberTo4ByteUint8Array(options.keyLength), // SuppPubInfo: 32-bit big-endian rep of keydatalen\n Buffer.alloc(0), // SuppPrivInfo (empty octet sequence)\n ])\n\n // Derive final key using Concat KDF\n return concatKDF(sharedSecret, options.keyLength, nodeConcatKdfHash, otherInfo)\n}\n\nfunction numberTo4ByteUint8Array(number: number) {\n const buffer = new ArrayBuffer(4)\n const view = new DataView(buffer)\n view.setUint32(0, number)\n return new Uint8Array(buffer)\n}\n\n/**\n * Implements Concat KDF as per NIST SP 800-56A\n */\nfunction concatKDF(secret: Buffer, length: number, hashLength: ConcatKdfHashLength, otherInfo: Buffer): Buffer {\n const reps = Math.ceil((length >> 3) / (hashLength >> 3))\n const output = Buffer.alloc(reps * (hashLength >> 3))\n\n for (let i = 0; i < reps; i++) {\n const counter = Buffer.alloc(4 + secret.length + otherInfo.length)\n counter.writeUInt32BE(i + 1)\n counter.set(secret, 4)\n counter.set(otherInfo, 4 + secret.length)\n\n createHash(`sha${hashLength}`)\n .update(counter)\n .digest()\n .copy(output, (i * hashLength) >> 3)\n }\n\n return output.subarray(0, length >> 3)\n}\n\nfunction mapCrvToNodeEcdhCurveName(crv: Kms.KmsJwkPublicEc['crv'] | Kms.KmsJwkPublicOkp['crv']) {\n switch (crv) {\n case 'P-256':\n return 'prime256v1'\n case 'P-384':\n return 'secp384r1'\n case 'P-521':\n return 'secp521r1'\n case 'secp256k1':\n return 'secp256k1'\n case 'X25519':\n return 'x25519'\n default:\n throw new Kms.KeyManagementAlgorithmNotSupportedError(`crv '${crv}' for ECDH-ES`, 'node')\n }\n}\n\ntype ConcatKdfHashLength = ReturnType<typeof mapCrvToHashLength>\nfunction mapCrvToHashLength(crv: Kms.KmsJwkPublicEc['crv'] | Kms.KmsJwkPublicOkp['crv']) {\n switch (crv) {\n case 'secp256k1':\n case 'X25519':\n case 'P-256':\n return 256\n case 'P-384':\n return 384\n case 'P-521':\n return 512\n default:\n throw new Kms.KeyManagementAlgorithmNotSupportedError(`crv '${crv}' for ECDH-ES`, 'node')\n }\n}\n\n// TODO: might be worthwhile to add this to core?\n// TODO: we might want to have a separate definition per algorithm\n// defines things such as required key length.\nfunction mapContentEncryptionAlgorithmToKeyLength(\n encryptionAlgorithm: Kms.KnownJwaContentEncryptionAlgorithm | Kms.KnownJwaKeyEncryptionAlgorithm\n): number {\n switch (encryptionAlgorithm) {\n case 'A128CBC':\n case 'A128GCM':\n case 'A128KW':\n return 128\n case 'A192KW':\n return 192\n case 'A128CBC-HS256':\n case 'A256CBC':\n case 'A256GCM':\n case 'C20P':\n case 'XC20P':\n case 'A256KW':\n return 256\n\n case 'A192CBC-HS384':\n case 'A192GCM':\n return 384\n case 'A256CBC-HS512':\n return 512\n case 'XSALSA20-POLY1305':\n return 256\n }\n}\n"],"mappings":";;;;AAKA,MAAM,sCAAsC;CAC1C;CACA;CACA;CACA;AACF;AAEA,MAAa,sCAAsC;CACjD;CACA;CACA;CACA;AACF;AAEA,SAAS,wCACP,KACkH;CAClH,IACG,IAAI,QAAQ,SAAS,IAAI,QAAQ,YACjC,IAAI,QAAQ,QAAQ,CAAE,oCAAiD,SAAS,IAAI,GAAG,GAExF,MAAM,IAAI,IAAI,wCACZ,4BAA4B,IAAI,IAAI,aAAa,IAAI,IAAI,IACzD,MACF;AAEJ;AASA,eAAsB,oBAAoB,SAIvC;CACD,MAAM,EAAE,cAAc,YAAY,eAAe;CAEjD,wCAAwC,aAAa,iBAAiB;CACtE,wCAAwC,UAAU;CAWlD,MAAM,kBAAkB,MAAM,gBAAgB;EAC5C,WATA,aAAa,cAAc,YACvB,yCAAyC,WAAW,SAAS,IAC7D,aAAa,cAAc,mBACzB,MACA,aAAa,cAAc,mBACzB,MACA;EAIR,gBAAgB,aAAa,cAAc,YAAY,WAAW,YAAY,aAAa;EAC3F;EACA,WAAW,aAAa;EACxB,KAAK,aAAa;EAClB,KAAK,aAAa;CACpB,CAAC;CAED,IAAI,aAAa,cAAc,WAC7B,OAAO,EAEL,sBAAsB;EACpB,KAAK;EACL,GAAG,gBAAgB,SAAS,WAAW;CACzC,EACF;CAIF,MAAM,aAAa,MAAM,OAAO,UAAU,OAAO,iBAAiB,UAAU,MAAM,CAAC,SAAS,CAAC;CAC7F,MAAM,4BAA4B,gBAChC,IAAI,WAAW,yCAAyC,WAAW,SAAS,KAAK,CAAC,CACpF;CACA,MAAM,uBAAuB,MAAM,OAAO,UAAU,OAAO,2BAA2B,UAAU,MAAM,CAAC,SAAS,CAAC;CACjH,MAAM,gCAAgC,MAAM,OAAO,QAAQ,OAAO,sBAAsB,YAAY,QAAQ;CAE5G,OAAO;EACL,+BAA+B,EAC7B,WAAW,IAAI,WAAW,6BAA6B,EACzD;EACA,sBAAsB;GACpB,KAAK;GACL,GAAG,kBAAkB,YAAY,yBAAyB;EAC5D;CACF;AACF;AAEA,eAAsB,oBAAoB,SAIvC;CACD,MAAM,EAAE,cAAc,YAAY,eAAe;CAEjD,wCAAwC,aAAa,iBAAiB;CACtE,wCAAwC,UAAU;CAWlD,MAAM,kBAAkB,MAAM,gBAAgB;EAC5C,WATA,aAAa,cAAc,YACvB,yCAAyC,WAAW,SAAS,IAC7D,aAAa,cAAc,mBACzB,MACA,aAAa,cAAc,mBACzB,MACA;EAIR,gBAAgB,aAAa,cAAc,YAAY,WAAW,YAAY,aAAa;EAC/E;EACZ,WAAW,aAAa;EACxB,KAAK,aAAa;EAClB,KAAK,aAAa;CACpB,CAAC;CAED,IAAI,aAAa,cAAc,WAC7B,OAAO,EAEL,sBAAsB;EACpB,KAAK;EACL,GAAG,gBAAgB,SAAS,WAAW;CACzC,EACF;CAIF,MAAM,aAAa,MAAM,OAAO,UAAU,OAAO,iBAAiB,UAAU,MAAM,CAAC,SAAS,CAAC;CAE7F,MAAM,uBAAuB,MAAM,OAAO,UACxC,OACA,aAAa,aAAa,WAC1B,YACA,UAEA;EAAE,MAAM;EAAW,MAAM;CAAO,GAChC,MACA,CAAC,SAAS,CACZ;CAEA,OAAO,EACL,sBAAuB,MAAM,OAAO,UAAU,OAAO,oBAAoB,EAC3E;AACF;;;;AAKA,eAAe,gBAAgB,SAUX;CAKlB,MAAM,oBAAoB,0BAA0B,QAAQ,WAAW,GAAG;CAC1E,MAAM,oBAAoB,mBAAmB,QAAQ,UAAU,GAAG;CAElE,MAAM,OAAO,WAAW,iBAAiB;CAGzC,KAAK,cAAc,kBAAkB,cAAc,QAAQ,WAAW,CAAC,CAAC;CAExE,MAAM,YAAY,IAAI,UAAU,cAAc,QAAQ,SAAS,CAAC,CAAC;CACjE,IAAI,UAAU,QAAQ,OACpB,MAAM,IAAI,IAAI,mBAAmB,2CAA2C;CAI9E,MAAM,eAAe,KAAK,cAAc,UAAU,SAAS;CAG3D,MAAM,gBAAgB,kBAAkB,eAAe,QAAQ,cAAc;CAC7E,MAAM,cAAc,kBAAkB,OAAO,CAC3C,wBAAwB,cAAc,MAAM,GAC5C,aACF,CAAC;CAGD,MAAM,MAAM,QAAQ,OAAO,OAAO,MAAM,CAAC;CACzC,MAAM,aAAa,OAAO,OAAO,CAC/B,wBAAwB,IAAI,MAAM,GAClC,GACF,CAAC;CAGD,MAAM,MAAM,QAAQ,OAAO,OAAO,MAAM,CAAC;CACzC,MAAM,aAAa,OAAO,OAAO,CAC/B,wBAAwB,IAAI,MAAM,GAClC,GACF,CAAC;CAGD,MAAM,YAAY,OAAO,OAAO;EAC9B;EACA;EACA;EACA,wBAAwB,QAAQ,SAAS;EACzC,OAAO,MAAM,CAAC;CAChB,CAAC;CAGD,OAAO,UAAU,cAAc,QAAQ,WAAW,mBAAmB,SAAS;AAChF;AAEA,SAAS,wBAAwB,QAAgB;CAC/C,MAAM,yBAAS,IAAI,YAAY,CAAC;CAEhC,IADiB,SAAS,MACvB,CAAC,CAAC,UAAU,GAAG,MAAM;CACxB,OAAO,IAAI,WAAW,MAAM;AAC9B;;;;AAKA,SAAS,UAAU,QAAgB,QAAgB,YAAiC,WAA2B;CAC7G,MAAM,OAAO,KAAK,MAAM,UAAU,MAAM,cAAc,EAAE;CACxD,MAAM,SAAS,OAAO,MAAM,QAAQ,cAAc,EAAE;CAEpD,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,KAAK;EAC7B,MAAM,UAAU,OAAO,MAAM,IAAI,OAAO,SAAS,UAAU,MAAM;EACjE,QAAQ,cAAc,IAAI,CAAC;EAC3B,QAAQ,IAAI,QAAQ,CAAC;EACrB,QAAQ,IAAI,WAAW,IAAI,OAAO,MAAM;EAExC,WAAW,MAAM,YAAY,CAAC,CAC3B,OAAO,OAAO,CAAC,CACf,OAAO,CAAC,CACR,KAAK,QAAS,IAAI,cAAe,CAAC;CACvC;CAEA,OAAO,OAAO,SAAS,GAAG,UAAU,CAAC;AACvC;AAEA,SAAS,0BAA0B,KAA6D;CAC9F,QAAQ,KAAR;EACE,KAAK,SACH,OAAO;EACT,KAAK,SACH,OAAO;EACT,KAAK,SACH,OAAO;EACT,KAAK,aACH,OAAO;EACT,KAAK,UACH,OAAO;EACT,SACE,MAAM,IAAI,IAAI,wCAAwC,QAAQ,IAAI,gBAAgB,MAAM;CAC5F;AACF;AAGA,SAAS,mBAAmB,KAA6D;CACvF,QAAQ,KAAR;EACE,KAAK;EACL,KAAK;EACL,KAAK,SACH,OAAO;EACT,KAAK,SACH,OAAO;EACT,KAAK,SACH,OAAO;EACT,SACE,MAAM,IAAI,IAAI,wCAAwC,QAAQ,IAAI,gBAAgB,MAAM;CAC5F;AACF;AAKA,SAAS,yCACP,qBACQ;CACR,QAAQ,qBAAR;EACE,KAAK;EACL,KAAK;EACL,KAAK,UACH,OAAO;EACT,KAAK,UACH,OAAO;EACT,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,UACH,OAAO;EAET,KAAK;EACL,KAAK,WACH,OAAO;EACT,KAAK,iBACH,OAAO;EACT,KAAK,qBACH,OAAO;CACX;AACF"}
|
|
@@ -2,7 +2,6 @@ import { performSign } from "./sign.mjs";
|
|
|
2
2
|
import { Kms, TypedArrayEncoder } from "@credo-ts/core";
|
|
3
3
|
import { createCipheriv, createSecretKey, randomBytes } from "node:crypto";
|
|
4
4
|
import { Buffer } from "node:buffer";
|
|
5
|
-
|
|
6
5
|
//#region src/kms/crypto/encrypt.ts
|
|
7
6
|
const nodeSupportedEncryptionAlgorithms = [
|
|
8
7
|
"A128CBC",
|
|
@@ -91,7 +90,7 @@ async function performEncrypt(key, dataEncryption, data) {
|
|
|
91
90
|
}
|
|
92
91
|
throw new Kms.KeyManagementAlgorithmNotSupportedError(`JWA content encryption algorithm '${dataEncryption.algorithm}'`, "node");
|
|
93
92
|
}
|
|
94
|
-
|
|
95
93
|
//#endregion
|
|
96
94
|
export { nodeSupportedEncryptionAlgorithms, performEncrypt };
|
|
95
|
+
|
|
97
96
|
//# sourceMappingURL=encrypt.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encrypt.mjs","names":[],"sources":["../../../src/kms/crypto/encrypt.ts"],"sourcesContent":["import { Buffer } from 'node:buffer'\nimport type { CipherGCM } from 'node:crypto'\nimport { createCipheriv, createSecretKey, randomBytes } from 'node:crypto'\nimport { Kms, TypedArrayEncoder } from '@credo-ts/core'\nimport { performSign } from './sign'\n\nexport const nodeSupportedEncryptionAlgorithms = [\n 'A128CBC',\n 'A256CBC',\n 'A128CBC-HS256',\n 'A192CBC-HS384',\n 'A256CBC-HS512',\n 'A128GCM',\n 'A192GCM',\n 'A256GCM',\n 'C20P',\n] as const satisfies Kms.KnownJwaContentEncryptionAlgorithm[]\n\nexport async function performEncrypt(\n key: Kms.KmsJwkPrivateOct,\n dataEncryption: Kms.KmsEncryptDataContentEncryption,\n data: Uint8Array\n): Promise<{ encrypted: Uint8Array; tag?: Uint8Array; iv: Uint8Array }> {\n const secretKeyBytes = TypedArrayEncoder.fromBase64Url(key.k)\n const nodeKey = createSecretKey(secretKeyBytes)\n\n // Create cipher with key and IV\n if (dataEncryption.algorithm === 'A128CBC' || dataEncryption.algorithm === 'A256CBC') {\n const nodeAlgorithm = dataEncryption.algorithm === 'A128CBC' ? 'aes-128-cbc' : 'aes-256-cbc'\n\n // IV should be exactly 16 bytes (128 bits) for CBC mode\n const iv = dataEncryption.iv ?? randomBytes(16)\n\n const cipher = createCipheriv(nodeAlgorithm, nodeKey, iv)\n\n // Get encrypted data\n const encrypted = Buffer.concat([cipher.update(data), cipher.final()])\n\n return { encrypted, iv }\n }\n if (\n dataEncryption.algorithm === 'A128CBC-HS256' ||\n dataEncryption.algorithm === 'A192CBC-HS384' ||\n dataEncryption.algorithm === 'A256CBC-HS512'\n ) {\n // Map algorithms to their corresponding CBC and HMAC settings\n const algSettings = {\n 'A128CBC-HS256': { cbcAlg: 'aes-128-cbc', hmacAlg: 'HS256', keySize: 16 } as const,\n 'A192CBC-HS384': { cbcAlg: 'aes-192-cbc', hmacAlg: 'HS384', keySize: 24 } as const,\n 'A256CBC-HS512': { cbcAlg: 'aes-256-cbc', hmacAlg: 'HS512', keySize: 32 } as const,\n }[dataEncryption.algorithm]\n\n // IV should be exactly 16 bytes (128 bits) for CBC mode\n const iv = dataEncryption.iv ?? randomBytes(16)\n\n // Split the input key into MAC and ENC keys (MAC key is first half, ENC key is second half)\n const macKey = secretKeyBytes.subarray(0, algSettings.keySize)\n const encKey = createSecretKey(secretKeyBytes.subarray(algSettings.keySize))\n\n // Perform encryption\n const cipher = createCipheriv(algSettings.cbcAlg, encKey, iv)\n const encrypted = Buffer.concat([cipher.update(data), cipher.final()])\n\n // Calculate authentication tag\n // AL (Associated Length) is 64-bit big-endian length of AAD in bits\n const al = Buffer.alloc(8)\n const aadLength = dataEncryption.aad ? dataEncryption.aad.length * 8 : 0\n al.writeBigUInt64BE(BigInt(aadLength))\n\n // Create concatenated buffer for MAC calculation\n const macData = Buffer.concat([\n // If AAD exists, include it first, otherwise empty buffer\n dataEncryption.aad ?? Buffer.alloc(0),\n iv, // Initial Vector\n encrypted, // Ciphertext\n al, // Associated Length (AL)\n ])\n\n const hmac = await performSign(\n { kty: 'oct', k: TypedArrayEncoder.toBase64Url(macKey) },\n algSettings.hmacAlg,\n macData\n )\n const tag = hmac.subarray(0, algSettings.keySize) // Truncate to appropriate size\n\n return { encrypted, tag, iv }\n }\n if (\n dataEncryption.algorithm === 'A128GCM' ||\n dataEncryption.algorithm === 'A192GCM' ||\n dataEncryption.algorithm === 'A256GCM'\n ) {\n const nodeAlgorithm =\n dataEncryption.algorithm === 'A128GCM'\n ? 'aes-128-gcm'\n : dataEncryption.algorithm === 'A192GCM'\n ? 'aes-192-gcm'\n : 'aes-256-gcm'\n\n // IV should be exactly 12 bytes (96 bits) for GCM\n const iv = dataEncryption.iv ?? randomBytes(12)\n\n const cipher = createCipheriv(nodeAlgorithm, nodeKey, iv)\n\n // If AAD is provided, update the cipher with it before encryption\n if (dataEncryption.aad) {\n cipher.setAAD(dataEncryption.aad)\n }\n\n // Get encrypted data\n const encrypted = Buffer.concat([cipher.update(data), cipher.final()])\n\n // Get auth tag - must be saved to verify decryption\n const tag = cipher.getAuthTag() as Uint8Array\n\n return {\n encrypted,\n tag,\n iv,\n }\n }\n if (dataEncryption.algorithm === 'C20P') {\n // IV should be exactly 12 bytes (96 bits) for C20P\n const iv = dataEncryption.iv ?? randomBytes(12)\n\n const cipher: CipherGCM = createCipheriv('chacha20-poly1305', nodeKey, iv, {\n authTagLength: 16,\n })\n\n // If AAD is provided, update the cipher with it before encryption\n if (dataEncryption.aad) {\n cipher.setAAD(dataEncryption.aad)\n }\n\n // Get encrypted data\n const encrypted = Buffer.concat([cipher.update(data), cipher.final()])\n\n // Get auth tag - must be saved to verify decryption\n const tag = cipher.getAuthTag() as Uint8Array\n\n return {\n encrypted,\n tag,\n iv,\n }\n }\n\n throw new Kms.KeyManagementAlgorithmNotSupportedError(\n `JWA content encryption algorithm '${dataEncryption.algorithm}'`,\n 'node'\n )\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"encrypt.mjs","names":[],"sources":["../../../src/kms/crypto/encrypt.ts"],"sourcesContent":["import { Buffer } from 'node:buffer'\nimport type { CipherGCM } from 'node:crypto'\nimport { createCipheriv, createSecretKey, randomBytes } from 'node:crypto'\nimport { Kms, TypedArrayEncoder } from '@credo-ts/core'\nimport { performSign } from './sign'\n\nexport const nodeSupportedEncryptionAlgorithms = [\n 'A128CBC',\n 'A256CBC',\n 'A128CBC-HS256',\n 'A192CBC-HS384',\n 'A256CBC-HS512',\n 'A128GCM',\n 'A192GCM',\n 'A256GCM',\n 'C20P',\n] as const satisfies Kms.KnownJwaContentEncryptionAlgorithm[]\n\nexport async function performEncrypt(\n key: Kms.KmsJwkPrivateOct,\n dataEncryption: Kms.KmsEncryptDataContentEncryption,\n data: Uint8Array\n): Promise<{ encrypted: Uint8Array; tag?: Uint8Array; iv: Uint8Array }> {\n const secretKeyBytes = TypedArrayEncoder.fromBase64Url(key.k)\n const nodeKey = createSecretKey(secretKeyBytes)\n\n // Create cipher with key and IV\n if (dataEncryption.algorithm === 'A128CBC' || dataEncryption.algorithm === 'A256CBC') {\n const nodeAlgorithm = dataEncryption.algorithm === 'A128CBC' ? 'aes-128-cbc' : 'aes-256-cbc'\n\n // IV should be exactly 16 bytes (128 bits) for CBC mode\n const iv = dataEncryption.iv ?? randomBytes(16)\n\n const cipher = createCipheriv(nodeAlgorithm, nodeKey, iv)\n\n // Get encrypted data\n const encrypted = Buffer.concat([cipher.update(data), cipher.final()])\n\n return { encrypted, iv }\n }\n if (\n dataEncryption.algorithm === 'A128CBC-HS256' ||\n dataEncryption.algorithm === 'A192CBC-HS384' ||\n dataEncryption.algorithm === 'A256CBC-HS512'\n ) {\n // Map algorithms to their corresponding CBC and HMAC settings\n const algSettings = {\n 'A128CBC-HS256': { cbcAlg: 'aes-128-cbc', hmacAlg: 'HS256', keySize: 16 } as const,\n 'A192CBC-HS384': { cbcAlg: 'aes-192-cbc', hmacAlg: 'HS384', keySize: 24 } as const,\n 'A256CBC-HS512': { cbcAlg: 'aes-256-cbc', hmacAlg: 'HS512', keySize: 32 } as const,\n }[dataEncryption.algorithm]\n\n // IV should be exactly 16 bytes (128 bits) for CBC mode\n const iv = dataEncryption.iv ?? randomBytes(16)\n\n // Split the input key into MAC and ENC keys (MAC key is first half, ENC key is second half)\n const macKey = secretKeyBytes.subarray(0, algSettings.keySize)\n const encKey = createSecretKey(secretKeyBytes.subarray(algSettings.keySize))\n\n // Perform encryption\n const cipher = createCipheriv(algSettings.cbcAlg, encKey, iv)\n const encrypted = Buffer.concat([cipher.update(data), cipher.final()])\n\n // Calculate authentication tag\n // AL (Associated Length) is 64-bit big-endian length of AAD in bits\n const al = Buffer.alloc(8)\n const aadLength = dataEncryption.aad ? dataEncryption.aad.length * 8 : 0\n al.writeBigUInt64BE(BigInt(aadLength))\n\n // Create concatenated buffer for MAC calculation\n const macData = Buffer.concat([\n // If AAD exists, include it first, otherwise empty buffer\n dataEncryption.aad ?? Buffer.alloc(0),\n iv, // Initial Vector\n encrypted, // Ciphertext\n al, // Associated Length (AL)\n ])\n\n const hmac = await performSign(\n { kty: 'oct', k: TypedArrayEncoder.toBase64Url(macKey) },\n algSettings.hmacAlg,\n macData\n )\n const tag = hmac.subarray(0, algSettings.keySize) // Truncate to appropriate size\n\n return { encrypted, tag, iv }\n }\n if (\n dataEncryption.algorithm === 'A128GCM' ||\n dataEncryption.algorithm === 'A192GCM' ||\n dataEncryption.algorithm === 'A256GCM'\n ) {\n const nodeAlgorithm =\n dataEncryption.algorithm === 'A128GCM'\n ? 'aes-128-gcm'\n : dataEncryption.algorithm === 'A192GCM'\n ? 'aes-192-gcm'\n : 'aes-256-gcm'\n\n // IV should be exactly 12 bytes (96 bits) for GCM\n const iv = dataEncryption.iv ?? randomBytes(12)\n\n const cipher = createCipheriv(nodeAlgorithm, nodeKey, iv)\n\n // If AAD is provided, update the cipher with it before encryption\n if (dataEncryption.aad) {\n cipher.setAAD(dataEncryption.aad)\n }\n\n // Get encrypted data\n const encrypted = Buffer.concat([cipher.update(data), cipher.final()])\n\n // Get auth tag - must be saved to verify decryption\n const tag = cipher.getAuthTag() as Uint8Array\n\n return {\n encrypted,\n tag,\n iv,\n }\n }\n if (dataEncryption.algorithm === 'C20P') {\n // IV should be exactly 12 bytes (96 bits) for C20P\n const iv = dataEncryption.iv ?? randomBytes(12)\n\n const cipher: CipherGCM = createCipheriv('chacha20-poly1305', nodeKey, iv, {\n authTagLength: 16,\n })\n\n // If AAD is provided, update the cipher with it before encryption\n if (dataEncryption.aad) {\n cipher.setAAD(dataEncryption.aad)\n }\n\n // Get encrypted data\n const encrypted = Buffer.concat([cipher.update(data), cipher.final()])\n\n // Get auth tag - must be saved to verify decryption\n const tag = cipher.getAuthTag() as Uint8Array\n\n return {\n encrypted,\n tag,\n iv,\n }\n }\n\n throw new Kms.KeyManagementAlgorithmNotSupportedError(\n `JWA content encryption algorithm '${dataEncryption.algorithm}'`,\n 'node'\n )\n}\n"],"mappings":";;;;;AAMA,MAAa,oCAAoC;CAC/C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,eAAsB,eACpB,KACA,gBACA,MACsE;CACtE,MAAM,iBAAiB,kBAAkB,cAAc,IAAI,CAAC;CAC5D,MAAM,UAAU,gBAAgB,cAAc;CAG9C,IAAI,eAAe,cAAc,aAAa,eAAe,cAAc,WAAW;EACpF,MAAM,gBAAgB,eAAe,cAAc,YAAY,gBAAgB;EAG/E,MAAM,KAAK,eAAe,MAAM,YAAY,EAAE;EAE9C,MAAM,SAAS,eAAe,eAAe,SAAS,EAAE;EAKxD,OAAO;GAAE,WAFS,OAAO,OAAO,CAAC,OAAO,OAAO,IAAI,GAAG,OAAO,MAAM,CAAC,CAEnD;GAAG;EAAG;CACzB;CACA,IACE,eAAe,cAAc,mBAC7B,eAAe,cAAc,mBAC7B,eAAe,cAAc,iBAC7B;EAEA,MAAM,cAAc;GAClB,iBAAiB;IAAE,QAAQ;IAAe,SAAS;IAAS,SAAS;GAAG;GACxE,iBAAiB;IAAE,QAAQ;IAAe,SAAS;IAAS,SAAS;GAAG;GACxE,iBAAiB;IAAE,QAAQ;IAAe,SAAS;IAAS,SAAS;GAAG;EAC1E,EAAE,eAAe;EAGjB,MAAM,KAAK,eAAe,MAAM,YAAY,EAAE;EAG9C,MAAM,SAAS,eAAe,SAAS,GAAG,YAAY,OAAO;EAC7D,MAAM,SAAS,gBAAgB,eAAe,SAAS,YAAY,OAAO,CAAC;EAG3E,MAAM,SAAS,eAAe,YAAY,QAAQ,QAAQ,EAAE;EAC5D,MAAM,YAAY,OAAO,OAAO,CAAC,OAAO,OAAO,IAAI,GAAG,OAAO,MAAM,CAAC,CAAC;EAIrE,MAAM,KAAK,OAAO,MAAM,CAAC;EACzB,MAAM,YAAY,eAAe,MAAM,eAAe,IAAI,SAAS,IAAI;EACvE,GAAG,iBAAiB,OAAO,SAAS,CAAC;EAGrC,MAAM,UAAU,OAAO,OAAO;GAE5B,eAAe,OAAO,OAAO,MAAM,CAAC;GACpC;GACA;GACA;EACF,CAAC;EASD,OAAO;GAAE;GAAW,MAFR,MALO,YACjB;IAAE,KAAK;IAAO,GAAG,kBAAkB,YAAY,MAAM;GAAE,GACvD,YAAY,SACZ,OACF,EAAA,CACiB,SAAS,GAAG,YAAY,OAEnB;GAAG;EAAG;CAC9B;CACA,IACE,eAAe,cAAc,aAC7B,eAAe,cAAc,aAC7B,eAAe,cAAc,WAC7B;EACA,MAAM,gBACJ,eAAe,cAAc,YACzB,gBACA,eAAe,cAAc,YAC3B,gBACA;EAGR,MAAM,KAAK,eAAe,MAAM,YAAY,EAAE;EAE9C,MAAM,SAAS,eAAe,eAAe,SAAS,EAAE;EAGxD,IAAI,eAAe,KACjB,OAAO,OAAO,eAAe,GAAG;EASlC,OAAO;GACL,WANgB,OAAO,OAAO,CAAC,OAAO,OAAO,IAAI,GAAG,OAAO,MAAM,CAAC,CAM1D;GACR,KAJU,OAAO,WAIf;GACF;EACF;CACF;CACA,IAAI,eAAe,cAAc,QAAQ;EAEvC,MAAM,KAAK,eAAe,MAAM,YAAY,EAAE;EAE9C,MAAM,SAAoB,eAAe,qBAAqB,SAAS,IAAI,EACzE,eAAe,GACjB,CAAC;EAGD,IAAI,eAAe,KACjB,OAAO,OAAO,eAAe,GAAG;EASlC,OAAO;GACL,WANgB,OAAO,OAAO,CAAC,OAAO,OAAO,IAAI,GAAG,OAAO,MAAM,CAAC,CAM1D;GACR,KAJU,OAAO,WAIf;GACF;EACF;CACF;CAEA,MAAM,IAAI,IAAI,wCACZ,qCAAqC,eAAe,UAAU,IAC9D,MACF;AACF"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Kms, TypedArrayEncoder } from "@credo-ts/core";
|
|
2
2
|
import { AEAD_AES_128_GCM, AEAD_AES_256_GCM, CipherSuite, KDF_HKDF_SHA256, KEM_DHKEM_P256_HKDF_SHA256, KEM_DHKEM_X25519_HKDF_SHA256 } from "hpke";
|
|
3
|
-
|
|
4
3
|
//#region src/kms/crypto/hpke.ts
|
|
5
4
|
const nodeSupportedHpkeAlgorithms = [
|
|
6
5
|
"HPKE-0",
|
|
@@ -77,7 +76,7 @@ async function hpkeOpen(options) {
|
|
|
77
76
|
aad: options.aad
|
|
78
77
|
});
|
|
79
78
|
}
|
|
80
|
-
|
|
81
79
|
//#endregion
|
|
82
80
|
export { hpkeOpen, hpkeSeal, nodeSupportedHpkeAlgorithms };
|
|
81
|
+
|
|
83
82
|
//# sourceMappingURL=hpke.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hpke.mjs","names":[],"sources":["../../../src/kms/crypto/hpke.ts"],"sourcesContent":["import { Kms, TypedArrayEncoder } from '@credo-ts/core'\nimport {\n AEAD_AES_128_GCM,\n AEAD_AES_256_GCM,\n type AEADFactory,\n CipherSuite,\n KDF_HKDF_SHA256,\n type KDFFactory,\n KEM_DHKEM_P256_HKDF_SHA256,\n KEM_DHKEM_X25519_HKDF_SHA256,\n type KEMFactory,\n} from 'hpke'\n\nexport const nodeSupportedHpkeAlgorithms = ['HPKE-0', 'HPKE-3', 'HPKE-7'] satisfies Kms.KnownJwaHpkeAlgorithm[]\n\nexport type NodeSupportedHpkeAlgorithm = (typeof nodeSupportedHpkeAlgorithms)[number]\n\ntype HpkeSuiteDefinition = {\n kem: KEMFactory\n kdf: KDFFactory\n aead: AEADFactory\n /**\n * The curve the KEM is defined over. HPKE binds the KEM to the suite, so a key on another curve\n * can never be used with this algorithm.\n */\n crv: 'P-256' | 'X25519'\n}\n\nconst hpkeSuites = {\n 'HPKE-0': {\n kem: KEM_DHKEM_P256_HKDF_SHA256,\n kdf: KDF_HKDF_SHA256,\n aead: AEAD_AES_128_GCM,\n crv: 'P-256',\n },\n 'HPKE-3': {\n kem: KEM_DHKEM_X25519_HKDF_SHA256,\n kdf: KDF_HKDF_SHA256,\n aead: AEAD_AES_128_GCM,\n crv: 'X25519',\n },\n 'HPKE-7': {\n kem: KEM_DHKEM_P256_HKDF_SHA256,\n kdf: KDF_HKDF_SHA256,\n aead: AEAD_AES_256_GCM,\n crv: 'P-256',\n },\n} as const satisfies Record<NodeSupportedHpkeAlgorithm, HpkeSuiteDefinition>\n\nfunction getHpkeCipherSuite(algorithm: string) {\n const suite = hpkeSuites[algorithm as NodeSupportedHpkeAlgorithm]\n if (!suite) {\n throw new Kms.KeyManagementAlgorithmNotSupportedError(`JWA key agreement algorithm '${algorithm}'`, 'node')\n }\n\n return { suite, cipherSuite: new CipherSuite(suite.kem, suite.kdf, suite.aead) }\n}\n\n/**\n * Serialize a public JWK to the raw form the KEM expects. `PublicJwk` already encodes EC keys as\n * the uncompressed SEC1 point required by the DHKEM(P-256) suites, and X25519 keys as the raw 32\n * bytes required by DHKEM(X25519).\n */\nfunction serializePublicKey(jwk: Kms.KmsJwkPublic, suite: HpkeSuiteDefinition) {\n const publicJwk = Kms.PublicJwk.fromUnknown(jwk)\n\n if (suite.crv === 'X25519') {\n if (!publicJwk.is(Kms.X25519PublicJwk)) {\n throw new Kms.KeyManagementError(`HPKE suite requires an OKP key with crv 'X25519'`)\n }\n return publicJwk.publicKey.publicKey\n }\n\n if (!publicJwk.is(Kms.P256PublicJwk)) {\n throw new Kms.KeyManagementError(`HPKE suite requires an EC key with crv 'P-256'`)\n }\n\n return publicJwk.publicKey.publicKey\n}\n\nfunction serializePrivateKey(jwk: Kms.KmsJwkPrivate, suite: HpkeSuiteDefinition) {\n if (suite.crv === 'X25519') {\n if (jwk.kty !== 'OKP' || jwk.crv !== 'X25519') {\n throw new Kms.KeyManagementError(`HPKE suite requires an OKP key with crv 'X25519'`)\n }\n return TypedArrayEncoder.fromBase64Url(jwk.d)\n }\n\n if (jwk.kty !== 'EC' || jwk.crv !== 'P-256') {\n throw new Kms.KeyManagementError(`HPKE suite requires an EC key with crv 'P-256'`)\n }\n\n return TypedArrayEncoder.fromBase64Url(jwk.d)\n}\n\nexport async function hpkeSeal(options: {\n algorithm: string\n recipientPublicJwk: Kms.KmsJwkPublic\n data: Uint8Array\n info?: Uint8Array\n aad?: Uint8Array\n}): Promise<{ encrypted: Uint8Array; encapsulatedKey: Uint8Array }> {\n const { suite, cipherSuite } = getHpkeCipherSuite(options.algorithm)\n\n const recipientPublicKey = await cipherSuite.DeserializePublicKey(\n serializePublicKey(options.recipientPublicJwk, suite)\n )\n\n const { encapsulatedSecret, ciphertext } = await cipherSuite.Seal(recipientPublicKey, options.data, {\n info: options.info,\n aad: options.aad,\n })\n\n return { encrypted: ciphertext, encapsulatedKey: encapsulatedSecret }\n}\n\nexport async function hpkeOpen(options: {\n algorithm: string\n recipientPrivateJwk: Kms.KmsJwkPrivate\n encapsulatedKey: Uint8Array\n encrypted: Uint8Array\n info?: Uint8Array\n aad?: Uint8Array\n}): Promise<Uint8Array> {\n const { suite, cipherSuite } = getHpkeCipherSuite(options.algorithm)\n\n // The key is imported as extractable, as not all runtimes can derive the public key from a\n // non-extractable private key (`SubtleCrypto.getPublicKey` is only available in newer runtimes).\n // The private key material is already available to us here, so this does not expose anything new.\n const recipientPrivateKey = await cipherSuite.DeserializePrivateKey(\n serializePrivateKey(options.recipientPrivateJwk, suite),\n true\n )\n\n return await cipherSuite.Open(recipientPrivateKey, options.encapsulatedKey, options.encrypted, {\n info: options.info,\n aad: options.aad,\n })\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"hpke.mjs","names":[],"sources":["../../../src/kms/crypto/hpke.ts"],"sourcesContent":["import { Kms, TypedArrayEncoder } from '@credo-ts/core'\nimport {\n AEAD_AES_128_GCM,\n AEAD_AES_256_GCM,\n type AEADFactory,\n CipherSuite,\n KDF_HKDF_SHA256,\n type KDFFactory,\n KEM_DHKEM_P256_HKDF_SHA256,\n KEM_DHKEM_X25519_HKDF_SHA256,\n type KEMFactory,\n} from 'hpke'\n\nexport const nodeSupportedHpkeAlgorithms = ['HPKE-0', 'HPKE-3', 'HPKE-7'] satisfies Kms.KnownJwaHpkeAlgorithm[]\n\nexport type NodeSupportedHpkeAlgorithm = (typeof nodeSupportedHpkeAlgorithms)[number]\n\ntype HpkeSuiteDefinition = {\n kem: KEMFactory\n kdf: KDFFactory\n aead: AEADFactory\n /**\n * The curve the KEM is defined over. HPKE binds the KEM to the suite, so a key on another curve\n * can never be used with this algorithm.\n */\n crv: 'P-256' | 'X25519'\n}\n\nconst hpkeSuites = {\n 'HPKE-0': {\n kem: KEM_DHKEM_P256_HKDF_SHA256,\n kdf: KDF_HKDF_SHA256,\n aead: AEAD_AES_128_GCM,\n crv: 'P-256',\n },\n 'HPKE-3': {\n kem: KEM_DHKEM_X25519_HKDF_SHA256,\n kdf: KDF_HKDF_SHA256,\n aead: AEAD_AES_128_GCM,\n crv: 'X25519',\n },\n 'HPKE-7': {\n kem: KEM_DHKEM_P256_HKDF_SHA256,\n kdf: KDF_HKDF_SHA256,\n aead: AEAD_AES_256_GCM,\n crv: 'P-256',\n },\n} as const satisfies Record<NodeSupportedHpkeAlgorithm, HpkeSuiteDefinition>\n\nfunction getHpkeCipherSuite(algorithm: string) {\n const suite = hpkeSuites[algorithm as NodeSupportedHpkeAlgorithm]\n if (!suite) {\n throw new Kms.KeyManagementAlgorithmNotSupportedError(`JWA key agreement algorithm '${algorithm}'`, 'node')\n }\n\n return { suite, cipherSuite: new CipherSuite(suite.kem, suite.kdf, suite.aead) }\n}\n\n/**\n * Serialize a public JWK to the raw form the KEM expects. `PublicJwk` already encodes EC keys as\n * the uncompressed SEC1 point required by the DHKEM(P-256) suites, and X25519 keys as the raw 32\n * bytes required by DHKEM(X25519).\n */\nfunction serializePublicKey(jwk: Kms.KmsJwkPublic, suite: HpkeSuiteDefinition) {\n const publicJwk = Kms.PublicJwk.fromUnknown(jwk)\n\n if (suite.crv === 'X25519') {\n if (!publicJwk.is(Kms.X25519PublicJwk)) {\n throw new Kms.KeyManagementError(`HPKE suite requires an OKP key with crv 'X25519'`)\n }\n return publicJwk.publicKey.publicKey\n }\n\n if (!publicJwk.is(Kms.P256PublicJwk)) {\n throw new Kms.KeyManagementError(`HPKE suite requires an EC key with crv 'P-256'`)\n }\n\n return publicJwk.publicKey.publicKey\n}\n\nfunction serializePrivateKey(jwk: Kms.KmsJwkPrivate, suite: HpkeSuiteDefinition) {\n if (suite.crv === 'X25519') {\n if (jwk.kty !== 'OKP' || jwk.crv !== 'X25519') {\n throw new Kms.KeyManagementError(`HPKE suite requires an OKP key with crv 'X25519'`)\n }\n return TypedArrayEncoder.fromBase64Url(jwk.d)\n }\n\n if (jwk.kty !== 'EC' || jwk.crv !== 'P-256') {\n throw new Kms.KeyManagementError(`HPKE suite requires an EC key with crv 'P-256'`)\n }\n\n return TypedArrayEncoder.fromBase64Url(jwk.d)\n}\n\nexport async function hpkeSeal(options: {\n algorithm: string\n recipientPublicJwk: Kms.KmsJwkPublic\n data: Uint8Array\n info?: Uint8Array\n aad?: Uint8Array\n}): Promise<{ encrypted: Uint8Array; encapsulatedKey: Uint8Array }> {\n const { suite, cipherSuite } = getHpkeCipherSuite(options.algorithm)\n\n const recipientPublicKey = await cipherSuite.DeserializePublicKey(\n serializePublicKey(options.recipientPublicJwk, suite)\n )\n\n const { encapsulatedSecret, ciphertext } = await cipherSuite.Seal(recipientPublicKey, options.data, {\n info: options.info,\n aad: options.aad,\n })\n\n return { encrypted: ciphertext, encapsulatedKey: encapsulatedSecret }\n}\n\nexport async function hpkeOpen(options: {\n algorithm: string\n recipientPrivateJwk: Kms.KmsJwkPrivate\n encapsulatedKey: Uint8Array\n encrypted: Uint8Array\n info?: Uint8Array\n aad?: Uint8Array\n}): Promise<Uint8Array> {\n const { suite, cipherSuite } = getHpkeCipherSuite(options.algorithm)\n\n // The key is imported as extractable, as not all runtimes can derive the public key from a\n // non-extractable private key (`SubtleCrypto.getPublicKey` is only available in newer runtimes).\n // The private key material is already available to us here, so this does not expose anything new.\n const recipientPrivateKey = await cipherSuite.DeserializePrivateKey(\n serializePrivateKey(options.recipientPrivateJwk, suite),\n true\n )\n\n return await cipherSuite.Open(recipientPrivateKey, options.encapsulatedKey, options.encrypted, {\n info: options.info,\n aad: options.aad,\n })\n}\n"],"mappings":";;;AAaA,MAAa,8BAA8B;CAAC;CAAU;CAAU;AAAQ;AAexE,MAAM,aAAa;CACjB,UAAU;EACR,KAAK;EACL,KAAK;EACL,MAAM;EACN,KAAK;CACP;CACA,UAAU;EACR,KAAK;EACL,KAAK;EACL,MAAM;EACN,KAAK;CACP;CACA,UAAU;EACR,KAAK;EACL,KAAK;EACL,MAAM;EACN,KAAK;CACP;AACF;AAEA,SAAS,mBAAmB,WAAmB;CAC7C,MAAM,QAAQ,WAAW;CACzB,IAAI,CAAC,OACH,MAAM,IAAI,IAAI,wCAAwC,gCAAgC,UAAU,IAAI,MAAM;CAG5G,OAAO;EAAE;EAAO,aAAa,IAAI,YAAY,MAAM,KAAK,MAAM,KAAK,MAAM,IAAI;CAAE;AACjF;;;;;;AAOA,SAAS,mBAAmB,KAAuB,OAA4B;CAC7E,MAAM,YAAY,IAAI,UAAU,YAAY,GAAG;CAE/C,IAAI,MAAM,QAAQ,UAAU;EAC1B,IAAI,CAAC,UAAU,GAAG,IAAI,eAAe,GACnC,MAAM,IAAI,IAAI,mBAAmB,kDAAkD;EAErF,OAAO,UAAU,UAAU;CAC7B;CAEA,IAAI,CAAC,UAAU,GAAG,IAAI,aAAa,GACjC,MAAM,IAAI,IAAI,mBAAmB,gDAAgD;CAGnF,OAAO,UAAU,UAAU;AAC7B;AAEA,SAAS,oBAAoB,KAAwB,OAA4B;CAC/E,IAAI,MAAM,QAAQ,UAAU;EAC1B,IAAI,IAAI,QAAQ,SAAS,IAAI,QAAQ,UACnC,MAAM,IAAI,IAAI,mBAAmB,kDAAkD;EAErF,OAAO,kBAAkB,cAAc,IAAI,CAAC;CAC9C;CAEA,IAAI,IAAI,QAAQ,QAAQ,IAAI,QAAQ,SAClC,MAAM,IAAI,IAAI,mBAAmB,gDAAgD;CAGnF,OAAO,kBAAkB,cAAc,IAAI,CAAC;AAC9C;AAEA,eAAsB,SAAS,SAMqC;CAClE,MAAM,EAAE,OAAO,gBAAgB,mBAAmB,QAAQ,SAAS;CAEnE,MAAM,qBAAqB,MAAM,YAAY,qBAC3C,mBAAmB,QAAQ,oBAAoB,KAAK,CACtD;CAEA,MAAM,EAAE,oBAAoB,eAAe,MAAM,YAAY,KAAK,oBAAoB,QAAQ,MAAM;EAClG,MAAM,QAAQ;EACd,KAAK,QAAQ;CACf,CAAC;CAED,OAAO;EAAE,WAAW;EAAY,iBAAiB;CAAmB;AACtE;AAEA,eAAsB,SAAS,SAOP;CACtB,MAAM,EAAE,OAAO,gBAAgB,mBAAmB,QAAQ,SAAS;CAKnE,MAAM,sBAAsB,MAAM,YAAY,sBAC5C,oBAAoB,QAAQ,qBAAqB,KAAK,GACtD,IACF;CAEA,OAAO,MAAM,YAAY,KAAK,qBAAqB,QAAQ,iBAAiB,QAAQ,WAAW;EAC7F,MAAM,QAAQ;EACd,KAAK,QAAQ;CACf,CAAC;AACH"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Kms, TypedArrayEncoder } from "@credo-ts/core";
|
|
2
2
|
import { constants, createHmac, createPrivateKey, createSecretKey, sign } from "node:crypto";
|
|
3
3
|
import { promisify } from "node:util";
|
|
4
|
-
|
|
5
4
|
//#region src/kms/crypto/sign.ts
|
|
6
5
|
const sign$1 = promisify(sign);
|
|
7
6
|
function performSign(key, algorithm, data) {
|
|
@@ -12,11 +11,14 @@ function performSign(key, algorithm, data) {
|
|
|
12
11
|
});
|
|
13
12
|
switch (key.kty) {
|
|
14
13
|
case "RSA":
|
|
15
|
-
case "OKP":
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
case "OKP": {
|
|
15
|
+
const nodeKeyInput = algorithm.startsWith("PS") ? {
|
|
16
|
+
key: nodeKey,
|
|
17
|
+
padding: constants.RSA_PKCS1_PSS_PADDING,
|
|
18
|
+
saltLength: Number.parseInt(algorithm.slice(2), 10) / 8
|
|
19
|
+
} : nodeKey;
|
|
20
|
+
return sign$1(nodeAlgorithm, data, nodeKeyInput);
|
|
21
|
+
}
|
|
20
22
|
case "EC": return sign$1(nodeAlgorithm, data, nodeKey).then((derSignature) => Kms.derEcSignatureToRaw(derSignature, key.crv));
|
|
21
23
|
case "oct": return createHmac(nodeAlgorithm, nodeKey).update(data).digest();
|
|
22
24
|
default: throw new Kms.KeyManagementAlgorithmNotSupportedError(`kty '${key.kty}'`, "node");
|
|
@@ -59,7 +61,7 @@ function mapJwaSignatureAlgorithmToNode(algorithm) {
|
|
|
59
61
|
default: throw new Kms.KeyManagementAlgorithmNotSupportedError(`JWA algorithm '${algorithm}'`, "node");
|
|
60
62
|
}
|
|
61
63
|
}
|
|
62
|
-
|
|
63
64
|
//#endregion
|
|
64
65
|
export { mapJwaSignatureAlgorithmToNode, nodeSupportedJwaAlgorithm, performSign };
|
|
66
|
+
|
|
65
67
|
//# sourceMappingURL=sign.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sign.mjs","names":["sign","_sign"],"sources":["../../../src/kms/crypto/sign.ts"],"sourcesContent":["import { sign as _sign, constants, createHmac, createPrivateKey, createSecretKey } from 'node:crypto'\nimport { promisify } from 'node:util'\nimport type { CanBePromise } from '@credo-ts/core'\nimport { Kms, TypedArrayEncoder } from '@credo-ts/core'\n\nconst sign = promisify(_sign)\n\nexport function performSign(\n key: Kms.KmsJwkPrivate,\n algorithm: Kms.KnownJwaSignatureAlgorithm,\n data: Uint8Array\n): CanBePromise<Uint8Array> {\n const nodeAlgorithm = mapJwaSignatureAlgorithmToNode(algorithm)\n const nodeKey =\n key.kty === 'oct'\n ? createSecretKey(TypedArrayEncoder.fromBase64Url(key.k))\n : createPrivateKey({ format: 'jwk', key })\n\n switch (key.kty) {\n case 'RSA':\n case 'OKP': {\n const nodeKeyInput = algorithm.startsWith('PS')\n ? // For RSA-PSS, we need to set padding\n {\n key: nodeKey,\n padding: constants.RSA_PKCS1_PSS_PADDING,\n saltLength: Number.parseInt(algorithm.slice(2), 10) / 8,\n }\n : nodeKey\n\n return sign(nodeAlgorithm, data, nodeKeyInput) as Promise<Uint8Array>\n }\n case 'EC': {\n // Node returns EC signatures as DER encoded, but we need raw\n return sign(nodeAlgorithm, data, nodeKey).then((derSignature) => Kms.derEcSignatureToRaw(derSignature, key.crv))\n }\n case 'oct': {\n return createHmac(nodeAlgorithm as string, nodeKey)\n .update(data)\n .digest()\n }\n default:\n // @ts-expect-error\n throw new Kms.KeyManagementAlgorithmNotSupportedError(`kty '${key.kty}'`, 'node')\n }\n}\n\nexport const nodeSupportedJwaAlgorithm = [\n 'RS256',\n 'PS256',\n 'HS256',\n 'ES256',\n 'ES256K',\n 'RS384',\n 'PS384',\n 'HS384',\n 'ES384',\n 'RS512',\n 'PS512',\n 'HS512',\n 'ES512',\n 'EdDSA',\n 'Ed25519',\n] as const satisfies Kms.KnownJwaSignatureAlgorithm[]\n\nexport function mapJwaSignatureAlgorithmToNode(algorithm: Kms.KnownJwaSignatureAlgorithm) {\n switch (algorithm) {\n case 'RS256':\n case 'PS256':\n case 'HS256':\n case 'ES256':\n case 'ES256K':\n return 'sha256'\n case 'RS384':\n case 'PS384':\n case 'HS384':\n case 'ES384':\n return 'sha384'\n case 'RS512':\n case 'PS512':\n case 'HS512':\n case 'ES512':\n return 'sha512'\n // For EdDSA it's derived based on the key\n case 'EdDSA':\n case 'Ed25519':\n return undefined\n default:\n throw new Kms.KeyManagementAlgorithmNotSupportedError(`JWA algorithm '${algorithm}'`, 'node')\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"sign.mjs","names":["sign","_sign"],"sources":["../../../src/kms/crypto/sign.ts"],"sourcesContent":["import { sign as _sign, constants, createHmac, createPrivateKey, createSecretKey } from 'node:crypto'\nimport { promisify } from 'node:util'\nimport type { CanBePromise } from '@credo-ts/core'\nimport { Kms, TypedArrayEncoder } from '@credo-ts/core'\n\nconst sign = promisify(_sign)\n\nexport function performSign(\n key: Kms.KmsJwkPrivate,\n algorithm: Kms.KnownJwaSignatureAlgorithm,\n data: Uint8Array\n): CanBePromise<Uint8Array> {\n const nodeAlgorithm = mapJwaSignatureAlgorithmToNode(algorithm)\n const nodeKey =\n key.kty === 'oct'\n ? createSecretKey(TypedArrayEncoder.fromBase64Url(key.k))\n : createPrivateKey({ format: 'jwk', key })\n\n switch (key.kty) {\n case 'RSA':\n case 'OKP': {\n const nodeKeyInput = algorithm.startsWith('PS')\n ? // For RSA-PSS, we need to set padding\n {\n key: nodeKey,\n padding: constants.RSA_PKCS1_PSS_PADDING,\n saltLength: Number.parseInt(algorithm.slice(2), 10) / 8,\n }\n : nodeKey\n\n return sign(nodeAlgorithm, data, nodeKeyInput) as Promise<Uint8Array>\n }\n case 'EC': {\n // Node returns EC signatures as DER encoded, but we need raw\n return sign(nodeAlgorithm, data, nodeKey).then((derSignature) => Kms.derEcSignatureToRaw(derSignature, key.crv))\n }\n case 'oct': {\n return createHmac(nodeAlgorithm as string, nodeKey)\n .update(data)\n .digest()\n }\n default:\n // @ts-expect-error\n throw new Kms.KeyManagementAlgorithmNotSupportedError(`kty '${key.kty}'`, 'node')\n }\n}\n\nexport const nodeSupportedJwaAlgorithm = [\n 'RS256',\n 'PS256',\n 'HS256',\n 'ES256',\n 'ES256K',\n 'RS384',\n 'PS384',\n 'HS384',\n 'ES384',\n 'RS512',\n 'PS512',\n 'HS512',\n 'ES512',\n 'EdDSA',\n 'Ed25519',\n] as const satisfies Kms.KnownJwaSignatureAlgorithm[]\n\nexport function mapJwaSignatureAlgorithmToNode(algorithm: Kms.KnownJwaSignatureAlgorithm) {\n switch (algorithm) {\n case 'RS256':\n case 'PS256':\n case 'HS256':\n case 'ES256':\n case 'ES256K':\n return 'sha256'\n case 'RS384':\n case 'PS384':\n case 'HS384':\n case 'ES384':\n return 'sha384'\n case 'RS512':\n case 'PS512':\n case 'HS512':\n case 'ES512':\n return 'sha512'\n // For EdDSA it's derived based on the key\n case 'EdDSA':\n case 'Ed25519':\n return undefined\n default:\n throw new Kms.KeyManagementAlgorithmNotSupportedError(`JWA algorithm '${algorithm}'`, 'node')\n }\n}\n"],"mappings":";;;;AAKA,MAAMA,SAAO,UAAUC,IAAK;AAE5B,SAAgB,YACd,KACA,WACA,MAC0B;CAC1B,MAAM,gBAAgB,+BAA+B,SAAS;CAC9D,MAAM,UACJ,IAAI,QAAQ,QACR,gBAAgB,kBAAkB,cAAc,IAAI,CAAC,CAAC,IACtD,iBAAiB;EAAE,QAAQ;EAAO;CAAI,CAAC;CAE7C,QAAQ,IAAI,KAAZ;EACE,KAAK;EACL,KAAK,OAAO;GACV,MAAM,eAAe,UAAU,WAAW,IAAI,IAE1C;IACE,KAAK;IACL,SAAS,UAAU;IACnB,YAAY,OAAO,SAAS,UAAU,MAAM,CAAC,GAAG,EAAE,IAAI;GACxD,IACA;GAEJ,OAAOD,OAAK,eAAe,MAAM,YAAY;EAC/C;EACA,KAAK,MAEH,OAAOA,OAAK,eAAe,MAAM,OAAO,CAAC,CAAC,MAAM,iBAAiB,IAAI,oBAAoB,cAAc,IAAI,GAAG,CAAC;EAEjH,KAAK,OACH,OAAO,WAAW,eAAyB,OAAO,CAAC,CAChD,OAAO,IAAI,CAAC,CACZ,OAAO;EAEZ,SAEE,MAAM,IAAI,IAAI,wCAAwC,QAAQ,IAAI,IAAI,IAAI,MAAM;CACpF;AACF;AAEA,MAAa,4BAA4B;CACvC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAgB,+BAA+B,WAA2C;CACxF,QAAQ,WAAR;EACE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,UACH,OAAO;EACT,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,SACH,OAAO;EACT,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,SACH,OAAO;EAET,KAAK;EACL,KAAK,WACH;EACF,SACE,MAAM,IAAI,IAAI,wCAAwC,kBAAkB,UAAU,IAAI,MAAM;CAChG;AACF"}
|
|
@@ -2,7 +2,6 @@ import { mapJwaSignatureAlgorithmToNode } from "./sign.mjs";
|
|
|
2
2
|
import { Kms, TypedArrayEncoder } from "@credo-ts/core";
|
|
3
3
|
import { constants, createHmac, createPublicKey, createSecretKey, timingSafeEqual, verify } from "node:crypto";
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
|
-
|
|
6
5
|
//#region src/kms/crypto/verify.ts
|
|
7
6
|
const verify$1 = promisify(verify);
|
|
8
7
|
function performVerify(key, algorithm, data, signature) {
|
|
@@ -13,17 +12,23 @@ function performVerify(key, algorithm, data, signature) {
|
|
|
13
12
|
});
|
|
14
13
|
switch (key.kty) {
|
|
15
14
|
case "RSA":
|
|
16
|
-
case "OKP":
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
case "OKP": {
|
|
16
|
+
const nodeKeyInput = algorithm.startsWith("PS") ? {
|
|
17
|
+
key: nodeKey,
|
|
18
|
+
padding: constants.RSA_PKCS1_PSS_PADDING,
|
|
19
|
+
saltLength: Number.parseInt(algorithm.slice(2), 10) / 8
|
|
20
|
+
} : nodeKey;
|
|
21
|
+
return verify$1(nodeAlgorithm, data, nodeKeyInput, signature);
|
|
22
|
+
}
|
|
21
23
|
case "EC": return verify$1(nodeAlgorithm, data, nodeKey, Kms.rawEcSignatureToDer(signature, key.crv));
|
|
22
|
-
case "oct":
|
|
24
|
+
case "oct": {
|
|
25
|
+
const expectedHmac = createHmac(nodeAlgorithm, nodeKey).update(data).digest();
|
|
26
|
+
return timingSafeEqual(expectedHmac, signature);
|
|
27
|
+
}
|
|
23
28
|
default: throw new Kms.KeyManagementAlgorithmNotSupportedError(`kty '${key.kty}'`, "node");
|
|
24
29
|
}
|
|
25
30
|
}
|
|
26
|
-
|
|
27
31
|
//#endregion
|
|
28
32
|
export { performVerify };
|
|
33
|
+
|
|
29
34
|
//# sourceMappingURL=verify.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.mjs","names":["verify","_verify"],"sources":["../../../src/kms/crypto/verify.ts"],"sourcesContent":["import {\n verify as _verify,\n constants,\n createHmac,\n createPublicKey,\n createSecretKey,\n timingSafeEqual,\n} from 'node:crypto'\nimport { promisify } from 'node:util'\nimport { type CanBePromise, Kms, TypedArrayEncoder } from '@credo-ts/core'\n\nimport { mapJwaSignatureAlgorithmToNode } from './sign'\n\nconst verify = promisify(_verify)\n\nexport function performVerify(\n key: Kms.KmsJwkPrivate | Kms.KmsJwkPublicEc | Kms.KmsJwkPublicOkp | Kms.KmsJwkPublicRsa,\n algorithm: Kms.KnownJwaSignatureAlgorithm,\n data: Uint8Array,\n signature: Uint8Array\n): CanBePromise<boolean> {\n const nodeAlgorithm = mapJwaSignatureAlgorithmToNode(algorithm)\n const nodeKey =\n key.kty === 'oct'\n ? createSecretKey(TypedArrayEncoder.fromBase64Url(key.k))\n : createPublicKey({ format: 'jwk', key })\n\n switch (key.kty) {\n case 'RSA':\n case 'OKP': {\n const nodeKeyInput = algorithm.startsWith('PS')\n ? // For RSA-PSS, we need to set padding\n {\n key: nodeKey,\n padding: constants.RSA_PKCS1_PSS_PADDING,\n saltLength: Number.parseInt(algorithm.slice(2), 10) / 8,\n }\n : nodeKey\n\n return verify(nodeAlgorithm, data, nodeKeyInput, signature)\n }\n case 'EC': {\n // Node expects DER encoded signature, but we input raw\n return verify(nodeAlgorithm, data, nodeKey, Kms.rawEcSignatureToDer(signature, key.crv))\n }\n case 'oct': {\n const expectedHmac = createHmac(nodeAlgorithm as string, nodeKey)\n .update(data)\n .digest()\n\n return timingSafeEqual(expectedHmac, signature)\n }\n default:\n // @ts-expect-error\n throw new Kms.KeyManagementAlgorithmNotSupportedError(`kty '${key.kty}'`, 'node')\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"verify.mjs","names":["verify","_verify"],"sources":["../../../src/kms/crypto/verify.ts"],"sourcesContent":["import {\n verify as _verify,\n constants,\n createHmac,\n createPublicKey,\n createSecretKey,\n timingSafeEqual,\n} from 'node:crypto'\nimport { promisify } from 'node:util'\nimport { type CanBePromise, Kms, TypedArrayEncoder } from '@credo-ts/core'\n\nimport { mapJwaSignatureAlgorithmToNode } from './sign'\n\nconst verify = promisify(_verify)\n\nexport function performVerify(\n key: Kms.KmsJwkPrivate | Kms.KmsJwkPublicEc | Kms.KmsJwkPublicOkp | Kms.KmsJwkPublicRsa,\n algorithm: Kms.KnownJwaSignatureAlgorithm,\n data: Uint8Array,\n signature: Uint8Array\n): CanBePromise<boolean> {\n const nodeAlgorithm = mapJwaSignatureAlgorithmToNode(algorithm)\n const nodeKey =\n key.kty === 'oct'\n ? createSecretKey(TypedArrayEncoder.fromBase64Url(key.k))\n : createPublicKey({ format: 'jwk', key })\n\n switch (key.kty) {\n case 'RSA':\n case 'OKP': {\n const nodeKeyInput = algorithm.startsWith('PS')\n ? // For RSA-PSS, we need to set padding\n {\n key: nodeKey,\n padding: constants.RSA_PKCS1_PSS_PADDING,\n saltLength: Number.parseInt(algorithm.slice(2), 10) / 8,\n }\n : nodeKey\n\n return verify(nodeAlgorithm, data, nodeKeyInput, signature)\n }\n case 'EC': {\n // Node expects DER encoded signature, but we input raw\n return verify(nodeAlgorithm, data, nodeKey, Kms.rawEcSignatureToDer(signature, key.crv))\n }\n case 'oct': {\n const expectedHmac = createHmac(nodeAlgorithm as string, nodeKey)\n .update(data)\n .digest()\n\n return timingSafeEqual(expectedHmac, signature)\n }\n default:\n // @ts-expect-error\n throw new Kms.KeyManagementAlgorithmNotSupportedError(`kty '${key.kty}'`, 'node')\n }\n}\n"],"mappings":";;;;;AAaA,MAAMA,WAAS,UAAUC,MAAO;AAEhC,SAAgB,cACd,KACA,WACA,MACA,WACuB;CACvB,MAAM,gBAAgB,+BAA+B,SAAS;CAC9D,MAAM,UACJ,IAAI,QAAQ,QACR,gBAAgB,kBAAkB,cAAc,IAAI,CAAC,CAAC,IACtD,gBAAgB;EAAE,QAAQ;EAAO;CAAI,CAAC;CAE5C,QAAQ,IAAI,KAAZ;EACE,KAAK;EACL,KAAK,OAAO;GACV,MAAM,eAAe,UAAU,WAAW,IAAI,IAE1C;IACE,KAAK;IACL,SAAS,UAAU;IACnB,YAAY,OAAO,SAAS,UAAU,MAAM,CAAC,GAAG,EAAE,IAAI;GACxD,IACA;GAEJ,OAAOD,SAAO,eAAe,MAAM,cAAc,SAAS;EAC5D;EACA,KAAK,MAEH,OAAOA,SAAO,eAAe,MAAM,SAAS,IAAI,oBAAoB,WAAW,IAAI,GAAG,CAAC;EAEzF,KAAK,OAAO;GACV,MAAM,eAAe,WAAW,eAAyB,OAAO,CAAC,CAC9D,OAAO,IAAI,CAAC,CACZ,OAAO;GAEV,OAAO,gBAAgB,cAAc,SAAS;EAChD;EACA,SAEE,MAAM,IAAI,IAAI,wCAAwC,QAAQ,IAAI,IAAI,IAAI,MAAM;CACpF;AACF"}
|
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
import { AgentContext } from "@credo-ts/core";
|
|
2
|
-
import * as http$1 from "http";
|
|
3
2
|
import { Server } from "http";
|
|
4
3
|
import { DidCommInboundTransport } from "@credo-ts/didcomm";
|
|
5
4
|
import { Express } from "express";
|
|
6
|
-
|
|
7
5
|
//#region src/transport/DidCommHttpInboundTransport.d.ts
|
|
8
|
-
declare class DidCommHttpInboundTransport implements DidCommInboundTransport {
|
|
6
|
+
export declare class DidCommHttpInboundTransport implements DidCommInboundTransport {
|
|
9
7
|
readonly app: Express;
|
|
10
8
|
private port?;
|
|
11
9
|
private path;
|
|
12
10
|
private _server?;
|
|
13
11
|
private processedMessageListenerTimeoutMs;
|
|
14
|
-
get server(): Server<typeof http
|
|
15
|
-
constructor({
|
|
16
|
-
app,
|
|
17
|
-
path,
|
|
18
|
-
port,
|
|
19
|
-
processedMessageListenerTimeoutMs
|
|
20
|
-
}: {
|
|
12
|
+
get server(): Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse> | undefined;
|
|
13
|
+
constructor({ app, path, port, processedMessageListenerTimeoutMs }: {
|
|
21
14
|
app: Express;
|
|
22
15
|
port?: undefined;
|
|
23
16
|
path?: string;
|
|
@@ -33,5 +26,4 @@ declare class DidCommHttpInboundTransport implements DidCommInboundTransport {
|
|
|
33
26
|
private registerRoute;
|
|
34
27
|
}
|
|
35
28
|
//#endregion
|
|
36
|
-
export { DidCommHttpInboundTransport };
|
|
37
29
|
//# sourceMappingURL=DidCommHttpInboundTransport.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DidCommHttpInboundTransport.d.mts","names":[],"sources":["../../src/transport/DidCommHttpInboundTransport.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"DidCommHttpInboundTransport.d.mts","names":[],"sources":["../../src/transport/DidCommHttpInboundTransport.ts"],"mappings":";;;;;qBAiBa,uCAAuC;WAClC,KAAK;UACb;UACA;UACA;UACA;MAEG,UAAM,6BAAA,uCAAA;EAIE,cACjB,KACA,MACA,MACA;IAEI,KAAK;IAAS;IAAkB;IAAe;;IAC/C,MAAM;IAAS;IAAc;IAAe;;EASrC,MAAM,cAAc,eAAY;EA+BhC,QAAQ;UAQb"}
|
|
@@ -2,7 +2,6 @@ import { CredoError, EventEmitter, utils } from "@credo-ts/core";
|
|
|
2
2
|
import { DidCommEventTypes, DidCommMimeType, DidCommModuleConfig, DidCommTransportService } from "@credo-ts/didcomm";
|
|
3
3
|
import express, { text } from "express";
|
|
4
4
|
import { ReplaySubject, filter, firstValueFrom, take, timeout } from "rxjs";
|
|
5
|
-
|
|
6
5
|
//#region src/transport/DidCommHttpInboundTransport.ts
|
|
7
6
|
const supportedContentTypes = [DidCommMimeType.V0, DidCommMimeType.V1];
|
|
8
7
|
var DidCommHttpInboundTransport = class {
|
|
@@ -94,7 +93,7 @@ var HttpTransportSession = class {
|
|
|
94
93
|
this.res.status(200).contentType(responseMimeType).json(encryptedMessage).end();
|
|
95
94
|
}
|
|
96
95
|
};
|
|
97
|
-
|
|
98
96
|
//#endregion
|
|
99
|
-
export { DidCommHttpInboundTransport };
|
|
97
|
+
export { DidCommHttpInboundTransport, HttpTransportSession };
|
|
98
|
+
|
|
100
99
|
//# sourceMappingURL=DidCommHttpInboundTransport.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DidCommHttpInboundTransport.mjs","names":[],"sources":["../../src/transport/DidCommHttpInboundTransport.ts"],"sourcesContent":["import type { AgentContext } from '@credo-ts/core'\nimport { CredoError, EventEmitter, utils } from '@credo-ts/core'\nimport type {\n DidCommEncryptedMessage,\n DidCommInboundTransport,\n DidCommMessageProcessedEvent,\n DidCommMessageReceivedEvent,\n DidCommTransportSession,\n} from '@credo-ts/didcomm'\nimport { DidCommEventTypes, DidCommMimeType, DidCommModuleConfig, DidCommTransportService } from '@credo-ts/didcomm'\nimport type { Express, Request, Response } from 'express'\nimport express, { text } from 'express'\nimport type { Server } from 'http'\nimport { filter, firstValueFrom, ReplaySubject, take, timeout } from 'rxjs'\n\nconst supportedContentTypes: string[] = [DidCommMimeType.V0, DidCommMimeType.V1]\n\nexport class DidCommHttpInboundTransport implements DidCommInboundTransport {\n public readonly app: Express\n private port?: number\n private path: string\n private _server?: Server\n private processedMessageListenerTimeoutMs: number\n\n public get server() {\n return this._server\n }\n\n public constructor({\n app,\n path,\n port,\n processedMessageListenerTimeoutMs,\n }:\n | { app: Express; port?: undefined; path?: string; processedMessageListenerTimeoutMs?: number }\n | { app?: Express; port: number; path?: string; processedMessageListenerTimeoutMs?: number }) {\n this.port = port\n this.processedMessageListenerTimeoutMs = processedMessageListenerTimeoutMs ?? 10000 // timeout after 10 seconds\n\n // Use the caller-provided Express app, or create one\n this.app = app ?? express()\n this.path = path ?? '/'\n }\n\n public async start(agentContext: AgentContext) {\n const transportService = agentContext.dependencyManager.resolve(DidCommTransportService)\n\n agentContext.config.logger.debug('Starting HTTP inbound transport', {\n port: this.port,\n })\n\n this.registerRoute(agentContext, transportService)\n\n if (this.port === undefined) {\n return\n }\n\n const server = this.app.listen(this.port)\n this._server = server\n\n await new Promise<void>((resolve, reject) => {\n const onError = (error: Error) => {\n server.off('listening', onListening)\n reject(error)\n }\n const onListening = () => {\n server.off('error', onError)\n resolve()\n }\n\n server.once('error', onError)\n server.once('listening', onListening)\n })\n }\n\n public async stop(): Promise<void> {\n if (!this._server) {\n return\n }\n\n return new Promise((resolve, reject) => this._server?.close((err) => (err ? reject(err) : resolve())))\n }\n\n private registerRoute(agentContext: AgentContext, transportService: DidCommTransportService) {\n this.app.post(this.path, text({ type: supportedContentTypes, limit: '5mb' }), async (req, res) => {\n const contentType = req.headers['content-type']\n\n if (!contentType || !supportedContentTypes.includes(contentType)) {\n return res\n .status(415)\n .send(`Unsupported content-type. Supported content-types are: ${supportedContentTypes.join(', ')}`)\n }\n\n const session = new HttpTransportSession(utils.uuid(), req, res)\n // We want to make sure the session is removed if the connection is closed, as it\n // can't be used anymore then. This could happen if the client abruptly closes the connection.\n req.once('close', () => transportService.removeSession(session))\n\n try {\n const message = req.body as string\n const encryptedMessage = JSON.parse(message) as DidCommEncryptedMessage\n\n const eventEmitter = agentContext.dependencyManager.resolve(EventEmitter)\n const observable = eventEmitter.observable<DidCommMessageProcessedEvent>(\n DidCommEventTypes.DidCommMessageProcessed\n )\n const subject = new ReplaySubject(1)\n\n observable\n .pipe(\n filter((e) => e.type === DidCommEventTypes.DidCommMessageProcessed),\n filter((e) => e.payload.encryptedMessage === encryptedMessage),\n timeout({\n first: this.processedMessageListenerTimeoutMs,\n meta: 'DidCommHttpInboundTransport.start',\n }),\n take(1) // automatically unsubscribe after the first matching event\n )\n .subscribe(subject)\n\n eventEmitter.emit<DidCommMessageReceivedEvent>(agentContext, {\n type: DidCommEventTypes.DidCommMessageReceived,\n payload: {\n message: encryptedMessage,\n session: session,\n },\n })\n\n // Wait for message to be processed\n await firstValueFrom(subject)\n\n // If agent did not use session when processing message we need to send response here.\n if (!res.headersSent) {\n res.status(200).end()\n }\n } catch (error) {\n agentContext.config.logger.error(`Error processing inbound message: ${error.message}`, error)\n\n if (!res.headersSent) {\n res.status(500).send('Error processing message')\n }\n } finally {\n transportService.removeSession(session)\n }\n })\n }\n}\n\nexport class HttpTransportSession implements DidCommTransportSession {\n public id: string\n public readonly type = 'http'\n public req: Request\n public res: Response\n\n public constructor(id: string, req: Request, res: Response) {\n this.id = id\n this.req = req\n this.res = res\n }\n\n public async close(): Promise<void> {\n if (!this.res.headersSent) {\n this.res.status(200).end()\n }\n }\n\n public async send(agentContext: AgentContext, encryptedMessage: DidCommEncryptedMessage): Promise<void> {\n if (this.res.headersSent) {\n throw new CredoError(`${this.type} transport session has been closed.`)\n }\n\n // By default we take the agent config's default DIDComm content-type\n const didcommConfig = agentContext.dependencyManager.resolve(DidCommModuleConfig)\n let responseMimeType = didcommConfig.didCommMimeType as string\n\n // However, if the request mime-type is a mime-type that is supported by us, we use that\n // to minimize the chance of interoperability issues\n const requestMimeType = this.req.headers['content-type']\n if (requestMimeType && supportedContentTypes.includes(requestMimeType)) {\n responseMimeType = requestMimeType\n }\n\n this.res.status(200).contentType(responseMimeType).json(encryptedMessage).end()\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"DidCommHttpInboundTransport.mjs","names":[],"sources":["../../src/transport/DidCommHttpInboundTransport.ts"],"sourcesContent":["import type { AgentContext } from '@credo-ts/core'\nimport { CredoError, EventEmitter, utils } from '@credo-ts/core'\nimport type {\n DidCommEncryptedMessage,\n DidCommInboundTransport,\n DidCommMessageProcessedEvent,\n DidCommMessageReceivedEvent,\n DidCommTransportSession,\n} from '@credo-ts/didcomm'\nimport { DidCommEventTypes, DidCommMimeType, DidCommModuleConfig, DidCommTransportService } from '@credo-ts/didcomm'\nimport type { Express, Request, Response } from 'express'\nimport express, { text } from 'express'\nimport type { Server } from 'http'\nimport { filter, firstValueFrom, ReplaySubject, take, timeout } from 'rxjs'\n\nconst supportedContentTypes: string[] = [DidCommMimeType.V0, DidCommMimeType.V1]\n\nexport class DidCommHttpInboundTransport implements DidCommInboundTransport {\n public readonly app: Express\n private port?: number\n private path: string\n private _server?: Server\n private processedMessageListenerTimeoutMs: number\n\n public get server() {\n return this._server\n }\n\n public constructor({\n app,\n path,\n port,\n processedMessageListenerTimeoutMs,\n }:\n | { app: Express; port?: undefined; path?: string; processedMessageListenerTimeoutMs?: number }\n | { app?: Express; port: number; path?: string; processedMessageListenerTimeoutMs?: number }) {\n this.port = port\n this.processedMessageListenerTimeoutMs = processedMessageListenerTimeoutMs ?? 10000 // timeout after 10 seconds\n\n // Use the caller-provided Express app, or create one\n this.app = app ?? express()\n this.path = path ?? '/'\n }\n\n public async start(agentContext: AgentContext) {\n const transportService = agentContext.dependencyManager.resolve(DidCommTransportService)\n\n agentContext.config.logger.debug('Starting HTTP inbound transport', {\n port: this.port,\n })\n\n this.registerRoute(agentContext, transportService)\n\n if (this.port === undefined) {\n return\n }\n\n const server = this.app.listen(this.port)\n this._server = server\n\n await new Promise<void>((resolve, reject) => {\n const onError = (error: Error) => {\n server.off('listening', onListening)\n reject(error)\n }\n const onListening = () => {\n server.off('error', onError)\n resolve()\n }\n\n server.once('error', onError)\n server.once('listening', onListening)\n })\n }\n\n public async stop(): Promise<void> {\n if (!this._server) {\n return\n }\n\n return new Promise((resolve, reject) => this._server?.close((err) => (err ? reject(err) : resolve())))\n }\n\n private registerRoute(agentContext: AgentContext, transportService: DidCommTransportService) {\n this.app.post(this.path, text({ type: supportedContentTypes, limit: '5mb' }), async (req, res) => {\n const contentType = req.headers['content-type']\n\n if (!contentType || !supportedContentTypes.includes(contentType)) {\n return res\n .status(415)\n .send(`Unsupported content-type. Supported content-types are: ${supportedContentTypes.join(', ')}`)\n }\n\n const session = new HttpTransportSession(utils.uuid(), req, res)\n // We want to make sure the session is removed if the connection is closed, as it\n // can't be used anymore then. This could happen if the client abruptly closes the connection.\n req.once('close', () => transportService.removeSession(session))\n\n try {\n const message = req.body as string\n const encryptedMessage = JSON.parse(message) as DidCommEncryptedMessage\n\n const eventEmitter = agentContext.dependencyManager.resolve(EventEmitter)\n const observable = eventEmitter.observable<DidCommMessageProcessedEvent>(\n DidCommEventTypes.DidCommMessageProcessed\n )\n const subject = new ReplaySubject(1)\n\n observable\n .pipe(\n filter((e) => e.type === DidCommEventTypes.DidCommMessageProcessed),\n filter((e) => e.payload.encryptedMessage === encryptedMessage),\n timeout({\n first: this.processedMessageListenerTimeoutMs,\n meta: 'DidCommHttpInboundTransport.start',\n }),\n take(1) // automatically unsubscribe after the first matching event\n )\n .subscribe(subject)\n\n eventEmitter.emit<DidCommMessageReceivedEvent>(agentContext, {\n type: DidCommEventTypes.DidCommMessageReceived,\n payload: {\n message: encryptedMessage,\n session: session,\n },\n })\n\n // Wait for message to be processed\n await firstValueFrom(subject)\n\n // If agent did not use session when processing message we need to send response here.\n if (!res.headersSent) {\n res.status(200).end()\n }\n } catch (error) {\n agentContext.config.logger.error(`Error processing inbound message: ${error.message}`, error)\n\n if (!res.headersSent) {\n res.status(500).send('Error processing message')\n }\n } finally {\n transportService.removeSession(session)\n }\n })\n }\n}\n\nexport class HttpTransportSession implements DidCommTransportSession {\n public id: string\n public readonly type = 'http'\n public req: Request\n public res: Response\n\n public constructor(id: string, req: Request, res: Response) {\n this.id = id\n this.req = req\n this.res = res\n }\n\n public async close(): Promise<void> {\n if (!this.res.headersSent) {\n this.res.status(200).end()\n }\n }\n\n public async send(agentContext: AgentContext, encryptedMessage: DidCommEncryptedMessage): Promise<void> {\n if (this.res.headersSent) {\n throw new CredoError(`${this.type} transport session has been closed.`)\n }\n\n // By default we take the agent config's default DIDComm content-type\n const didcommConfig = agentContext.dependencyManager.resolve(DidCommModuleConfig)\n let responseMimeType = didcommConfig.didCommMimeType as string\n\n // However, if the request mime-type is a mime-type that is supported by us, we use that\n // to minimize the chance of interoperability issues\n const requestMimeType = this.req.headers['content-type']\n if (requestMimeType && supportedContentTypes.includes(requestMimeType)) {\n responseMimeType = requestMimeType\n }\n\n this.res.status(200).contentType(responseMimeType).json(encryptedMessage).end()\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import WebSocket, { WebSocketServer } from "ws";
|
|
1
|
+
import WebSocket$1, { WebSocketServer } from "ws";
|
|
2
2
|
import { AgentContext } from "@credo-ts/core";
|
|
3
3
|
import { DidCommInboundTransport } from "@credo-ts/didcomm";
|
|
4
|
-
|
|
5
4
|
//#region src/transport/DidCommWsInboundTransport.d.ts
|
|
6
|
-
declare class DidCommWsInboundTransport implements DidCommInboundTransport {
|
|
5
|
+
export declare class DidCommWsInboundTransport implements DidCommInboundTransport {
|
|
7
6
|
private socketServer;
|
|
8
7
|
private logger;
|
|
9
8
|
private socketIds;
|
|
10
|
-
constructor({
|
|
11
|
-
server,
|
|
12
|
-
port
|
|
13
|
-
}: {
|
|
9
|
+
constructor({ server, port }: {
|
|
14
10
|
server: WebSocketServer;
|
|
15
11
|
port?: undefined;
|
|
16
12
|
} | {
|
|
@@ -22,5 +18,4 @@ declare class DidCommWsInboundTransport implements DidCommInboundTransport {
|
|
|
22
18
|
private listenOnWebSocketMessages;
|
|
23
19
|
}
|
|
24
20
|
//#endregion
|
|
25
|
-
export { DidCommWsInboundTransport };
|
|
26
21
|
//# sourceMappingURL=DidCommWsInboundTransport.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DidCommWsInboundTransport.d.mts","names":[],"sources":["../../src/transport/DidCommWsInboundTransport.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"DidCommWsInboundTransport.d.mts","names":[],"sources":["../../src/transport/DidCommWsInboundTransport.ts"],"mappings":";;;;qBAWa,qCAAqC;UACxC;UACA;UAGA;EAEW,cACjB,QACA;IACG,QAAQ;IAAiB;;IAAuB;IAAoB;;EAI5D,MAAM,cAAc,eAAY;EA+BhC,QAAI;UAiBT"}
|