@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
JavaScript
"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