UNPKG

@sphereon/ssi-sdk-ext.kms-musap-rn

Version:

Sphereon SSI-SDK react-native plugin for management of keys with musap.

284 lines 13.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (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 __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MusapKeyManagementSystem = exports.logger = void 0; const ssi_sdk_ext_x509_utils_1 = require("@sphereon/ssi-sdk-ext.x509-utils"); const musap_react_native_1 = require("@sphereon/musap-react-native"); const key_manager_1 = require("@veramo/key-manager"); const text_encoding_1 = require("text-encoding"); const ssi_types_1 = require("@sphereon/ssi-types"); const ssi_sdk_ext_key_utils_1 = require("@sphereon/ssi-sdk-ext.key-utils"); const u8a = __importStar(require("uint8arrays")); exports.logger = ssi_types_1.Loggers.DEFAULT.get('sphereon:musap-rn-kms'); class MusapKeyManagementSystem extends key_manager_1.AbstractKeyManagementSystem { constructor(sscdType, sscdId, opts) { super(); this.mapKeyTypeToAlgorithmType = (type) => { switch (type) { case 'Secp256k1': return 'ECCP256K1'; case 'Secp256r1': return 'ECCP256R1'; case 'RSA': return 'RSA2K'; default: throw new Error(`Key type ${type} is not supported by MUSAP`); } }; this.mapAlgorithmTypeToKeyType = (type) => { switch (type) { case 'eccp256k1': return 'Secp256k1'; case 'eccp256r1': return 'Secp256r1'; case 'ecc_ed25519': return 'Ed25519'; case 'rsa2k': case 'rsa4k': return 'RSA'; default: throw new Error(`Key type ${type} is not supported.`); } }; this.decodeMusapPublicKey = (args) => { const { publicKey, keyType } = args; // First try the normal PEM decoding path const pemBinary = (0, ssi_sdk_ext_x509_utils_1.PEMToBinary)(publicKey.pem); // Check if we got a string that looks like base64 (might be double encoded) // Convert Uint8Array to string safely const pemString = u8a.toString(pemBinary, 'utf8'); const isDoubleEncoded = pemBinary.length > 0 && typeof pemString === 'string' && pemString.startsWith('MF'); if (isDoubleEncoded) { // Handle double-encoded case const actualDerBytes = u8a.fromString(pemString, 'base64'); // For double-encoded case, we know the key data starts after the header const keyDataStart = 24; const keyData = actualDerBytes.slice(keyDataStart); // Convert to public key hex let publicKeyHex = u8a.toString(keyData, 'hex'); // If it's not compressed yet and doesn't start with 0x04 (uncompressed point marker), add it if (publicKeyHex.length <= 128 && !publicKeyHex.startsWith('04')) { publicKeyHex = '04' + publicKeyHex; } // Ensure we have full 65 bytes for uncompressed keys while (publicKeyHex.startsWith('04') && publicKeyHex.length < 130) { publicKeyHex = publicKeyHex + '0'; } // Now convert to compressed format if needed if (publicKeyHex.startsWith('04') && publicKeyHex.length === 130) { const xCoord = u8a.fromString(publicKeyHex.slice(2, 66), 'hex'); const yCoord = u8a.fromString(publicKeyHex.slice(66, 130), 'hex'); const prefix = new Uint8Array([yCoord[31] % 2 === 0 ? 0x02 : 0x03]); const compressedKey = new Uint8Array(33); // 1 byte prefix + 32 bytes x coordinate compressedKey.set(prefix, 0); compressedKey.set(xCoord, 1); return u8a.toString(compressedKey, 'hex'); } return publicKeyHex; } // Not double encoded, proceed with normal path const publicKeyBinary = (0, ssi_sdk_ext_key_utils_1.isAsn1Der)(pemBinary) ? (0, ssi_sdk_ext_key_utils_1.asn1DerToRawPublicKey)(pemBinary, keyType) : pemBinary; return (0, ssi_sdk_ext_key_utils_1.isRawCompressedPublicKey)(publicKeyBinary) ? (0, ssi_sdk_ext_key_utils_1.hexStringFromUint8Array)(publicKeyBinary) : (0, ssi_sdk_ext_key_utils_1.toRawCompressedHexPublicKey)(publicKeyBinary, keyType); }; try { this.musapClient = musap_react_native_1.MusapClient; this.sscdType = sscdType ? sscdType : 'TEE'; this.sscdId = sscdId !== null && sscdId !== void 0 ? sscdId : this.sscdType; this.defaultKeyAttributes = opts === null || opts === void 0 ? void 0 : opts.defaultKeyAttributes; this.defaultSignAttributes = opts === null || opts === void 0 ? void 0 : opts.defaultSignAttributes; const enabledSscds = this.musapClient.listEnabledSscds(); if (!enabledSscds.some(value => value.sscdId == sscdId)) { this.musapClient.enableSscd(this.sscdType, this.sscdId, opts === null || opts === void 0 ? void 0 : opts.externalSscdSettings); } } catch (e) { console.error('enableSscd', e); throw Error('enableSscd failed'); } } listKeys() { return __awaiter(this, void 0, void 0, function* () { const keysJson = (this.musapClient.listKeys()); return keysJson.map((key) => this.asMusapKeyInfo(key)); }); } createKey(args) { return __awaiter(this, void 0, void 0, function* () { const { type, meta } = args; if (meta === undefined || !('keyAlias' in meta)) { return Promise.reject(Error('a unique keyAlias field is required for MUSAP')); } if (this.sscdType == 'EXTERNAL') { const existingKeys = (this.musapClient.listKeys()); const extKey = existingKeys.find(musapKey => musapKey.sscdType === 'External Signature'); // FIXME returning does not match SscdType enum if (extKey) { extKey.algorithm = 'eccp256r1'; // FIXME MUSAP announces key as rsa2k, but it's actually EC return this.asMusapKeyInfo(extKey); } return Promise.reject(Error(`No external key was bound yet for sscd ${this.sscdId}`)); } const keyGenReq = { keyAlgorithm: this.mapKeyTypeToAlgorithmType(type), keyUsage: 'keyUsage' in meta ? meta.keyUsage : 'sign', keyAlias: meta.keyAlias, attributes: this.recordToKeyAttributes(Object.assign(Object.assign({}, this.defaultKeyAttributes), ('attributes' in meta ? meta.attributes : {}))), role: 'role' in meta ? meta.role : 'administrator', }; try { const generatedKeyUri = yield this.musapClient.generateKey(this.sscdType, keyGenReq); if (generatedKeyUri) { exports.logger.debug('Generated key:', generatedKeyUri); const key = this.musapClient.getKeyByUri(generatedKeyUri); return this.asMusapKeyInfo(key); } else { return Promise.reject(new Error('Failed to generate key. No key URI')); } } catch (error) { exports.logger.error('An error occurred:', error); throw error; } }); } deleteKey(_a) { return __awaiter(this, arguments, void 0, function* ({ kid }) { try { const key = this.musapClient.getKeyById(kid); if (key.sscdType === 'External Signature') { return true; // FIXME we can't remove a eSim key for now because this would mean onboarding again } void this.musapClient.removeKey(kid); return true; } catch (error) { console.warn('Failed to delete key:', error); return false; } }); } determineAlgorithm(providedAlgorithm, keyAlgorithm) { if (providedAlgorithm === undefined) { return (0, musap_react_native_1.signatureAlgorithmFromKeyAlgorithm)(keyAlgorithm); } if ((0, musap_react_native_1.isSignatureAlgorithmType)(providedAlgorithm)) { return providedAlgorithm; } // Veramo translates TKeyType to JWSAlgorithm return (0, musap_react_native_1.signatureAlgorithmFromKeyAlgorithm)(providedAlgorithm); } sign(args) { return __awaiter(this, void 0, void 0, function* () { var _a; if (!args.keyRef) { throw new Error('key_not_found: No key ref provided'); } const data = new text_encoding_1.TextDecoder().decode(args.data); const key = this.musapClient.getKeyById(args.keyRef.kid); if (key.sscdType === 'External Signature') { key.algorithm = 'eccp256r1'; // FIXME MUSAP announces key as rsa2k, but it's actually EC } const signatureReq = { keyUri: key.keyUri, data, algorithm: this.determineAlgorithm(args.algorithm, key.algorithm), displayText: args.displayText, transId: args.transId, format: (_a = args.format) !== null && _a !== void 0 ? _a : 'RAW', attributes: this.recordToSignatureAttributes(Object.assign(Object.assign({}, this.defaultSignAttributes), args.attributes)), }; return this.musapClient.sign(signatureReq); }); } importKey(args) { return __awaiter(this, void 0, void 0, function* () { throw new Error('importKey is not implemented for MusapKeyManagementSystem.'); }); } asMusapKeyInfo(args) { const _a = Object.assign({}, args), { keyId, publicKey } = _a, metadata = __rest(_a, ["keyId", "publicKey"]); const keyType = this.mapAlgorithmTypeToKeyType(args.algorithm); const publicKeyHex = this.decodeMusapPublicKey({ publicKey: publicKey, keyType: keyType }); const keyInfo = { kid: keyId, type: keyType, publicKeyHex, meta: metadata, }; const jwkThumbprint = (0, ssi_sdk_ext_key_utils_1.calculateJwkThumbprintForKey)({ key: keyInfo }); keyInfo.meta = Object.assign(Object.assign({}, keyInfo.meta), { jwkThumbprint }); return keyInfo; } sharedSecret(args) { throw new Error('Not supported.'); } recordToKeyAttributes(record) { if (!record) { return []; } return Object.entries(record).map(([key, value]) => ({ name: key, value, })); } recordToSignatureAttributes(record) { if (!record) { return []; } return Object.entries(record).map(([key, value]) => ({ name: key, value, })); } } exports.MusapKeyManagementSystem = MusapKeyManagementSystem; //# sourceMappingURL=MusapKeyManagerSystem.js.map