parse-cosekey
Version:
Parse COSE(CBOR Object Signing and Encryption) to JWK(JSON Web Key) or PEM.
360 lines • 14.7 kB
JavaScript
;
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