@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 +9 -1
- package/crypto.js +8 -0
- package/global.d.ts +63 -0
- package/global.js +1 -0
- package/kv.d.ts +2 -0
- package/kv.js +6 -0
- package/openenclave.d.ts +5 -0
- package/openenclave.js +12 -0
- package/package.json +1 -1
- package/polyfill.js +84 -0
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
|
-
|
|
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
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,
|
package/openenclave.d.ts
ADDED
|
@@ -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
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);
|