UNPKG

parse-cosekey

Version:

Parse COSE(CBOR Object Signing and Encryption) to JWK(JSON Web Key) or PEM.

360 lines 14.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const crypto_1 = __importDefault(require("crypto")); const cbor_1 = __importDefault(require("cbor")); const jose = __importStar(require("jose")); const str2ab_1 = __importDefault(require("str2ab")); const coseKey_1 = require("./coseKey"); const coseJoseMapping_1 = require("./coseJoseMapping"); const keyParseError_1 = __importDefault(require("./exception/keyParseError")); const joseKey_1 = require("./joseKey"); /** * Parse between JSON Web Key (JWK), CBOR Object Signing and Encryption (COSE) and PEM. */ class KeyParser { constructor() { // private } static findCoseKeyAllParameterFromLabel(label, keyType) { return coseKey_1.COSEKeyCommonParameter.fromLabel(label) || coseKey_1.COSEKeyTypeParameter.fromLabel(keyType, label) || null; } static findCoseKeyParameterValueFromLabel(label, keyParameter) { const parameterValueMapping = coseKey_1.COSEKeyParameterValueMapping.fromParameter(keyParameter); if (parameterValueMapping == null) { return null; } return parameterValueMapping.fromValueLabel(label); } static findJSONWebKeyParameterFromName(name, keyType) { return joseKey_1.JSONWebKeyParameter.fromName(keyType, name); } static findJSONWebKeyParameterValueFromValue(value) { if (typeof value !== 'string') { return null; } const param = joseKey_1.JSONWebKeyType.fromValue(value) || joseKey_1.JSONWebKeyEllipticCurve.fromName(value) || joseKey_1.JSONWebKeyUse.fromValue(value) || joseKey_1.JSONWebKeyOperation.fromValue(value) || joseKey_1.JSONWebSignatureAndEncryptionAlgorithm.fromName(value); return param; } static convertCoseJoseValue(parameterValue) { if (parameterValue instanceof coseKey_1.COSEKeyType) { return coseJoseMapping_1.KeyTypeMapping.fromCOSEKeyType(parameterValue); } else if (parameterValue instanceof coseKey_1.COSEKeyOperationValue) { return coseJoseMapping_1.KeyOperationMapping.fromCOSEKeyOperation(parameterValue); } else if (parameterValue instanceof coseKey_1.COSEEllipticCurve) { return coseJoseMapping_1.EllipticCurveMapping.fromCOSEEllipticCurve(parameterValue); } else if (parameterValue instanceof coseKey_1.COSEAlgorithm) { return coseJoseMapping_1.KeyAlgorithmMapping.fromCOSEAlgorithm(parameterValue); } return null; } static convertJoseCoseKey(keyParameter) { return coseJoseMapping_1.KeyParameterMapping.fromJSONWebKeyParameter(keyParameter); } static convertJoseCoseValue(parameterValue) { if (parameterValue instanceof joseKey_1.JSONWebKeyType) { return coseJoseMapping_1.KeyTypeMapping.fromJSONWebKeyType(parameterValue); } else if (parameterValue instanceof joseKey_1.JSONWebKeyEllipticCurve) { return coseJoseMapping_1.EllipticCurveMapping.fromJSONWebKeyEllipticCurve(parameterValue); } else if (parameterValue instanceof joseKey_1.JSONWebKeyUse) { return parameterValue; } else if (parameterValue instanceof joseKey_1.JSONWebKeyOperation) { return coseJoseMapping_1.KeyOperationMapping.fromJSONWebKeyOperation(parameterValue); } else if (parameterValue instanceof joseKey_1.JSONWebSignatureAndEncryptionAlgorithm) { return coseJoseMapping_1.KeyAlgorithmMapping.fromJoseAlgorithm(parameterValue); } return null; } /** * Parse COSE key to JWK format. * * @param coseMap COSE key * @returns JWK */ static cose2jwk(coseMap) { // specify key type const keyTypeValue = coseMap.get(coseKey_1.COSEKeyCommonParameter.KTY.label); if (keyTypeValue == null) { throw new keyParseError_1.default('"kty" is not included.'); } if (typeof keyTypeValue !== 'number') { throw new keyParseError_1.default(`"kty" is not number: ${keyTypeValue}`); } const keyType = coseKey_1.COSEKeyType.fromValue(keyTypeValue); if (!keyType) { throw new keyParseError_1.default('Cannot detect key type.'); } const jwk = {}; for (const [key, value] of coseMap.entries()) { const keyParameter = this.findCoseKeyAllParameterFromLabel(key, keyType); if (keyParameter == null) { continue; } const parameterValue = this.findCoseKeyParameterValueFromLabel(value, keyParameter); if (parameterValue != null) { const joseParameterValue = this.convertCoseJoseValue(parameterValue); if (joseParameterValue instanceof joseKey_1.JSONWebKeyType) { jwk[keyParameter.name] = joseParameterValue.value; } else if (joseParameterValue instanceof joseKey_1.JSONWebKeyEllipticCurve) { jwk[keyParameter.name] = joseParameterValue.name; } else if (joseParameterValue instanceof joseKey_1.JSONWebKeyOperation) { jwk[keyParameter.name] = joseParameterValue.value; } else if (parameterValue instanceof coseKey_1.COSEAlgorithm) { jwk[keyParameter.name] = joseParameterValue != null ? joseParameterValue.name : parameterValue.name; } } else { if (value instanceof Buffer) { jwk[keyParameter.name] = str2ab_1.default.buffer2base64url(value); } else { jwk[keyParameter.name] = value; } } } return jwk; } /** * Parse JWK to COSE key. * * @param jwk JWK * @returns COSE key */ static jwk2cose(jwk) { const keyTypeValue = jwk.kty; if (!keyTypeValue) { throw new keyParseError_1.default('jwk does not have "kty".'); } const keyType = joseKey_1.JSONWebKeyType.fromValue(keyTypeValue); if (keyType == null) { throw new keyParseError_1.default('Cannot convert key type.'); } const coseMap = new Map(); for (const [key, value] of Object.entries(jwk)) { const keyParameter = this.findJSONWebKeyParameterFromName(key, keyType); const coseKeyParameter = keyParameter != null ? this.convertJoseCoseKey(keyParameter) : null; if (coseKeyParameter == null) { continue; } const parameterValue = typeof value === 'string' ? this.findJSONWebKeyParameterValueFromValue(value) : null; const coseParameterValue = parameterValue != null ? this.convertJoseCoseValue(parameterValue) : null; if (coseParameterValue != null) { coseMap.set(Number(coseKeyParameter.label), coseParameterValue.value); } else { if (typeof value === 'string' && /[a-zA-Z0-9\-_]+/.test(value) && KeyParser.BUFFER_TYPE_COSE_KEY_PARAMETER_NAME.includes(coseKeyParameter.name)) { coseMap.set(Number(coseKeyParameter.label), str2ab_1.default.base64url2buffer(value)); } else { coseMap.set(Number(coseKeyParameter.label), value); } } } return coseMap; } /** * Parse JWK to PEM. * * @param jwk JWK * @returns PEM */ static jwk2pem(jwk) { return __awaiter(this, void 0, void 0, function* () { try { const key = yield jose.importJWK(jwk); if (key instanceof Uint8Array) { const keyType = jwk.d == null ? 'PUBLIC KEY' : 'PRIVATE KEY'; return KeyParser.der2pem(keyType, Buffer.from(key)); } else { if (jwk.d == null) { // Public key return yield jose.exportSPKI(key); } else { // Private key return yield jose.exportPKCS8(key); } } } catch (err) { if (err instanceof Error) { throw new keyParseError_1.default(err); } else { throw new keyParseError_1.default('Cannot convert from jwk to PEM.'); } } }); } /** * Parse PEM to JWK. * * This method's parameter `alg` is specified in JWK's "alg" parameter ({@link https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms | here} is registry). * * @param pem PEM * @param alg JWK's "alg" parameter * @returns JWK */ static pem2jwk(pem, alg) { return __awaiter(this, void 0, void 0, function* () { const pemLine = pem.split(/(\r\n|\r|\n)+/g); const match = /^-----BEGIN (RSA )?(PUBLIC|PRIVATE) KEY-----$/.exec(pemLine[0]); if (match == null) { throw new keyParseError_1.default('Cannot convert this pem.'); } let jwk; if (match[2] === 'PUBLIC') { const key = crypto_1.default.createPublicKey(pem); jwk = yield jose.exportJWK(key); } else { const key = crypto_1.default.createPrivateKey(pem); jwk = yield jose.exportJWK(key); } if (alg != null && alg !== '') { jwk.alg = alg; } return jwk; }); } /** * Parse COSE key to PEM. * * @param coseMap COSE key * @returns PEM */ static cose2pem(coseMap) { return __awaiter(this, void 0, void 0, function* () { const jwk = KeyParser.cose2jwk(coseMap); const pem = yield KeyParser.jwk2pem(jwk); return pem; }); } /** * Parse PEM to COSE key. * * This method's parameter `alg` is specified in JWK's "alg" parameter ({@link https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms | here} is registry). * * @param pem PEM * @param alg Key algorithm, specified with JWK's "alg" parameter * @returns COSE key */ static pem2cose(pem, alg) { return __awaiter(this, void 0, void 0, function* () { const jwk = yield KeyParser.pem2jwk(pem, alg); const coseMap = KeyParser.jwk2cose(jwk); return coseMap; }); } /** * Parse attestation object in webauthn to JWK. * * @param attObj Attestation object * @returns JWK */ static attestationObject2jwk(attObj) { const decoded = cbor_1.default.decodeAllSync(attObj); const authData = decoded[0]['authData']; const credIdLenBuf = authData.slice(53, 53 + 2); const credIdLen = credIdLenBuf.readUInt16BE(); const credPubKeyAnd = authData.slice(55 + credIdLen); const [coseMap, ...extensions] = cbor_1.default.decodeAllSync(credPubKeyAnd); return KeyParser.cose2jwk(coseMap); } /** * Parse DER format key to PEM format. * * @param type Key type * @param der DER format key * @returns PEM format key */ static der2pem(type, der) { const base64 = str2ab_1.default.buffer2base64(der); return [ `-----BEGIN ${type}-----\n`, ...(base64.match(/.{1,64}/g) || []).join('\n').concat('\n'), `-----END ${type}-----\n`, ].join(''); } /** * Parse PEM format key to DER format. * * @param pem PEM format key * @returns DER format key */ static pem2der(pem) { const base64 = pem .replace(/-----BEGIN (RSA )?(PUBLIC|PRIVATE) KEY-----/, '') .replace(/-----END (RSA )?(PUBLIC|PRIVATE) KEY-----/, '') .replace(/(\r\n|\r|\n)+/g, ''); return str2ab_1.default.base642buffer(base64); } } KeyParser.BUFFER_TYPE_COSE_KEY_PARAMETER_NAME = [ 'kid', 'Base IV', 'x', 'd', 'y', 'n', 'e', 'p', 'q', 'dP', 'dQ', 'qInv', 'r_i', 'd_i', 't_i', 'k', 'pub', ]; exports.default = KeyParser; //# sourceMappingURL=keyParser.js.map