UNPKG

cloudworker-proxy

Version:
334 lines (333 loc) 14 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const webcrypto = require("webcrypto-core"); const AlgorithmError = webcrypto.AlgorithmError; const WebCryptoError = webcrypto.WebCryptoError; const AlgorithmNames = webcrypto.AlgorithmNames; const BaseCrypto = webcrypto.BaseCrypto; const Base64Url = webcrypto.Base64Url; const key_1 = require("../key"); const native = require("../native"); const aes = require("./aes"); function nc2ssl(nc) { let namedCurve = ""; switch (nc.toUpperCase()) { case "P-192": namedCurve = "secp192r1"; break; case "P-256": namedCurve = "secp256r1"; break; case "P-384": namedCurve = "secp384r1"; break; case "P-521": namedCurve = "secp521r1"; break; case "K-256": namedCurve = "secp256k1"; break; default: throw new WebCryptoError("Unsupported namedCurve in use"); } return native.EcNamedCurves[namedCurve]; } function b64_decode(b64url) { return Buffer.from(Base64Url.decode(b64url)); } function buf_pad(buf, padSize = 0) { if (padSize && Buffer.length < padSize) { const pad = Buffer.from(new Uint8Array(padSize - buf.length).map((v) => 0)); return Buffer.concat([pad, buf]); } return buf; } class EcCrypto extends BaseCrypto { static generateKey(algorithm, extractable, keyUsages) { return new Promise((resolve, reject) => { const alg = algorithm; const namedCurve = nc2ssl(alg.namedCurve); native.Key.generateEc(namedCurve, (err, key) => { if (err) { reject(err); } else { const prvUsages = ["sign", "deriveKey", "deriveBits"] .filter((usage) => keyUsages.some((keyUsage) => keyUsage === usage)); const pubUsages = ["verify"] .filter((usage) => keyUsages.some((keyUsage) => keyUsage === usage)); resolve({ privateKey: new key_1.CryptoKey(key, algorithm, "private", extractable, prvUsages), publicKey: new key_1.CryptoKey(key, algorithm, "public", true, pubUsages), }); } }); }); } static importKey(format, keyData, algorithm, extractable, keyUsages) { return new Promise((resolve, reject) => { const formatLC = format.toLocaleLowerCase(); const alg = algorithm; const data = {}; let keyType = native.KeyType.PUBLIC; switch (formatLC) { case "raw": if (!Buffer.isBuffer(keyData)) { throw new WebCryptoError("ImportKey: keyData is not a Buffer"); } if (!alg.namedCurve) { throw new WebCryptoError("ImportKey: namedCurve property of algorithm parameter is required"); } let keyLength = 0; if (keyData.length === 65) { keyLength = 32; } else if (keyData.length === 97) { keyLength = 48; } else if (keyData.length === 133) { keyLength = 66; } const x = keyData.slice(1, keyLength + 1); const y = keyData.slice(keyLength + 1, (keyLength * 2) + 1); data["kty"] = Buffer.from("EC", "utf-8"); data["crv"] = nc2ssl(alg.namedCurve.toUpperCase()); data["x"] = b64_decode(Base64Url.encode(buf_pad(x, keyLength))); data["y"] = b64_decode(Base64Url.encode(buf_pad(y, keyLength))); native.Key.importJwk(data, keyType, (err, key) => { try { if (err) { reject(new WebCryptoError(`ImportKey: Cannot import key from JWK\n${err}`)); } else { const ec = new key_1.CryptoKey(key, alg, keyType ? "private" : "public", extractable, keyUsages); resolve(ec); } } catch (e) { reject(e); } }); break; case "jwk": const jwk = keyData; data["kty"] = jwk.kty; data["crv"] = nc2ssl(jwk.crv); data["x"] = b64_decode(jwk.x); data["y"] = b64_decode(jwk.y); if (jwk.d) { keyType = native.KeyType.PRIVATE; data["d"] = b64_decode(jwk.d); } native.Key.importJwk(data, keyType, (err, key) => { try { if (err) { reject(new WebCryptoError(`ImportKey: Cannot import key from JWK\n${err}`)); } else { const ec = new key_1.CryptoKey(key, alg, keyType ? "private" : "public", extractable, keyUsages); resolve(ec); } } catch (e) { reject(e); } }); break; case "pkcs8": case "spki": if (!Buffer.isBuffer(keyData)) { throw new WebCryptoError("ImportKey: keyData is not a Buffer"); } let importFunction = native.Key.importPkcs8; if (formatLC === "spki") { importFunction = native.Key.importSpki; } importFunction(keyData, (err, key) => { try { if (err) { reject(new WebCryptoError(`ImportKey: Can not import key for ${format}\n${err.message}`)); } else { const ec = new key_1.CryptoKey(key, alg, format.toLocaleLowerCase() === "spki" ? "public" : "private", extractable, keyUsages); resolve(ec); } } catch (e) { reject(e); } }); break; default: throw new WebCryptoError(`ImportKey: Wrong format value '${format}'`); } }); } static exportKey(format, key) { return new Promise((resolve, reject) => { const nativeKey = key.native; const type = key.type === "public" ? native.KeyType.PUBLIC : native.KeyType.PRIVATE; switch (format.toLocaleLowerCase()) { case "jwk": nativeKey.exportJwk(type, (err, data) => { try { const jwk = { kty: "EC" }; jwk.crv = key.algorithm.namedCurve; jwk.key_ops = key.usages; let padSize = 0; switch (jwk.crv) { case "P-256": case "K-256": padSize = 32; break; case "P-384": padSize = 48; break; case "P-521": padSize = 66; break; default: throw new Error(`Unsupported named curve '${jwk.crv}'`); } jwk.x = Base64Url.encode(buf_pad(data.x, padSize)); jwk.y = Base64Url.encode(buf_pad(data.y, padSize)); if (key.type === "private") { jwk.d = Base64Url.encode(buf_pad(data.d, padSize)); } resolve(jwk); } catch (e) { reject(e); } }); break; case "spki": nativeKey.exportSpki((err, raw) => { if (err) { reject(err); } else { resolve(raw.buffer); } }); break; case "pkcs8": nativeKey.exportPkcs8((err, raw) => { if (err) { reject(err); } else { resolve(raw.buffer); } }); break; case "raw": nativeKey.exportJwk(type, (err, data) => { if (err) { reject(err); } else { let padSize = 0; const crv = key.algorithm.namedCurve; switch (crv) { case "P-256": case "K-256": padSize = 32; break; case "P-384": padSize = 48; break; case "P-521": padSize = 66; break; default: throw new Error(`Unsupported named curve '${crv}'`); } const x = Base64Url.decode(Base64Url.encode(buf_pad(data.x, padSize))); const y = Base64Url.decode(Base64Url.encode(buf_pad(data.y, padSize))); const rawKey = new Uint8Array(1 + x.length + y.length); rawKey.set([4]); rawKey.set(x, 1); rawKey.set(y, 1 + x.length); resolve(rawKey.buffer); } }); break; default: throw new WebCryptoError(`ExportKey: Unknown export format '${format}'`); } }); } static sign(algorithm, key, data) { return new Promise((resolve, reject) => { const alg = this.wc2ssl(algorithm); const nativeKey = key.native; nativeKey.sign(alg, data, (err, signature) => { if (err) { reject(new WebCryptoError("NativeError: " + err.message)); } else { resolve(signature.buffer); } }); }); } static verify(algorithm, key, signature, data) { return new Promise((resolve, reject) => { const alg = this.wc2ssl(algorithm); const nativeKey = key.native; nativeKey.verify(alg, data, signature, (err, res) => { if (err) { reject(new WebCryptoError("NativeError: " + err.message)); } else { resolve(res); } }); }); } static deriveKey(algorithm, baseKey, derivedKeyType, extractable, keyUsages) { return new Promise((resolve, reject) => { const algDerivedKeyType = derivedKeyType; const alg = algorithm; let AesClass; switch (algDerivedKeyType.name.toLowerCase()) { case AlgorithmNames.AesCBC.toLowerCase(): case AlgorithmNames.AesGCM.toLowerCase(): case AlgorithmNames.AesKW.toLowerCase(): AesClass = aes.AesCrypto; break; default: throw new AlgorithmError(AlgorithmError.NOT_SUPPORTED, algDerivedKeyType.name); } baseKey.native.EcdhDeriveKey(alg.public.native, algDerivedKeyType.length / 8, (err, raw) => { if (err) { reject(err); } else { AesClass.importKey("raw", raw, algDerivedKeyType, extractable, keyUsages) .then(resolve, reject); } }); }); } static deriveBits(algorithm, baseKey, length) { return new Promise((resolve, reject) => { const alg = algorithm; const nativeKey = baseKey.native; nativeKey.EcdhDeriveBits(alg.public.native, length, (err, raw) => { if (err) { reject(err); } else { resolve(raw.buffer); } }); }); } static wc2ssl(algorithm) { const alg = algorithm.hash.name.toUpperCase().replace("-", ""); return alg; } } exports.EcCrypto = EcCrypto;