@microsoft/ccf-app 3.0.0-dev5 → 3.0.0-rc0

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/converters.js CHANGED
@@ -19,8 +19,29 @@
19
19
  * @module
20
20
  */
21
21
  import { ccf } from "./global.js";
22
+ function checkBoolean(val) {
23
+ if (typeof val !== "boolean") {
24
+ throw new TypeError(`Value ${val} is not a boolean`);
25
+ }
26
+ }
27
+ function checkNumber(val) {
28
+ if (typeof val !== "number") {
29
+ throw new TypeError(`Value ${val} is not a number`);
30
+ }
31
+ }
32
+ function checkBigInt(val) {
33
+ if (typeof val !== "bigint") {
34
+ throw new TypeError(`Value ${val} is not a bigint`);
35
+ }
36
+ }
37
+ function checkString(val) {
38
+ if (typeof val !== "string") {
39
+ throw new TypeError(`Value ${val} is not a string`);
40
+ }
41
+ }
22
42
  class BoolConverter {
23
43
  encode(val) {
44
+ checkBoolean(val);
24
45
  const buf = new ArrayBuffer(1);
25
46
  new DataView(buf).setUint8(0, val ? 1 : 0);
26
47
  return buf;
@@ -31,6 +52,7 @@ class BoolConverter {
31
52
  }
32
53
  class Int8Converter {
33
54
  encode(val) {
55
+ checkNumber(val);
34
56
  if (val < -128 || val > 127) {
35
57
  throw new RangeError("value is not within int8 range");
36
58
  }
@@ -44,6 +66,7 @@ class Int8Converter {
44
66
  }
45
67
  class Uint8Converter {
46
68
  encode(val) {
69
+ checkNumber(val);
47
70
  if (val < 0 || val > 255) {
48
71
  throw new RangeError("value is not within uint8 range");
49
72
  }
@@ -57,6 +80,7 @@ class Uint8Converter {
57
80
  }
58
81
  class Int16Converter {
59
82
  encode(val) {
83
+ checkNumber(val);
60
84
  if (val < -32768 || val > 32767) {
61
85
  throw new RangeError("value is not within int16 range");
62
86
  }
@@ -70,6 +94,7 @@ class Int16Converter {
70
94
  }
71
95
  class Uint16Converter {
72
96
  encode(val) {
97
+ checkNumber(val);
73
98
  if (val < 0 || val > 65535) {
74
99
  throw new RangeError("value is not within uint16 range");
75
100
  }
@@ -83,6 +108,7 @@ class Uint16Converter {
83
108
  }
84
109
  class Int32Converter {
85
110
  encode(val) {
111
+ checkNumber(val);
86
112
  if (val < -2147483648 || val > 2147483647) {
87
113
  throw new RangeError("value is not within int32 range");
88
114
  }
@@ -96,6 +122,7 @@ class Int32Converter {
96
122
  }
97
123
  class Uint32Converter {
98
124
  encode(val) {
125
+ checkNumber(val);
99
126
  if (val < 0 || val > 4294967295) {
100
127
  throw new RangeError("value is not within uint32 range");
101
128
  }
@@ -109,6 +136,7 @@ class Uint32Converter {
109
136
  }
110
137
  class Int64Converter {
111
138
  encode(val) {
139
+ checkBigInt(val);
112
140
  const buf = new ArrayBuffer(8);
113
141
  new DataView(buf).setBigInt64(0, val, true);
114
142
  return buf;
@@ -119,6 +147,7 @@ class Int64Converter {
119
147
  }
120
148
  class Uint64Converter {
121
149
  encode(val) {
150
+ checkBigInt(val);
122
151
  const buf = new ArrayBuffer(8);
123
152
  new DataView(buf).setBigUint64(0, val, true);
124
153
  return buf;
@@ -129,6 +158,7 @@ class Uint64Converter {
129
158
  }
130
159
  class Float32Converter {
131
160
  encode(val) {
161
+ checkNumber(val);
132
162
  const buf = new ArrayBuffer(4);
133
163
  new DataView(buf).setFloat32(0, val, true);
134
164
  return buf;
@@ -139,6 +169,7 @@ class Float32Converter {
139
169
  }
140
170
  class Float64Converter {
141
171
  encode(val) {
172
+ checkNumber(val);
142
173
  const buf = new ArrayBuffer(8);
143
174
  new DataView(buf).setFloat64(0, val, true);
144
175
  return buf;
@@ -149,6 +180,7 @@ class Float64Converter {
149
180
  }
150
181
  class StringConverter {
151
182
  encode(val) {
183
+ checkString(val);
152
184
  return ccf.strToBuf(val);
153
185
  }
154
186
  decode(buf) {
package/crypto.d.ts CHANGED
@@ -10,6 +10,10 @@ export declare const generateRsaKeyPair: (size: number, exponent?: number | unde
10
10
  * @inheritDoc CCF.generateEcdsaKeyPair
11
11
  */
12
12
  export declare const generateEcdsaKeyPair: (curve: string) => import("./global.js").CryptoKeyPair;
13
+ /**
14
+ * @inheritDoc CCFCrypto.generateEcdsaKeyPair
15
+ */
16
+ export declare const generateEddsaKeyPair: (curve: string) => import("./global.js").CryptoKeyPair;
13
17
  /**
14
18
  * @inheritDoc CCF.wrapKey
15
19
  */
@@ -30,4 +34,20 @@ export declare const isValidX509CertBundle: (pem: string) => boolean;
30
34
  * @inheritDoc CCF.isValidX509CertChain
31
35
  */
32
36
  export declare const isValidX509CertChain: (chain: string, trusted: string) => boolean;
37
+ /**
38
+ * @inheritDoc CCF.pubPemToJwk
39
+ */
40
+ export declare const pubPemToJwk: (pem: string, kid?: string | undefined) => import("./global.js").JsonWebKeyECPublic;
41
+ /**
42
+ * @inheritDoc CCF.pemToJwk
43
+ */
44
+ export declare const pemToJwk: (pem: string, kid?: string | undefined) => import("./global.js").JsonWebKeyECPrivate;
45
+ /**
46
+ * @inheritDoc CCF.pubRsaPemToJwk
47
+ */
48
+ export declare const pubRsaPemToJwk: (pem: string, kid?: string | undefined) => import("./global.js").JsonWebKeyRSAPublic;
49
+ /**
50
+ * @inheritDoc CCF.rsaPemToJwk
51
+ */
52
+ export declare const rsaPemToJwk: (pem: string, kid?: string | undefined) => import("./global.js").JsonWebKeyRSAPrivate;
33
53
  export { WrapAlgoParams, AesKwpParams, RsaOaepParams, RsaOaepAesKwpParams, CryptoKeyPair, DigestAlgorithm, SigningAlgorithm, RsaPkcsParams, EcdsaParams, } from "./global";
package/crypto.js CHANGED
@@ -17,19 +17,23 @@ import { ccf } from "./global.js";
17
17
  /**
18
18
  * @inheritDoc CCF.generateAesKey
19
19
  */
20
- export const generateAesKey = ccf.generateAesKey;
20
+ export const generateAesKey = ccf.crypto.generateAesKey;
21
21
  /**
22
22
  * @inheritDoc CCF.generateRsaKeyPair
23
23
  */
24
- export const generateRsaKeyPair = ccf.generateRsaKeyPair;
24
+ export const generateRsaKeyPair = ccf.crypto.generateRsaKeyPair;
25
25
  /**
26
26
  * @inheritDoc CCF.generateEcdsaKeyPair
27
27
  */
28
- export const generateEcdsaKeyPair = ccf.generateEcdsaKeyPair;
28
+ export const generateEcdsaKeyPair = ccf.crypto.generateEcdsaKeyPair;
29
+ /**
30
+ * @inheritDoc CCFCrypto.generateEcdsaKeyPair
31
+ */
32
+ export const generateEddsaKeyPair = ccf.crypto.generateEddsaKeyPair;
29
33
  /**
30
34
  * @inheritDoc CCF.wrapKey
31
35
  */
32
- export const wrapKey = ccf.wrapKey;
36
+ export const wrapKey = ccf.crypto.wrapKey;
33
37
  /**
34
38
  * @inheritDoc CCFCrypto.verifySignature
35
39
  */
@@ -37,12 +41,28 @@ export const verifySignature = ccf.crypto.verifySignature;
37
41
  /**
38
42
  * @inheritDoc CCF.digest
39
43
  */
40
- export const digest = ccf.digest;
44
+ export const digest = ccf.crypto.digest;
41
45
  /**
42
46
  * @inheritDoc CCF.isValidX509CertBundle
43
47
  */
44
- export const isValidX509CertBundle = ccf.isValidX509CertBundle;
48
+ export const isValidX509CertBundle = ccf.crypto.isValidX509CertBundle;
45
49
  /**
46
50
  * @inheritDoc CCF.isValidX509CertChain
47
51
  */
48
- export const isValidX509CertChain = ccf.isValidX509CertChain;
52
+ export const isValidX509CertChain = ccf.crypto.isValidX509CertChain;
53
+ /**
54
+ * @inheritDoc CCF.pubPemToJwk
55
+ */
56
+ export const pubPemToJwk = ccf.crypto.pubPemToJwk;
57
+ /**
58
+ * @inheritDoc CCF.pemToJwk
59
+ */
60
+ export const pemToJwk = ccf.crypto.pemToJwk;
61
+ /**
62
+ * @inheritDoc CCF.pubRsaPemToJwk
63
+ */
64
+ export const pubRsaPemToJwk = ccf.crypto.pubRsaPemToJwk;
65
+ /**
66
+ * @inheritDoc CCF.rsaPemToJwk
67
+ */
68
+ export const rsaPemToJwk = ccf.crypto.rsaPemToJwk;
package/global.d.ts CHANGED
@@ -164,11 +164,11 @@ export interface RsaOaepAesKwpParams {
164
164
  export declare type WrapAlgoParams = RsaOaepParams | AesKwpParams | RsaOaepAesKwpParams;
165
165
  export interface CryptoKeyPair {
166
166
  /**
167
- * RSA private key in PEM encoding.
167
+ * Private key in PEM encoding.
168
168
  */
169
169
  privateKey: string;
170
170
  /**
171
- * RSA public key in PEM encoding.
171
+ * Public key in PEM encoding.
172
172
  */
173
173
  publicKey: string;
174
174
  }
@@ -193,6 +193,63 @@ export interface EcdsaParams {
193
193
  }
194
194
  export declare type SigningAlgorithm = RsaPkcsParams | EcdsaParams;
195
195
  export declare type DigestAlgorithm = "SHA-256";
196
+ /**
197
+ * Interfaces for JSON Web Key objects, as per [RFC7517](https://www.rfc-editor.org/rfc/rfc751).
198
+ */
199
+ export interface JsonWebKey {
200
+ /**
201
+ * Key type.
202
+ */
203
+ kty: string;
204
+ /**
205
+ * Key ID.
206
+ */
207
+ kid?: string;
208
+ }
209
+ export interface JsonWebKeyECPublic extends JsonWebKey {
210
+ /**
211
+ * Elliptic curve identifier.
212
+ */
213
+ crv: string;
214
+ /**
215
+ * Base64url-encoded x coordinate.
216
+ */
217
+ x: string;
218
+ /**
219
+ * Base64url-encoded y coordinate.
220
+ */
221
+ y: string;
222
+ }
223
+ export interface JsonWebKeyECPrivate extends JsonWebKeyECPublic {
224
+ /**
225
+ * Base64url-encoded d coordinate.
226
+ */
227
+ d: string;
228
+ }
229
+ export interface JsonWebKeyRSAPublic extends JsonWebKey {
230
+ /**
231
+ * Base64url-encoded modulus.
232
+ */
233
+ n: string;
234
+ /**
235
+ * Base64url-encoded exponent.
236
+ */
237
+ e: string;
238
+ }
239
+ export interface JsonWebKeyRSAPrivate extends JsonWebKeyRSAPublic {
240
+ /**
241
+ * Private exponent.
242
+ */
243
+ d: string;
244
+ /**
245
+ * Additional exponents.
246
+ */
247
+ p: string;
248
+ q: string;
249
+ dp: string;
250
+ dq: string;
251
+ qi: string;
252
+ }
196
253
  export interface CCFCrypto {
197
254
  /**
198
255
  * Returns whether digital signature is valid.
@@ -205,6 +262,83 @@ export interface CCFCrypto {
205
262
  * signing algorithm or if an unknown algorithm is used.
206
263
  */
207
264
  verifySignature(algorithm: SigningAlgorithm, key: string, signature: ArrayBuffer, data: ArrayBuffer): boolean;
265
+ /**
266
+ * Generate an AES key.
267
+ *
268
+ * @param size The length in bits of the key to generate. 128, 192, or 256.
269
+ */
270
+ generateAesKey(size: number): ArrayBuffer;
271
+ /**
272
+ * Generate an RSA key pair.
273
+ *
274
+ * @param size The length in bits of the RSA modulus. Minimum: 2048.
275
+ * @param exponent The public exponent. Default: 65537.
276
+ */
277
+ generateRsaKeyPair(size: number, exponent?: number): CryptoKeyPair;
278
+ /**
279
+ * Generate an ECDSA key pair.
280
+ *
281
+ * @param curve The name of the curve, one of "secp256r1", "secp256k1", "secp384r1".
282
+ */
283
+ generateEcdsaKeyPair(curve: string): CryptoKeyPair;
284
+ /**
285
+ * Generate an EdDSA key pair.
286
+ *
287
+ * @param curve The name of the curve. Currently only "curve25519" is supported.
288
+ */
289
+ generateEddsaKeyPair(curve: string): CryptoKeyPair;
290
+ /**
291
+ * Wraps a key using a wrapping key.
292
+ *
293
+ * Constraints on the `key` and `wrappingKey` parameters depend
294
+ * on the wrapping algorithm that is used (`wrapAlgo`).
295
+ */
296
+ wrapKey(key: ArrayBuffer, wrappingKey: ArrayBuffer, wrapAlgo: WrapAlgoParams): ArrayBuffer;
297
+ /**
298
+ * Generate a digest (hash) of the given data.
299
+ */
300
+ digest(algorithm: DigestAlgorithm, data: ArrayBuffer): ArrayBuffer;
301
+ /**
302
+ * Returns whether a string is a PEM-encoded bundle of X.509 certificates.
303
+ *
304
+ * A bundle consists of one or more certificates.
305
+ * Certificates in the bundle do not have to be related to each other.
306
+ * Validation is only syntactical, properties like validity dates are not evaluated.
307
+ */
308
+ isValidX509CertBundle(pem: string): boolean;
309
+ /**
310
+ * Returns whether a certificate chain is valid given a set of trusted certificates.
311
+ * The chain and trusted certificates are PEM-encoded bundles of X.509 certificates.
312
+ */
313
+ isValidX509CertChain(chain: string, trusted: string): boolean;
314
+ /**
315
+ * Converts an elliptic curve public key as PEM to JSON Web Key (JWK) object.
316
+ *
317
+ * @param pem Elliptic curve public key as PEM
318
+ * @param kid Key identifier (optional)
319
+ */
320
+ pubPemToJwk(pem: string, kid?: string): JsonWebKeyECPublic;
321
+ /**
322
+ * Converts an elliptic curve private key as PEM to JSON Web Key (JWK) object.
323
+ *
324
+ * @param pem Elliptic curve private key as PEM
325
+ * @param kid Key identifier (optional)
326
+ */
327
+ pemToJwk(pem: string, kid?: string): JsonWebKeyECPrivate;
328
+ /**
329
+ * Converts an RSA public key as PEM to JSON Web Key (JWK) object.
330
+ *
331
+ * @param pem RSA public key as PEM
332
+ * @param kid Key identifier (optional)
333
+ */
334
+ pubRsaPemToJwk(pem: string, kid?: string): JsonWebKeyRSAPublic;
335
+ /**
336
+ * Converts an RSA private key as PEM to JSON Web Key (JWK) object.
337
+ *
338
+ * @param pem RSA private key as PEM
339
+ * @param kid Key identifier (optional)
340
+ */
341
+ rsaPemToJwk(pem: string, kid?: string): JsonWebKeyRSAPrivate;
208
342
  }
209
343
  export interface CCFRpc {
210
344
  /**
@@ -309,46 +443,38 @@ export interface CCF {
309
443
  */
310
444
  bufToJsonCompatible<T extends JsonCompatible<T>>(v: ArrayBuffer): T;
311
445
  /**
312
- * Generate an AES key.
313
- *
314
- * @param size The length in bits of the key to generate. 128, 192, or 256.
446
+ * @deprecated This method has been moved to ccf.crypto namespace
447
+ * @see crypto.generateAesKey
315
448
  */
316
449
  generateAesKey(size: number): ArrayBuffer;
317
450
  /**
318
- * Generate an RSA key pair.
319
- *
320
- * @param size The length in bits of the RSA modulus. Minimum: 2048.
321
- * @param exponent The public exponent. Default: 65537.
451
+ * @deprecated This method has been moved to ccf.crypto namespace
452
+ * @see crypto.generateRsaKeyPair
322
453
  */
323
454
  generateRsaKeyPair(size: number, exponent?: number): CryptoKeyPair;
324
455
  /**
325
- * Generate an ECDSA key pair.
326
- *
327
- * @param curve The name of the curve, one of "secp256r1", "secp384r1".
456
+ * @deprecated This method has been moved to ccf.crypto namespace
457
+ * @see crypto.generateEcdsaKeyPair
328
458
  */
329
459
  generateEcdsaKeyPair(curve: string): CryptoKeyPair;
330
460
  /**
331
- * Wraps a key using a wrapping key.
332
- *
333
- * Constraints on the `key` and `wrappingKey` parameters depend
334
- * on the wrapping algorithm that is used (`wrapAlgo`).
461
+ * @deprecated This method has been moved to ccf.crypto namespace
462
+ * @see crypto.wrapKey
335
463
  */
336
464
  wrapKey(key: ArrayBuffer, wrappingKey: ArrayBuffer, wrapAlgo: WrapAlgoParams): ArrayBuffer;
337
465
  /**
338
- * Generate a digest (hash) of the given data.
466
+ * @deprecated This method has been moved to ccf.crypto namespace
467
+ * @see crypto.digest
339
468
  */
340
469
  digest(algorithm: DigestAlgorithm, data: ArrayBuffer): ArrayBuffer;
341
470
  /**
342
- * Returns whether a string is a PEM-encoded bundle of X.509 certificates.
343
- *
344
- * A bundle consists of one or more certificates.
345
- * Certificates in the bundle do not have to be related to each other.
346
- * Validation is only syntactical, properties like validity dates are not evaluated.
471
+ * @deprecated
472
+ * @see crypto.isValidX509CertBundle
347
473
  */
348
474
  isValidX509CertBundle(pem: string): boolean;
349
475
  /**
350
- * Returns whether a certificate chain is valid given a set of trusted certificates.
351
- * The chain and trusted certificates are PEM-encoded bundles of X.509 certificates.
476
+ * @deprecated This method has been moved to ccf.crypto namespace
477
+ * @see crypto.isValidX509CertChain
352
478
  */
353
479
  isValidX509CertChain(chain: string, trusted: string): boolean;
354
480
  crypto: CCFCrypto;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microsoft/ccf-app",
3
- "version": "3.0.0-dev5",
3
+ "version": "3.0.0-rc0",
4
4
  "description": "CCF app support package",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -19,12 +19,14 @@
19
19
  "license": "Apache-2.0",
20
20
  "devDependencies": {
21
21
  "@types/chai": "^4.2.15",
22
+ "@types/jsrsasign": "^10.5.4",
22
23
  "@types/mocha": "^10.0.0",
23
24
  "@types/node": "^18.0.0",
24
25
  "@types/node-forge": "^1.0.0",
25
26
  "chai": "^4.3.4",
26
27
  "colors": "1.4.0",
27
28
  "cross-env": "^7.0.3",
29
+ "jsrsasign": "^10.5.27",
28
30
  "mocha": "^10.0.0",
29
31
  "node-forge": "^1.2.0",
30
32
  "ts-node": "^10.4.0",
package/polyfill.js CHANGED
@@ -14,8 +14,9 @@
14
14
  *
15
15
  * @module
16
16
  */
17
- import * as crypto from "crypto";
17
+ import * as jscrypto from "crypto";
18
18
  import { TextEncoder, TextDecoder } from "util";
19
+ import * as rs from "jsrsasign";
19
20
  // JavaScript's Map uses reference equality for non-primitive types,
20
21
  // whereas CCF compares the content of the ArrayBuffer.
21
22
  // To achieve CCF's semantics, all keys are base64-encoded.
@@ -93,10 +94,10 @@ class CCFPolyfill {
93
94
  this.crypto = {
94
95
  verifySignature(algorithm, key, signature, data) {
95
96
  let padding = undefined;
96
- const pubKey = crypto.createPublicKey(key);
97
+ const pubKey = jscrypto.createPublicKey(key);
97
98
  if (pubKey.asymmetricKeyType == "rsa") {
98
99
  if (algorithm.name === "RSASSA-PKCS1-v1_5") {
99
- padding = crypto.constants.RSA_PKCS1_PADDING;
100
+ padding = jscrypto.constants.RSA_PKCS1_PADDING;
100
101
  }
101
102
  else {
102
103
  throw new Error("incompatible signing algorithm for given key type");
@@ -111,7 +112,7 @@ class CCFPolyfill {
111
112
  throw new Error("unrecognized signing algorithm");
112
113
  }
113
114
  const hashAlg = algorithm.hash.replace("-", "").toLowerCase();
114
- const verifier = crypto.createVerify(hashAlg);
115
+ const verifier = jscrypto.createVerify(hashAlg);
115
116
  verifier.update(new Uint8Array(data));
116
117
  return verifier.verify({
117
118
  key: pubKey,
@@ -119,6 +120,189 @@ class CCFPolyfill {
119
120
  padding: padding,
120
121
  }, new Uint8Array(signature));
121
122
  },
123
+ generateAesKey(size) {
124
+ return nodeBufToArrBuf(jscrypto.randomBytes(size / 8));
125
+ },
126
+ generateRsaKeyPair(size, exponent) {
127
+ const rsaKeyPair = jscrypto.generateKeyPairSync("rsa", {
128
+ modulusLength: size,
129
+ publicExponent: exponent,
130
+ publicKeyEncoding: {
131
+ type: "spki",
132
+ format: "pem",
133
+ },
134
+ privateKeyEncoding: {
135
+ type: "pkcs8",
136
+ format: "pem",
137
+ },
138
+ });
139
+ return rsaKeyPair;
140
+ },
141
+ generateEcdsaKeyPair(curve) {
142
+ var curve_name = curve;
143
+ if (curve == "secp256r1")
144
+ curve_name = "prime256v1";
145
+ const ecdsaKeyPair = jscrypto.generateKeyPairSync("ec", {
146
+ namedCurve: curve_name,
147
+ publicKeyEncoding: {
148
+ type: "spki",
149
+ format: "pem",
150
+ },
151
+ privateKeyEncoding: {
152
+ type: "pkcs8",
153
+ format: "pem",
154
+ },
155
+ });
156
+ return ecdsaKeyPair;
157
+ },
158
+ generateEddsaKeyPair(curve) {
159
+ // `type` is always "ed25519" because currently only "curve25519" is supported for `curve`.
160
+ const type = "ed25519";
161
+ const ecdsaKeyPair = jscrypto.generateKeyPairSync(type, {
162
+ publicKeyEncoding: {
163
+ type: "spki",
164
+ format: "pem",
165
+ },
166
+ privateKeyEncoding: {
167
+ type: "pkcs8",
168
+ format: "pem",
169
+ },
170
+ });
171
+ return ecdsaKeyPair;
172
+ },
173
+ wrapKey(key, wrappingKey, parameters) {
174
+ if (parameters.name === "RSA-OAEP") {
175
+ return nodeBufToArrBuf(jscrypto.publicEncrypt({
176
+ key: Buffer.from(wrappingKey),
177
+ oaepHash: "sha256",
178
+ oaepLabel: parameters.label
179
+ ? new Uint8Array(parameters.label)
180
+ : undefined,
181
+ padding: jscrypto.constants.RSA_PKCS1_OAEP_PADDING,
182
+ }, new Uint8Array(key)));
183
+ }
184
+ else if (parameters.name === "AES-KWP") {
185
+ const iv = Buffer.from("A65959A6", "hex"); // defined in RFC 5649
186
+ const cipher = jscrypto.createCipheriv("id-aes256-wrap-pad", new Uint8Array(wrappingKey), iv);
187
+ return nodeBufToArrBuf(Buffer.concat([cipher.update(new Uint8Array(key)), cipher.final()]));
188
+ }
189
+ else if (parameters.name === "RSA-OAEP-AES-KWP") {
190
+ const randomAesKey = this.generateAesKey(parameters.aesKeySize);
191
+ const wrap1 = this.wrapKey(randomAesKey, wrappingKey, {
192
+ name: "RSA-OAEP",
193
+ label: parameters.label,
194
+ });
195
+ const wrap2 = this.wrapKey(key, randomAesKey, {
196
+ name: "AES-KWP",
197
+ });
198
+ return nodeBufToArrBuf(Buffer.concat([Buffer.from(wrap1), Buffer.from(wrap2)]));
199
+ }
200
+ else {
201
+ throw new Error("unsupported wrapAlgo.name");
202
+ }
203
+ },
204
+ digest(algorithm, data) {
205
+ if (algorithm === "SHA-256") {
206
+ return nodeBufToArrBuf(jscrypto.createHash("sha256").update(new Uint8Array(data)).digest());
207
+ }
208
+ else {
209
+ throw new Error("unsupported algorithm");
210
+ }
211
+ },
212
+ isValidX509CertBundle(pem) {
213
+ if ("X509Certificate" in jscrypto) {
214
+ const sep = "-----END CERTIFICATE-----";
215
+ const items = pem.split(sep);
216
+ if (items.length === 1) {
217
+ return false;
218
+ }
219
+ const pems = items.slice(0, -1).map((p) => p + sep);
220
+ for (const [i, p] of pems.entries()) {
221
+ try {
222
+ new jscrypto.X509Certificate(p);
223
+ }
224
+ catch (e) {
225
+ console.error(`cert ${i} is not valid: ${e.message}`);
226
+ console.error(p);
227
+ return false;
228
+ }
229
+ }
230
+ return true;
231
+ }
232
+ else {
233
+ throw new Error("X509 validation unsupported, Node.js version too old (< 15.6.0)");
234
+ }
235
+ },
236
+ isValidX509CertChain(chain, trusted) {
237
+ if (!("X509Certificate" in jscrypto)) {
238
+ throw new Error("X509 validation unsupported, Node.js version too old (< 15.6.0)");
239
+ }
240
+ try {
241
+ const toX509Array = (pem) => {
242
+ const sep = "-----END CERTIFICATE-----";
243
+ const items = pem.split(sep);
244
+ if (items.length === 1) {
245
+ return [];
246
+ }
247
+ const pems = items.slice(0, -1).map((p) => p + sep);
248
+ const arr = pems.map((pem) => new jscrypto.X509Certificate(pem));
249
+ return arr;
250
+ };
251
+ const certsChain = toX509Array(chain);
252
+ const certsTrusted = toX509Array(trusted);
253
+ if (certsChain.length === 0) {
254
+ throw new Error("chain cannot be empty");
255
+ }
256
+ for (let i = 0; i < certsChain.length - 1; i++) {
257
+ if (!certsChain[i].checkIssued(certsChain[i + 1])) {
258
+ throw new Error(`chain[${i}] is not issued by chain[${i + 1}]`);
259
+ }
260
+ }
261
+ for (const certChain of certsChain) {
262
+ for (const certTrusted of certsTrusted) {
263
+ if (certChain.fingerprint === certTrusted.fingerprint) {
264
+ return true;
265
+ }
266
+ if (certChain.verify(certTrusted.publicKey)) {
267
+ return true;
268
+ }
269
+ }
270
+ }
271
+ throw new Error("none of the chain certificates are identical to or issued by a trusted certificate");
272
+ }
273
+ catch (e) {
274
+ console.error(`certificate chain validation failed: ${e.message}`);
275
+ return false;
276
+ }
277
+ },
278
+ pubPemToJwk(pem, kid) {
279
+ let jwk = rs.KEYUTIL.getJWK(rs.KEYUTIL.getKey(pem));
280
+ if (kid !== undefined) {
281
+ jwk.kid = kid;
282
+ }
283
+ return jwk;
284
+ },
285
+ pemToJwk(pem, kid) {
286
+ let jwk = rs.KEYUTIL.getJWK(rs.KEYUTIL.getKey(pem));
287
+ if (kid !== undefined) {
288
+ jwk.kid = kid;
289
+ }
290
+ return jwk;
291
+ },
292
+ pubRsaPemToJwk(pem, kid) {
293
+ let jwk = rs.KEYUTIL.getJWK(rs.KEYUTIL.getKey(pem));
294
+ if (kid !== undefined) {
295
+ jwk.kid = kid;
296
+ }
297
+ return jwk;
298
+ },
299
+ rsaPemToJwk(pem, kid) {
300
+ let jwk = rs.KEYUTIL.getJWK(rs.KEYUTIL.getKey(pem));
301
+ if (kid !== undefined) {
302
+ jwk.kid = kid;
303
+ }
304
+ return jwk;
305
+ },
122
306
  };
123
307
  }
124
308
  strToBuf(s) {
@@ -134,144 +318,25 @@ class CCFPolyfill {
134
318
  return JSON.parse(this.bufToStr(v));
135
319
  }
136
320
  generateAesKey(size) {
137
- return nodeBufToArrBuf(crypto.randomBytes(size / 8));
321
+ return this.crypto.generateAesKey(size);
138
322
  }
139
323
  generateRsaKeyPair(size, exponent) {
140
- const rsaKeyPair = crypto.generateKeyPairSync("rsa", {
141
- modulusLength: size,
142
- publicExponent: exponent,
143
- publicKeyEncoding: {
144
- type: "spki",
145
- format: "pem",
146
- },
147
- privateKeyEncoding: {
148
- type: "pkcs8",
149
- format: "pem",
150
- },
151
- });
152
- return rsaKeyPair;
324
+ return this.crypto.generateRsaKeyPair(size, exponent);
153
325
  }
154
326
  generateEcdsaKeyPair(curve) {
155
- var curve_name = curve;
156
- if (curve == "secp256r1")
157
- curve_name = "prime256v1";
158
- const ecdsaKeyPair = crypto.generateKeyPairSync("ec", {
159
- namedCurve: curve_name,
160
- publicKeyEncoding: {
161
- type: "spki",
162
- format: "pem",
163
- },
164
- privateKeyEncoding: {
165
- type: "pkcs8",
166
- format: "pem",
167
- },
168
- });
169
- return ecdsaKeyPair;
327
+ return this.crypto.generateEcdsaKeyPair(curve);
170
328
  }
171
329
  wrapKey(key, wrappingKey, parameters) {
172
- if (parameters.name === "RSA-OAEP") {
173
- return nodeBufToArrBuf(crypto.publicEncrypt({
174
- key: Buffer.from(wrappingKey),
175
- oaepHash: "sha256",
176
- oaepLabel: parameters.label
177
- ? new Uint8Array(parameters.label)
178
- : undefined,
179
- padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
180
- }, new Uint8Array(key)));
181
- }
182
- else if (parameters.name === "AES-KWP") {
183
- const iv = Buffer.from("A65959A6", "hex"); // defined in RFC 5649
184
- const cipher = crypto.createCipheriv("id-aes256-wrap-pad", new Uint8Array(wrappingKey), iv);
185
- return nodeBufToArrBuf(Buffer.concat([cipher.update(new Uint8Array(key)), cipher.final()]));
186
- }
187
- else if (parameters.name === "RSA-OAEP-AES-KWP") {
188
- const randomAesKey = this.generateAesKey(parameters.aesKeySize);
189
- const wrap1 = this.wrapKey(randomAesKey, wrappingKey, {
190
- name: "RSA-OAEP",
191
- label: parameters.label,
192
- });
193
- const wrap2 = this.wrapKey(key, randomAesKey, {
194
- name: "AES-KWP",
195
- });
196
- return nodeBufToArrBuf(Buffer.concat([Buffer.from(wrap1), Buffer.from(wrap2)]));
197
- }
198
- else {
199
- throw new Error("unsupported wrapAlgo.name");
200
- }
330
+ return this.crypto.wrapKey(key, wrappingKey, parameters);
201
331
  }
202
332
  digest(algorithm, data) {
203
- if (algorithm === "SHA-256") {
204
- return nodeBufToArrBuf(crypto.createHash("sha256").update(new Uint8Array(data)).digest());
205
- }
206
- else {
207
- throw new Error("unsupported algorithm");
208
- }
333
+ return this.crypto.digest(algorithm, data);
209
334
  }
210
335
  isValidX509CertBundle(pem) {
211
- if ("X509Certificate" in crypto) {
212
- const sep = "-----END CERTIFICATE-----";
213
- const items = pem.split(sep);
214
- if (items.length === 1) {
215
- return false;
216
- }
217
- const pems = items.slice(0, -1).map((p) => p + sep);
218
- for (const [i, p] of pems.entries()) {
219
- try {
220
- new crypto.X509Certificate(p);
221
- }
222
- catch (e) {
223
- console.error(`cert ${i} is not valid: ${e.message}`);
224
- console.error(p);
225
- return false;
226
- }
227
- }
228
- return true;
229
- }
230
- else {
231
- throw new Error("X509 validation unsupported, Node.js version too old (< 15.6.0)");
232
- }
336
+ return this.crypto.isValidX509CertBundle(pem);
233
337
  }
234
338
  isValidX509CertChain(chain, trusted) {
235
- if (!("X509Certificate" in crypto)) {
236
- throw new Error("X509 validation unsupported, Node.js version too old (< 15.6.0)");
237
- }
238
- try {
239
- const toX509Array = (pem) => {
240
- const sep = "-----END CERTIFICATE-----";
241
- const items = pem.split(sep);
242
- if (items.length === 1) {
243
- return [];
244
- }
245
- const pems = items.slice(0, -1).map((p) => p + sep);
246
- const arr = pems.map((pem) => new crypto.X509Certificate(pem));
247
- return arr;
248
- };
249
- const certsChain = toX509Array(chain);
250
- const certsTrusted = toX509Array(trusted);
251
- if (certsChain.length === 0) {
252
- throw new Error("chain cannot be empty");
253
- }
254
- for (let i = 0; i < certsChain.length - 1; i++) {
255
- if (!certsChain[i].checkIssued(certsChain[i + 1])) {
256
- throw new Error(`chain[${i}] is not issued by chain[${i + 1}]`);
257
- }
258
- }
259
- for (const certChain of certsChain) {
260
- for (const certTrusted of certsTrusted) {
261
- if (certChain.fingerprint === certTrusted.fingerprint) {
262
- return true;
263
- }
264
- if (certChain.verify(certTrusted.publicKey)) {
265
- return true;
266
- }
267
- }
268
- }
269
- throw new Error("none of the chain certificates are identical to or issued by a trusted certificate");
270
- }
271
- catch (e) {
272
- console.error(`certificate chain validation failed: ${e.message}`);
273
- return false;
274
- }
339
+ return this.crypto.isValidX509CertChain(chain, trusted);
275
340
  }
276
341
  }
277
342
  globalThis.ccf = new CCFPolyfill();