@microsoft/ccf-app 5.0.0-dev1 → 5.0.0-dev10

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/crypto.d.ts CHANGED
@@ -11,13 +11,17 @@ export declare const generateRsaKeyPair: (size: number, exponent?: number | unde
11
11
  */
12
12
  export declare const generateEcdsaKeyPair: (curve: string) => import("./global.js").CryptoKeyPair;
13
13
  /**
14
- * @inheritDoc global!CCFCrypto.generateEcdsaKeyPair
14
+ * @inheritDoc global!CCFCrypto.generateEddsaKeyPair
15
15
  */
16
16
  export declare const generateEddsaKeyPair: (curve: string) => import("./global.js").CryptoKeyPair;
17
17
  /**
18
18
  * @inheritDoc global!CCFCrypto.wrapKey
19
19
  */
20
20
  export declare const wrapKey: (key: ArrayBuffer, wrappingKey: ArrayBuffer, wrapAlgo: import("./global.js").WrapAlgoParams) => ArrayBuffer;
21
+ /**
22
+ * @inheritDoc global!CCFCrypto.unwrapKey
23
+ */
24
+ export declare const unwrapKey: (key: ArrayBuffer, wrappingKey: ArrayBuffer, wrapAlgo: import("./global.js").WrapAlgoParams) => ArrayBuffer;
21
25
  /**
22
26
  * @inheritDoc global!CCFCrypto.sign
23
27
  */
package/crypto.js CHANGED
@@ -27,13 +27,17 @@ export const generateRsaKeyPair = ccf.crypto.generateRsaKeyPair;
27
27
  */
28
28
  export const generateEcdsaKeyPair = ccf.crypto.generateEcdsaKeyPair;
29
29
  /**
30
- * @inheritDoc global!CCFCrypto.generateEcdsaKeyPair
30
+ * @inheritDoc global!CCFCrypto.generateEddsaKeyPair
31
31
  */
32
32
  export const generateEddsaKeyPair = ccf.crypto.generateEddsaKeyPair;
33
33
  /**
34
34
  * @inheritDoc global!CCFCrypto.wrapKey
35
35
  */
36
36
  export const wrapKey = ccf.crypto.wrapKey;
37
+ /**
38
+ * @inheritDoc global!CCFCrypto.unwrapKey
39
+ */
40
+ export const unwrapKey = ccf.crypto.unwrapKey;
37
41
  /**
38
42
  * @inheritDoc global!CCFCrypto.sign
39
43
  */
package/global.d.ts CHANGED
@@ -298,7 +298,7 @@ export interface CCFCrypto {
298
298
  /**
299
299
  * Generate an EdDSA key pair.
300
300
  *
301
- * @param curve The name of the curve. Currently only "curve25519" is supported.
301
+ * @param curve The name of the curve. Only "curve25519" and "x25519" are supported.
302
302
  */
303
303
  generateEddsaKeyPair(curve: string): CryptoKeyPair;
304
304
  /**
@@ -308,6 +308,13 @@ export interface CCFCrypto {
308
308
  * on the wrapping algorithm that is used (`wrapAlgo`).
309
309
  */
310
310
  wrapKey(key: ArrayBuffer, wrappingKey: ArrayBuffer, wrapAlgo: WrapAlgoParams): ArrayBuffer;
311
+ /**
312
+ * Unwraps a key using a wrapping key.
313
+ *
314
+ * Constraints on the `key` and `wrappingKey` parameters depend
315
+ * on the wrapping algorithm that is used (`wrapAlgo`).
316
+ */
317
+ unwrapKey(key: ArrayBuffer, wrappingKey: ArrayBuffer, wrapAlgo: WrapAlgoParams): ArrayBuffer;
311
318
  /**
312
319
  * Generate a digest (hash) of the given data.
313
320
  */
@@ -355,7 +362,7 @@ export interface CCFCrypto {
355
362
  rsaPemToJwk(pem: string, kid?: string): JsonWebKeyRSAPrivate;
356
363
  /**
357
364
  * Converts an EdDSA public key as PEM to JSON Web Key (JWK) object.
358
- * Currently only Curve25519 is supported.
365
+ * Only Curve25519 and X25519 are supported.
359
366
  *
360
367
  * @param pem EdDSA public key as PEM
361
368
  * @param kid Key identifier (optional)
@@ -363,7 +370,7 @@ export interface CCFCrypto {
363
370
  pubEddsaPemToJwk(pem: string, kid?: string): JsonWebKeyEdDSAPublic;
364
371
  /**
365
372
  * Converts an EdDSA private key as PEM to JSON Web Key (JWK) object.
366
- * Currently only Curve25519 is supported.
373
+ * Only Curve25519 and X25519 are supported.
367
374
  *
368
375
  * @param pem EdDSA private key as PEM
369
376
  * @param kid Key identifier (optional)
@@ -608,3 +615,67 @@ export interface OpenEnclave {
608
615
  */
609
616
  verifyOpenEnclaveEvidence(format: string | undefined, evidence: ArrayBuffer, endorsements?: ArrayBuffer): EvidenceClaims;
610
617
  }
618
+ export interface TcbVersion {
619
+ boot_loader: number;
620
+ tee: number;
621
+ snp: number;
622
+ microcode: number;
623
+ }
624
+ export interface SnpAttestationResult {
625
+ attestation: {
626
+ version: number;
627
+ guest_svn: number;
628
+ policy: {
629
+ abi_minor: number;
630
+ abi_major: number;
631
+ smt: number;
632
+ migrate_ma: number;
633
+ debug: number;
634
+ single_socket: number;
635
+ };
636
+ family_id: ArrayBuffer;
637
+ image_id: ArrayBuffer;
638
+ vmpl: number;
639
+ signature_algo: number;
640
+ platform_version: TcbVersion;
641
+ platform_info: {
642
+ smt_en: number;
643
+ tsme_en: number;
644
+ };
645
+ flags: {
646
+ author_key_en: number;
647
+ mask_chip_key: number;
648
+ signing_key: number;
649
+ };
650
+ report_data: ArrayBuffer;
651
+ measurement: ArrayBuffer;
652
+ host_data: ArrayBuffer;
653
+ id_key_digest: ArrayBuffer;
654
+ author_key_digest: ArrayBuffer;
655
+ report_id: ArrayBuffer;
656
+ report_id_ma: ArrayBuffer;
657
+ reported_tcb: TcbVersion;
658
+ chip_id: ArrayBuffer;
659
+ committed_tcb: TcbVersion;
660
+ current_minor: number;
661
+ current_build: number;
662
+ current_major: number;
663
+ committed_build: number;
664
+ committed_minor: number;
665
+ committed_major: number;
666
+ launch_tcb: TcbVersion;
667
+ signature: {
668
+ r: ArrayBuffer;
669
+ s: ArrayBuffer;
670
+ };
671
+ };
672
+ uvm_endorsements?: {
673
+ did: string;
674
+ feed: string;
675
+ svn: string;
676
+ };
677
+ }
678
+ export declare const snp_attestation: SnpAttestation;
679
+ export interface SnpAttestation {
680
+ verifySnpAttestation(evidence: ArrayBuffer, endorsements: ArrayBuffer, uvm_endorsements?: ArrayBuffer, endorsed_tcb?: string): SnpAttestationResult;
681
+ }
package/global.js CHANGED
@@ -21,3 +21,5 @@
21
21
  // This avoids polluting the global namespace.
22
22
  export const ccf = globalThis.ccf;
23
23
  export const openenclave = globalThis.openenclave;
24
+ export const snp_attestation = globalThis
25
+ .snp_attestation;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microsoft/ccf-app",
3
- "version": "5.0.0-dev1",
3
+ "version": "5.0.0-dev10",
4
4
  "description": "CCF app support package",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -25,10 +25,11 @@
25
25
  "chai": "^4.3.4",
26
26
  "colors": "1.4.0",
27
27
  "cross-env": "^7.0.3",
28
+ "get-func-name": "3.0.0",
28
29
  "mocha": "^10.0.0",
29
30
  "node-forge": "^1.2.0",
30
31
  "ts-node": "^10.4.0",
31
- "typedoc": "^0.24.1",
32
+ "typedoc": "^0.25.0",
32
33
  "typescript": "^5.0.2"
33
34
  }
34
35
  }
package/polyfill.js CHANGED
@@ -207,19 +207,32 @@ class CCFPolyfill {
207
207
  return ecdsaKeyPair;
208
208
  },
209
209
  generateEddsaKeyPair(curve) {
210
- // `type` is always "ed25519" because currently only "curve25519" is supported for `curve`.
211
- const type = "ed25519";
212
- const ecdsaKeyPair = jscrypto.generateKeyPairSync(type, {
213
- publicKeyEncoding: {
214
- type: "spki",
215
- format: "pem",
216
- },
217
- privateKeyEncoding: {
218
- type: "pkcs8",
219
- format: "pem",
220
- },
221
- });
222
- return ecdsaKeyPair;
210
+ if (curve === "curve25519") {
211
+ return jscrypto.generateKeyPairSync("ed25519", {
212
+ publicKeyEncoding: {
213
+ type: "spki",
214
+ format: "pem",
215
+ },
216
+ privateKeyEncoding: {
217
+ type: "pkcs8",
218
+ format: "pem",
219
+ },
220
+ });
221
+ }
222
+ else {
223
+ if (curve !== "x25519")
224
+ throw new Error("Unsupported curve for EdDSA key pair generation: " + curve);
225
+ return jscrypto.generateKeyPairSync("x25519", {
226
+ publicKeyEncoding: {
227
+ type: "spki",
228
+ format: "pem",
229
+ },
230
+ privateKeyEncoding: {
231
+ type: "pkcs8",
232
+ format: "pem",
233
+ },
234
+ });
235
+ }
223
236
  },
224
237
  wrapKey(key, wrappingKey, parameters) {
225
238
  if (parameters.name === "RSA-OAEP") {
@@ -252,6 +265,41 @@ class CCFPolyfill {
252
265
  throw new Error("unsupported wrapAlgo.name");
253
266
  }
254
267
  },
268
+ unwrapKey(wrappedKey, unwrappingKey, unwrapAlgo) {
269
+ if (unwrapAlgo.name == "RSA-OAEP") {
270
+ return nodeBufToArrBuf(jscrypto.privateDecrypt({
271
+ key: Buffer.from(unwrappingKey),
272
+ oaepHash: "sha256",
273
+ padding: jscrypto.constants.RSA_PKCS1_OAEP_PADDING,
274
+ }, new Uint8Array(wrappedKey)));
275
+ }
276
+ else if (unwrapAlgo.name == "AES-KWP") {
277
+ const iv = Buffer.from("A65959A6", "hex"); // defined in RFC 5649
278
+ const decipher = jscrypto.createDecipheriv("id-aes256-wrap-pad", new Uint8Array(unwrappingKey), iv);
279
+ return nodeBufToArrBuf(Buffer.concat([
280
+ decipher.update(new Uint8Array(wrappedKey)),
281
+ decipher.final(),
282
+ ]));
283
+ }
284
+ else if (unwrapAlgo.name == "RSA-OAEP-AES-KWP") {
285
+ const keyInfo = jscrypto.createPrivateKey(Buffer.from(unwrappingKey));
286
+ // asymmetricKeyDetails added in Node.js 15.7.0, we're at 16.
287
+ console.log(`Modulus length: `, keyInfo?.asymmetricKeyDetails?.modulusLength);
288
+ const modulusLengthInBytes = (keyInfo?.asymmetricKeyDetails?.modulusLength || 2048) / 8;
289
+ const wrap1 = wrappedKey.slice(0, modulusLengthInBytes);
290
+ const wrap2 = wrappedKey.slice(modulusLengthInBytes);
291
+ const aesKey = this.unwrapKey(wrap1, unwrappingKey, {
292
+ name: "RSA-OAEP",
293
+ label: unwrapAlgo.label,
294
+ });
295
+ return this.unwrapKey(wrap2, aesKey, {
296
+ name: "AES-KWP",
297
+ });
298
+ }
299
+ else {
300
+ throw new Error("unsupported unwrapAlgo.name");
301
+ }
302
+ },
255
303
  digest(algorithm, data) {
256
304
  if (algorithm === "SHA-256") {
257
305
  return nodeBufToArrBuf(jscrypto.createHash("sha256").update(new Uint8Array(data)).digest());
@@ -456,6 +504,9 @@ class CCFPolyfill {
456
504
  wrapKey(key, wrappingKey, parameters) {
457
505
  return this.crypto.wrapKey(key, wrappingKey, parameters);
458
506
  }
507
+ unwrapKey(key, wrappingKey, parameters) {
508
+ return this.crypto.unwrapKey(key, wrappingKey, parameters);
509
+ }
459
510
  digest(algorithm, data) {
460
511
  return this.crypto.digest(algorithm, data);
461
512
  }
@@ -479,6 +530,12 @@ class OpenEnclavePolyfill {
479
530
  }
480
531
  }
481
532
  globalThis.openenclave = new OpenEnclavePolyfill();
533
+ class SnpAttestationPolyfill {
534
+ verifySnpAttestation(evidence, endorsements, uvm_endorsements, endorsed_tcb) {
535
+ throw new Error("Method not implemented.");
536
+ }
537
+ }
538
+ globalThis.snp_attestation = new SnpAttestationPolyfill();
482
539
  function nodeBufToArrBuf(buf) {
483
540
  // Note: buf.buffer is not safe, see docs.
484
541
  const arrBuf = new ArrayBuffer(buf.byteLength);
@@ -0,0 +1,4 @@
1
+ /**
2
+ * @inheritDoc global!SnpAttestation.verifySnpAttestation
3
+ */
4
+ export declare const verifySnpAttestation: (evidence: ArrayBuffer, endorsements: ArrayBuffer, uvm_endorsements?: ArrayBuffer | undefined, endorsed_tcb?: string | undefined) => import("./global").SnpAttestationResult;
@@ -0,0 +1,12 @@
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the Apache 2.0 License.
3
+ /**
4
+ * The `snp_attestation` module provides SNP Attestation Validation.
5
+ *
6
+ * @module
7
+ */
8
+ import { snp_attestation } from "./global";
9
+ /**
10
+ * @inheritDoc global!SnpAttestation.verifySnpAttestation
11
+ */
12
+ export const verifySnpAttestation = snp_attestation.verifySnpAttestation;
package/textcodec.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ export type TextEncoderEncodeIntoResult = {
2
+ read?: number;
3
+ written?: number;
4
+ };
5
+ /**
6
+ * TextEncoder can be used to encode string to Uint8Array.
7
+ */
8
+ export declare class TextEncoder {
9
+ /**
10
+ * Always returns "utf-8".
11
+ */
12
+ readonly encoding: string;
13
+ /**
14
+ * Returns Uint8Array containing UTF-8 encoded text.
15
+ * @param input Input string to encode.
16
+ * @returns Encoded bytes.
17
+ */
18
+ encode(input: string): Uint8Array;
19
+ /**
20
+ * Not implemented.
21
+ * @param input
22
+ * @param output
23
+ * @throws Always throws an Error object.
24
+ */
25
+ encodeInto(input: string, output: Uint8Array): TextEncoderEncodeIntoResult;
26
+ }
package/textcodec.js ADDED
@@ -0,0 +1,53 @@
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the Apache 2.0 License.
3
+ /**
4
+ * The `textcodec` module provides access to TextEncoder Web API class.
5
+ *
6
+ * Example:
7
+ * ```
8
+ * import * as ccftextcodec from '@microsoft/ccf-app/textcodec.js';
9
+ *
10
+ * const bytes = new ccftextcodec.TextEncoder().encode("foo")
11
+ * ```
12
+ *
13
+ * If you need TextEncoder Web API as a globally accessible class:
14
+ * ```
15
+ * import * as ccftextcodec from '@microsoft/ccf-app/textcodec.js';
16
+ *
17
+ * if (globalThis != undefined && (globalThis as any).TextEncoder == undefined) {
18
+ * (globalThis as any).TextEncoder = ccftextcodec.TextEncoder;
19
+ * }
20
+ *
21
+ * ```
22
+ *
23
+ * @module
24
+ */
25
+ import { ccf } from "./global.js";
26
+ /**
27
+ * TextEncoder can be used to encode string to Uint8Array.
28
+ */
29
+ export class TextEncoder {
30
+ constructor() {
31
+ /**
32
+ * Always returns "utf-8".
33
+ */
34
+ this.encoding = "utf-8";
35
+ }
36
+ /**
37
+ * Returns Uint8Array containing UTF-8 encoded text.
38
+ * @param input Input string to encode.
39
+ * @returns Encoded bytes.
40
+ */
41
+ encode(input) {
42
+ return new Uint8Array(ccf.strToBuf(input));
43
+ }
44
+ /**
45
+ * Not implemented.
46
+ * @param input
47
+ * @param output
48
+ * @throws Always throws an Error object.
49
+ */
50
+ encodeInto(input, output) {
51
+ throw new Error("Not implemented");
52
+ }
53
+ }