@microsoft/ccf-app 1.0.10 → 2.0.0-dev4

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
@@ -10,6 +10,10 @@ export declare const generateRsaKeyPair: (size: number, exponent?: number | unde
10
10
  * @inheritDoc CCF.wrapKey
11
11
  */
12
12
  export declare const wrapKey: (key: ArrayBuffer, wrappingKey: ArrayBuffer, wrapAlgo: import("./global.js").WrapAlgoParams) => ArrayBuffer;
13
+ /**
14
+ * @inheritDoc CCF.crypto.verifySignature
15
+ */
16
+ export declare const verifySignature: (algorithm: import("./global.js").SigningAlgorithm, key: string, signature: ArrayBuffer, data: ArrayBuffer) => boolean;
13
17
  /**
14
18
  * @inheritDoc CCF.digest
15
19
  */
@@ -18,4 +22,8 @@ export declare const digest: (algorithm: "SHA-256", data: ArrayBuffer) => ArrayB
18
22
  * @inheritDoc CCF.isValidX509CertBundle
19
23
  */
20
24
  export declare const isValidX509CertBundle: (pem: string) => boolean;
21
- export { WrapAlgoParams, AesKwpParams, RsaOaepParams, RsaOaepAesKwpParams, CryptoKeyPair, DigestAlgorithm, } from "./global";
25
+ /**
26
+ * @inheritDoc CCF.isValidX509CertChain
27
+ */
28
+ export declare const isValidX509CertChain: (chain: string, trusted: string) => boolean;
29
+ export { WrapAlgoParams, AesKwpParams, RsaOaepParams, RsaOaepAesKwpParams, CryptoKeyPair, DigestAlgorithm, SigningAlgorithm, RsaPkcsParams, EcdsaParams, } from "./global";
package/crypto.js CHANGED
@@ -26,6 +26,10 @@ export const generateRsaKeyPair = ccf.generateRsaKeyPair;
26
26
  * @inheritDoc CCF.wrapKey
27
27
  */
28
28
  export const wrapKey = ccf.wrapKey;
29
+ /**
30
+ * @inheritDoc CCF.crypto.verifySignature
31
+ */
32
+ export const verifySignature = ccf.crypto.verifySignature;
29
33
  /**
30
34
  * @inheritDoc CCF.digest
31
35
  */
@@ -34,3 +38,7 @@ export const digest = ccf.digest;
34
38
  * @inheritDoc CCF.isValidX509CertBundle
35
39
  */
36
40
  export const isValidX509CertBundle = ccf.isValidX509CertBundle;
41
+ /**
42
+ * @inheritDoc CCF.isValidX509CertChain
43
+ */
44
+ export const isValidX509CertChain = ccf.isValidX509CertChain;
package/global.d.ts CHANGED
@@ -27,7 +27,9 @@ export interface KvMap {
27
27
  get(key: ArrayBuffer): ArrayBuffer | undefined;
28
28
  set(key: ArrayBuffer, value: ArrayBuffer): KvMap;
29
29
  delete(key: ArrayBuffer): boolean;
30
+ clear(): void;
30
31
  forEach(callback: (value: ArrayBuffer, key: ArrayBuffer, kvmap: KvMap) => void): void;
32
+ size: number;
31
33
  }
32
34
  /**
33
35
  * @inheritDoc CCF.kv
@@ -140,6 +142,26 @@ export interface CryptoKeyPair {
140
142
  */
141
143
  publicKey: string;
142
144
  }
145
+ /**
146
+ * RSASSA-PKCS1-v1_5 signature algorithm parameters.
147
+ */
148
+ export interface RsaPkcsParams {
149
+ name: "RSASSA-PKCS1-v1_5";
150
+ hash: DigestAlgorithm;
151
+ }
152
+ /**
153
+ * ECDSA signature algorithm parameters.
154
+ *
155
+ * Note: ECDSA signatures are assumed to be encoded according
156
+ * to the Web Crypto API specification, which is the same
157
+ * format used in JSON Web Tokens and more generally known
158
+ * as IEEE P1363 encoding.
159
+ */
160
+ export interface EcdsaParams {
161
+ name: "ECDSA";
162
+ hash: DigestAlgorithm;
163
+ }
164
+ export declare type SigningAlgorithm = RsaPkcsParams | EcdsaParams;
143
165
  export declare type DigestAlgorithm = "SHA-256";
144
166
  export interface CCF {
145
167
  /**
@@ -194,6 +216,24 @@ export interface CCF {
194
216
  * Validation is only syntactical, properties like validity dates are not evaluated.
195
217
  */
196
218
  isValidX509CertBundle(pem: string): boolean;
219
+ /**
220
+ * Returns whether a certificate chain is valid given a set of trusted certificates.
221
+ * The chain and trusted certificates are PEM-encoded bundles of X.509 certificates.
222
+ */
223
+ isValidX509CertChain(chain: string, trusted: string): boolean;
224
+ crypto: {
225
+ /**
226
+ * Returns whether digital signature is valid.
227
+ *
228
+ * @param algorithm Signing algorithm and parameters
229
+ * @param key A PEM-encoded public key or X.509 certificate
230
+ * @param signature Signature to verify
231
+ * @param data Data that was signed
232
+ * @throws Will throw an error if the key is not compatible with the
233
+ * signing algorithm or if an unknown algorithm is used.
234
+ */
235
+ verifySignature(algorithm: SigningAlgorithm, key: string, signature: ArrayBuffer, data: ArrayBuffer): boolean;
236
+ };
197
237
  rpc: {
198
238
  /**
199
239
  * Set whether KV writes should be applied even if the response status is not 2xx.
@@ -212,3 +252,26 @@ export interface CCF {
212
252
  */
213
253
  historicalState?: HistoricalState;
214
254
  }
255
+ export declare const openenclave: OpenEnclave;
256
+ export interface EvidenceClaims {
257
+ claims: {
258
+ [name: string]: ArrayBuffer;
259
+ };
260
+ customClaims: {
261
+ [name: string]: ArrayBuffer;
262
+ };
263
+ }
264
+ export interface OpenEnclave {
265
+ /**
266
+ * Verifies Open Enclave evidence and returns the claims of the evidence.
267
+ *
268
+ * @param format The optional format id of the evidence to be verified as
269
+ * a UUID of the form "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx".
270
+ * If this parameter is `undefined`, the evidence and endorsement
271
+ * must either contain data with an attestation header holding a valid
272
+ * format id, or be an Open Enclave report generated by the legacy API function
273
+ * `oe_get_report()`. Otherwise, this parameter must be a valid format id, and
274
+ * the evidence and endorsements data must not be wrapped with an attestation header.
275
+ */
276
+ verifyOpenEnclaveEvidence(format: string | undefined, evidence: ArrayBuffer, endorsements?: ArrayBuffer): EvidenceClaims;
277
+ }
package/global.js CHANGED
@@ -20,3 +20,4 @@
20
20
  // in a .d.ts definition file.
21
21
  // This avoids polluting the global namespace.
22
22
  export const ccf = globalThis.ccf;
23
+ export const openenclave = globalThis.openenclave;
package/kv.d.ts CHANGED
@@ -33,7 +33,9 @@ export declare class TypedKvMap<K, V> {
33
33
  get(key: K): V | undefined;
34
34
  set(key: K, value: V): TypedKvMap<K, V>;
35
35
  delete(key: K): boolean;
36
+ clear(): void;
36
37
  forEach(callback: (value: V, key: K, table: TypedKvMap<K, V>) => void): void;
38
+ get size(): number;
37
39
  }
38
40
  /**
39
41
  * Returns a typed view of a map in the Key-Value Store,
package/kv.js CHANGED
@@ -45,6 +45,9 @@ export class TypedKvMap {
45
45
  delete(key) {
46
46
  return this.kv.delete(this.kt.encode(key));
47
47
  }
48
+ clear() {
49
+ this.kv.clear();
50
+ }
48
51
  forEach(callback) {
49
52
  let kt = this.kt;
50
53
  let vt = this.vt;
@@ -53,6 +56,9 @@ export class TypedKvMap {
53
56
  callback(vt.decode(raw_v), kt.decode(raw_k), typedMap);
54
57
  });
55
58
  }
59
+ get size() {
60
+ return this.kv.size;
61
+ }
56
62
  }
57
63
  /**
58
64
  * Returns a typed view of a map in the Key-Value Store,
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @inheritDoc OpenEnclave.verifyOpenEnclaveEvidence
3
+ */
4
+ export declare const verifyOpenEnclaveEvidence: (format: string | undefined, evidence: ArrayBuffer, endorsements?: ArrayBuffer | undefined) => import("./global").EvidenceClaims;
5
+ export { EvidenceClaims } from "./global";
package/openenclave.js ADDED
@@ -0,0 +1,12 @@
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the Apache 2.0 License.
3
+ /**
4
+ * The `openenclave` module provides access to Open Enclave functionality.
5
+ *
6
+ * @module
7
+ */
8
+ import { openenclave } from "./global";
9
+ /**
10
+ * @inheritDoc OpenEnclave.verifyOpenEnclaveEvidence
11
+ */
12
+ export const verifyOpenEnclaveEvidence = openenclave.verifyOpenEnclaveEvidence;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microsoft/ccf-app",
3
- "version": "1.0.10",
3
+ "version": "2.0.0-dev4",
4
4
  "description": "CCF app support package",
5
5
  "main": "index.js",
6
6
  "files": [
package/polyfill.js CHANGED
@@ -36,11 +36,17 @@ class KvMapPolyfill {
36
36
  delete(key) {
37
37
  return this.map.delete(base64(key));
38
38
  }
39
+ clear() {
40
+ this.map.clear();
41
+ }
39
42
  forEach(callback) {
40
43
  this.map.forEach((value, key, _) => {
41
44
  callback(value, unbase64(key), this);
42
45
  });
43
46
  }
47
+ get size() {
48
+ return this.map.size;
49
+ }
44
50
  }
45
51
  class CCFPolyfill {
46
52
  constructor() {
@@ -59,6 +65,36 @@ class CCFPolyfill {
59
65
  throw new Error("Not implemented");
60
66
  },
61
67
  };
68
+ this.crypto = {
69
+ verifySignature(algorithm, key, signature, data) {
70
+ let padding = undefined;
71
+ const pubKey = crypto.createPublicKey(key);
72
+ if (pubKey.asymmetricKeyType == "rsa") {
73
+ if (algorithm.name === "RSASSA-PKCS1-v1_5") {
74
+ padding = crypto.constants.RSA_PKCS1_PADDING;
75
+ }
76
+ else {
77
+ throw new Error("incompatible signing algorithm for given key type");
78
+ }
79
+ }
80
+ else if (pubKey.asymmetricKeyType == "ec") {
81
+ if (algorithm.name !== "ECDSA") {
82
+ throw new Error("incompatible signing algorithm for given key type");
83
+ }
84
+ }
85
+ else {
86
+ throw new Error("unrecognized signing algorithm");
87
+ }
88
+ const hashAlg = algorithm.hash.replace("-", "").toLowerCase();
89
+ const verifier = crypto.createVerify(hashAlg);
90
+ verifier.update(new Uint8Array(data));
91
+ return verifier.verify({
92
+ key: pubKey,
93
+ dsaEncoding: "ieee-p1363",
94
+ padding: padding,
95
+ }, new Uint8Array(signature));
96
+ },
97
+ };
62
98
  }
63
99
  strToBuf(s) {
64
100
  return typedArrToArrBuf(new TextEncoder().encode(s));
@@ -153,8 +189,56 @@ class CCFPolyfill {
153
189
  throw new Error("X509 validation unsupported, Node.js version too old (< 15.6.0)");
154
190
  }
155
191
  }
192
+ isValidX509CertChain(chain, trusted) {
193
+ if (!("X509Certificate" in crypto)) {
194
+ throw new Error("X509 validation unsupported, Node.js version too old (< 15.6.0)");
195
+ }
196
+ try {
197
+ const toX509Array = (pem) => {
198
+ const sep = "-----END CERTIFICATE-----";
199
+ const items = pem.split(sep);
200
+ if (items.length === 1) {
201
+ return [];
202
+ }
203
+ const pems = items.slice(0, -1).map((p) => p + sep);
204
+ const arr = pems.map((pem) => new crypto.X509Certificate(pem));
205
+ return arr;
206
+ };
207
+ const certsChain = toX509Array(chain);
208
+ const certsTrusted = toX509Array(trusted);
209
+ if (certsChain.length === 0) {
210
+ throw new Error("chain cannot be empty");
211
+ }
212
+ for (let i = 0; i < certsChain.length - 1; i++) {
213
+ if (!certsChain[i].checkIssued(certsChain[i + 1])) {
214
+ throw new Error(`chain[${i}] is not issued by chain[${i + 1}]`);
215
+ }
216
+ }
217
+ for (const certChain of certsChain) {
218
+ for (const certTrusted of certsTrusted) {
219
+ if (certChain.fingerprint === certTrusted.fingerprint) {
220
+ return true;
221
+ }
222
+ if (certChain.verify(certTrusted.publicKey)) {
223
+ return true;
224
+ }
225
+ }
226
+ }
227
+ throw new Error("none of the chain certificates are identical to or issued by a trusted certificate");
228
+ }
229
+ catch (e) {
230
+ console.error(`certificate chain validation failed: ${e.message}`);
231
+ return false;
232
+ }
233
+ }
156
234
  }
157
235
  globalThis.ccf = new CCFPolyfill();
236
+ class OpenEnclavePolyfill {
237
+ verifyOpenEnclaveEvidence(format, evidence, endorsements) {
238
+ throw new Error("Method not implemented.");
239
+ }
240
+ }
241
+ globalThis.openenclave = new OpenEnclavePolyfill();
158
242
  function nodeBufToArrBuf(buf) {
159
243
  // Note: buf.buffer is not safe, see docs.
160
244
  const arrBuf = new ArrayBuffer(buf.byteLength);