Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@sphereon/ssi-sdk-ext.key-utils

Package Overview
Dependencies
Maintainers
0
Versions
351
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sphereon/ssi-sdk-ext.key-utils - npm Package Compare versions

Comparing version 0.26.1-feature.SPRIND.116.9 to 0.26.1-feature.SPRIND.116.44

5

dist/conversion.js

@@ -25,9 +25,10 @@ "use strict";

const ssi_types_1 = require("@sphereon/ssi-types");
const functions_1 = require("./functions");
function coseKeyToJwk(coseKey) {
const { x5chain, key_ops, crv, alg, baseIV, kty } = coseKey, rest = __rest(coseKey, ["x5chain", "key_ops", "crv", "alg", "baseIV", "kty"]);
return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, rest), { kty: coseToJoseKty(kty) }), (crv && { crv: coseToJoseCurve(crv) })), (key_ops && { key_ops: key_ops.map(coseToJoseKeyOperation) })), (alg && { alg: coseToJoseSignatureAlg(alg) })), (baseIV && { iv: baseIV })), (x5chain && { x5c: x5chain }));
return (0, functions_1.removeNulls)(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, rest), { kty: coseToJoseKty(kty) }), (crv && { crv: coseToJoseCurve(crv) })), (key_ops && { key_ops: key_ops.map(coseToJoseKeyOperation) })), (alg && { alg: coseToJoseSignatureAlg(alg) })), (baseIV && { iv: baseIV })), (x5chain && { x5c: x5chain })));
}
function jwkToCoseKey(jwk) {
const { x5c, key_ops, crv, alg, iv, kty } = jwk, rest = __rest(jwk, ["x5c", "key_ops", "crv", "alg", "iv", "kty"]);
return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, rest), { kty: joseToCoseKty(kty) }), (crv && { crv: joseToCoseCurve(crv) })), (key_ops && { key_ops: key_ops.map(joseToCoseKeyOperation) })), (alg && { alg: joseToCoseSignatureAlg(alg) })), (iv && { baseIV: iv })), (x5c && { x5chain: x5c }));
return (0, functions_1.removeNulls)(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, rest), { kty: joseToCoseKty(kty) }), (crv && { crv: joseToCoseCurve(crv) })), (key_ops && { key_ops: key_ops.map(joseToCoseKeyOperation) })), (alg && { alg: joseToCoseSignatureAlg(alg) })), (iv && { baseIV: iv })), (x5c && { x5chain: x5c })));
}

@@ -34,0 +35,0 @@ function coseToJoseKty(kty) {

4

dist/digest-methods.d.ts

@@ -0,3 +1,4 @@

import { Hasher } from '@sphereon/ssi-types';
import { SupportedEncodings } from 'uint8arrays/to-string';
export type HashAlgorithm = 'SHA-256' | 'SHA-512';
export type HashAlgorithm = 'SHA-256' | 'SHA-384' | 'SHA-512';
export type TDigestMethod = (input: string, encoding?: SupportedEncodings) => string;

@@ -9,2 +10,3 @@ export declare const digestMethodParams: (hashAlgorithm: HashAlgorithm) => {

};
export declare const shaHasher: Hasher;
//# sourceMappingURL=digest-methods.d.ts.map

@@ -26,20 +26,31 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.digestMethodParams = void 0;
const sha256_1 = require("@stablelib/sha256");
const sha512_1 = require("@stablelib/sha512");
exports.shaHasher = exports.digestMethodParams = void 0;
const sha256_1 = require("@noble/hashes/sha256");
const sha512_1 = require("@noble/hashes/sha512");
const u8a = __importStar(require("uint8arrays"));
const digestMethodParams = (hashAlgorithm) => {
if (hashAlgorithm === 'SHA-256') {
return { hashAlgorithm: 'SHA-256', digestMethod: sha256DigestMethod, hash: sha256_1.hash };
return { hashAlgorithm: 'SHA-256', digestMethod: sha256DigestMethod, hash: sha256_1.sha256 };
}
else if (hashAlgorithm === 'SHA-384') {
return { hashAlgorithm: 'SHA-384', digestMethod: sha384DigestMethod, hash: sha512_1.sha384 };
}
else {
return { hashAlgorithm: 'SHA-512', digestMethod: sha512DigestMethod, hash: sha512_1.hash };
return { hashAlgorithm: 'SHA-512', digestMethod: sha512DigestMethod, hash: sha512_1.sha512 };
}
};
exports.digestMethodParams = digestMethodParams;
const shaHasher = (input, alg) => {
const hashAlgorithm = alg.includes('384') ? 'SHA-384' : alg.includes('512') ? 'SHA-512' : 'SHA-256';
return (0, exports.digestMethodParams)(hashAlgorithm).hash(u8a.fromString(input, 'utf-8'));
};
exports.shaHasher = shaHasher;
const sha256DigestMethod = (input, encoding = 'base16') => {
return u8a.toString((0, sha256_1.hash)(u8a.fromString(input, 'utf-8')), encoding);
return u8a.toString((0, sha256_1.sha256)(u8a.fromString(input, 'utf-8')), encoding);
};
const sha384DigestMethod = (input, encoding = 'base16') => {
return u8a.toString((0, sha512_1.sha384)(u8a.fromString(input, 'utf-8')), encoding);
};
const sha512DigestMethod = (input, encoding = 'base16') => {
return u8a.toString((0, sha512_1.hash)(u8a.fromString(input, 'utf-8')), encoding);
return u8a.toString((0, sha512_1.sha512)(u8a.fromString(input, 'utf-8')), encoding);
};

@@ -46,0 +57,0 @@ /*

@@ -60,2 +60,9 @@ import { JoseSignatureAlgorithm, JWK } from '@sphereon/ssi-types';

/**
* Convert a JWK to a raw hex key.
* Currently supports `RSA` and `EC` keys. Extendable for other key types.
* @param jwk - The JSON Web Key object.
* @returns A string representing the key in raw hexadecimal format.
*/
export declare const jwkToRawHexKey: (jwk: JWK) => Promise<string>;
/**
* Determines the use param based upon the key/signature type or supplied use value.

@@ -80,8 +87,16 @@ *

export declare const keyTypeFromCryptographicSuite: (args: KeyTypeFromCryptographicSuiteArgs) => TKeyType;
export declare function verifySignatureWithSubtle({ data, signature, key, crypto: cryptoArg, }: {
export declare function removeNulls<T>(obj: T | any): any;
export declare const globalCrypto: (setGlobal: boolean, suppliedCrypto?: Crypto) => Crypto;
export declare const sanitizedJwk: (input: JWK | JsonWebKey) => JWK;
/**
*
*/
export declare function verifyRawSignature({ data, signature, key: inputKey, opts, }: {
data: Uint8Array;
signature: Uint8Array;
key: JsonWebKey;
crypto?: Crypto;
key: JWK;
opts?: {
signatureAlg?: JoseSignatureAlgorithm;
};
}): Promise<boolean>;
//# sourceMappingURL=functions.d.ts.map

@@ -38,12 +38,25 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.keyTypeFromCryptographicSuite = exports.signatureAlgorithmFromKeyType = exports.signatureAlgorithmFromKey = exports.hexStringFromUint8Array = exports.toRawCompressedHexPublicKey = exports.isRawCompressedPublicKey = exports.asn1DerToRawPublicKey = exports.isAsn1Der = exports.padLeft = exports.jwkDetermineUse = exports.toJwk = exports.toJwkFromKey = exports.calculateJwkThumbprint = exports.toBase64url = exports.calculateJwkThumbprintForKey = exports.generatePrivateKeyHex = exports.getKms = exports.logger = void 0;
exports.sanitizedJwk = exports.globalCrypto = exports.keyTypeFromCryptographicSuite = exports.signatureAlgorithmFromKeyType = exports.signatureAlgorithmFromKey = exports.hexStringFromUint8Array = exports.toRawCompressedHexPublicKey = exports.isRawCompressedPublicKey = exports.asn1DerToRawPublicKey = exports.isAsn1Der = exports.padLeft = exports.jwkDetermineUse = exports.jwkToRawHexKey = exports.toJwk = exports.toJwkFromKey = exports.calculateJwkThumbprint = exports.toBase64url = exports.calculateJwkThumbprintForKey = exports.generatePrivateKeyHex = exports.getKms = exports.logger = void 0;
exports.importProvidedOrGeneratedKey = importProvidedOrGeneratedKey;
exports.verifySignatureWithSubtle = verifySignatureWithSubtle;
exports.removeNulls = removeNulls;
exports.verifyRawSignature = verifyRawSignature;
const random_1 = require("@ethersproject/random");
// Do not change these require statements to imports before we change to ESM. Breaks external CJS packages depending on this module
const bls12_381_1 = require("@noble/curves/bls12-381");
const ed25519_1 = require("@noble/curves/ed25519");
const p256_1 = require("@noble/curves/p256");
const p384_1 = require("@noble/curves/p384");
const p521_1 = require("@noble/curves/p521");
const secp256k1_1 = require("@noble/curves/secp256k1");
const sha256_1 = require("@noble/hashes/sha256");
const sha512_1 = require("@noble/hashes/sha512");
const ssi_sdk_ext_x509_utils_1 = require("@sphereon/ssi-sdk-ext.x509-utils");
const ssi_types_1 = require("@sphereon/ssi-types");
const ed25519_1 = require("@stablelib/ed25519");
const ed25519_2 = require("@stablelib/ed25519");
const debug_1 = __importDefault(require("debug"));
const elliptic_1 = __importDefault(require("elliptic"));
const rsa = __importStar(require("micro-rsa-dsa-dh/rsa.js"));
const u8a = __importStar(require("uint8arrays"));
const digest_methods_1 = require("./digest-methods");
const jwk_jcs_1 = require("./jwk-jcs");
const types_1 = require("./types");

@@ -75,3 +88,3 @@ exports.logger = ssi_types_1.Loggers.DEFAULT.get('sphereon:key-utils');

case 'Ed25519': {
const keyPairEd25519 = (0, ed25519_1.generateKeyPair)();
const keyPairEd25519 = (0, ed25519_2.generateKeyPair)();
return u8a.toString(keyPairEd25519.secretKey, 'base16');

@@ -173,3 +186,4 @@ }

const calculateJwkThumbprint = (args) => {
const { jwk, digestAlgorithm = 'sha256' } = args;
const { digestAlgorithm = 'sha256' } = args;
const jwk = (0, exports.sanitizedJwk)(args.jwk);
let components;

@@ -246,6 +260,90 @@ switch (jwk.kty) {

}
return jwk;
return (0, exports.sanitizedJwk)(jwk);
};
exports.toJwk = toJwk;
/**
* Convert a JWK to a raw hex key.
* Currently supports `RSA` and `EC` keys. Extendable for other key types.
* @param jwk - The JSON Web Key object.
* @returns A string representing the key in raw hexadecimal format.
*/
const jwkToRawHexKey = (jwk) => __awaiter(void 0, void 0, void 0, function* () {
// TODO: Probably makes sense to have an option to do the same for private keys
jwk = (0, exports.sanitizedJwk)(jwk);
if (jwk.kty === 'RSA') {
return rsaJwkToRawHexKey(jwk);
}
else if (jwk.kty === 'EC') {
return ecJwkToRawHexKey(jwk);
}
else if (jwk.kty === 'OKP') {
return okpJwkToRawHexKey(jwk);
}
else if (jwk.kty === 'oct') {
return octJwkToRawHexKey(jwk);
}
else {
throw new Error(`Unsupported key type: ${jwk.kty}`);
}
});
exports.jwkToRawHexKey = jwkToRawHexKey;
/**
* Convert an RSA JWK to a raw hex key.
* @param jwk - The RSA JWK object.
* @returns A string representing the RSA key in raw hexadecimal format.
*/
function rsaJwkToRawHexKey(jwk) {
jwk = (0, exports.sanitizedJwk)(jwk);
if (!jwk.n || !jwk.e) {
throw new Error("RSA JWK must contain 'n' and 'e' properties.");
}
// We are converting from base64 to base64url to be sure. The spec uses base64url, but in the wild we sometimes encounter a base64 string
const modulus = u8a.fromString(jwk.n.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''), 'base64url'); // 'n' is the modulus
const exponent = u8a.fromString(jwk.e.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''), 'base64url'); // 'e' is the exponent
return u8a.toString(modulus, 'hex') + u8a.toString(exponent, 'hex');
}
/**
* Convert an EC JWK to a raw hex key.
* @param jwk - The EC JWK object.
* @returns A string representing the EC key in raw hexadecimal format.
*/
function ecJwkToRawHexKey(jwk) {
jwk = (0, exports.sanitizedJwk)(jwk);
if (!jwk.x || !jwk.y) {
throw new Error("EC JWK must contain 'x' and 'y' properties.");
}
// We are converting from base64 to base64url to be sure. The spec uses base64url, but in the wild we sometimes encounter a base64 string
const x = u8a.fromString(jwk.x.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''), 'base64url');
const y = u8a.fromString(jwk.y.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''), 'base64url');
return '04' + u8a.toString(x, 'hex') + u8a.toString(y, 'hex');
}
/**
* Convert an EC JWK to a raw hex key.
* @param jwk - The EC JWK object.
* @returns A string representing the EC key in raw hexadecimal format.
*/
function okpJwkToRawHexKey(jwk) {
jwk = (0, exports.sanitizedJwk)(jwk);
if (!jwk.x) {
throw new Error("OKP JWK must contain 'x' property.");
}
// We are converting from base64 to base64url to be sure. The spec uses base64url, but in the wild we sometimes encounter a base64 string
const x = u8a.fromString(jwk.x.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''), 'base64url');
return u8a.toString(x, 'hex');
}
/**
* Convert an octet JWK to a raw hex key.
* @param jwk - The octet JWK object.
* @returns A string representing the octet key in raw hexadecimal format.
*/
function octJwkToRawHexKey(jwk) {
jwk = (0, exports.sanitizedJwk)(jwk);
if (!jwk.k) {
throw new Error("Octet JWK must contain 'k' property.");
}
// We are converting from base64 to base64url to be sure. The spec uses base64url, but in the wild we sometimes encounter a base64 string
const key = u8a.fromString(jwk.k.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''), 'base64url');
return u8a.toString(key, 'hex');
}
/**
* Determines the use param based upon the key/signature type or supplied use value.

@@ -301,3 +399,3 @@ *

const pubPoint = keyPair.getPublic();
return Object.assign(Object.assign(Object.assign({ alg: ssi_types_1.JoseSignatureAlgorithm.ES256K }, (use !== undefined && { use })), { kty: ssi_types_1.JwkKeyType.EC, crv: ssi_types_1.JoseCurve.secp256k1, x: (0, ssi_sdk_ext_x509_utils_1.hexToBase64)(pubPoint.getX().toString('hex'), 'base64url'), y: (0, ssi_sdk_ext_x509_utils_1.hexToBase64)(pubPoint.getY().toString('hex'), 'base64url') }), ((opts === null || opts === void 0 ? void 0 : opts.isPrivateKey) && { d: (0, ssi_sdk_ext_x509_utils_1.hexToBase64)(keyPair.getPrivate('hex'), 'base64url') }));
return (0, exports.sanitizedJwk)(Object.assign(Object.assign(Object.assign({ alg: ssi_types_1.JoseSignatureAlgorithm.ES256K }, (use !== undefined && { use })), { kty: ssi_types_1.JwkKeyType.EC, crv: ssi_types_1.JoseCurve.secp256k1, x: (0, ssi_sdk_ext_x509_utils_1.hexToBase64)(pubPoint.getX().toString('hex'), 'base64url'), y: (0, ssi_sdk_ext_x509_utils_1.hexToBase64)(pubPoint.getY().toString('hex'), 'base64url') }), ((opts === null || opts === void 0 ? void 0 : opts.isPrivateKey) && { d: (0, ssi_sdk_ext_x509_utils_1.hexToBase64)(keyPair.getPrivate('hex'), 'base64url') })));
};

@@ -324,3 +422,3 @@ /**

const pubPoint = keyPair.getPublic();
return Object.assign(Object.assign(Object.assign({ alg: ssi_types_1.JoseSignatureAlgorithm.ES256 }, (use !== undefined && { use })), { kty: ssi_types_1.JwkKeyType.EC, crv: ssi_types_1.JoseCurve.P_256, x: (0, ssi_sdk_ext_x509_utils_1.hexToBase64)(pubPoint.getX().toString('hex'), 'base64url'), y: (0, ssi_sdk_ext_x509_utils_1.hexToBase64)(pubPoint.getY().toString('hex'), 'base64url') }), ((opts === null || opts === void 0 ? void 0 : opts.isPrivateKey) && { d: (0, ssi_sdk_ext_x509_utils_1.hexToBase64)(keyPair.getPrivate('hex'), 'base64url') }));
return (0, exports.sanitizedJwk)(Object.assign(Object.assign(Object.assign({ alg: ssi_types_1.JoseSignatureAlgorithm.ES256 }, (use !== undefined && { use })), { kty: ssi_types_1.JwkKeyType.EC, crv: ssi_types_1.JoseCurve.P_256, x: (0, ssi_sdk_ext_x509_utils_1.hexToBase64)(pubPoint.getX().toString('hex'), 'base64url'), y: (0, ssi_sdk_ext_x509_utils_1.hexToBase64)(pubPoint.getY().toString('hex'), 'base64url') }), ((opts === null || opts === void 0 ? void 0 : opts.isPrivateKey) && { d: (0, ssi_sdk_ext_x509_utils_1.hexToBase64)(keyPair.getPrivate('hex'), 'base64url') })));
};

@@ -337,14 +435,27 @@ /**

const { use } = opts !== null && opts !== void 0 ? opts : {};
return Object.assign(Object.assign({ alg: ssi_types_1.JoseSignatureAlgorithm.EdDSA }, (use !== undefined && { use })), { kty: ssi_types_1.JwkKeyType.OKP, crv: (_a = opts === null || opts === void 0 ? void 0 : opts.crv) !== null && _a !== void 0 ? _a : ssi_types_1.JoseCurve.Ed25519, x: (0, ssi_sdk_ext_x509_utils_1.hexToBase64)(publicKeyHex, 'base64url') });
return (0, exports.sanitizedJwk)(Object.assign(Object.assign({ alg: ssi_types_1.JoseSignatureAlgorithm.EdDSA }, (use !== undefined && { use })), { kty: ssi_types_1.JwkKeyType.OKP, crv: (_a = opts === null || opts === void 0 ? void 0 : opts.crv) !== null && _a !== void 0 ? _a : ssi_types_1.JoseCurve.Ed25519, x: (0, ssi_sdk_ext_x509_utils_1.hexToBase64)(publicKeyHex, 'base64url') }));
};
const toRSAJwk = (publicKeyHex, opts) => {
var _a, _b, _c;
const { key } = opts !== null && opts !== void 0 ? opts : {};
var _a, _b;
const meta = (_a = opts === null || opts === void 0 ? void 0 : opts.key) === null || _a === void 0 ? void 0 : _a.meta;
if ((meta === null || meta === void 0 ? void 0 : meta.publicKeyJwk) || (meta === null || meta === void 0 ? void 0 : meta.publicKeyPEM)) {
if (meta === null || meta === void 0 ? void 0 : meta.publicKeyJwk) {
return meta.publicKeyJwk;
}
const publicKeyPEM = (_b = meta === null || meta === void 0 ? void 0 : meta.publicKeyPEM) !== null && _b !== void 0 ? _b : (0, ssi_sdk_ext_x509_utils_1.hexToPEM)(publicKeyHex, 'public');
return (0, ssi_sdk_ext_x509_utils_1.PEMToJwk)(publicKeyPEM, 'public');
}
// exponent (e) is 5 chars long, rest is modulus (n)
// const publicKey = publicKeyHex
// assertProperKeyLength(publicKey, [2048, 3072, 4096])
if ((_a = key === null || key === void 0 ? void 0 : key.meta) === null || _a === void 0 ? void 0 : _a.publicKeyJwk) {
return key.meta.publicKeyJwk;
}
const publicKeyPEM = (_c = (_b = key === null || key === void 0 ? void 0 : key.meta) === null || _b === void 0 ? void 0 : _b.publicKeyPEM) !== null && _c !== void 0 ? _c : (0, ssi_sdk_ext_x509_utils_1.hexToPEM)(publicKeyHex, 'public');
return (0, ssi_sdk_ext_x509_utils_1.PEMToJwk)(publicKeyPEM, 'public');
const exponent = publicKeyHex.slice(-5);
const modulus = publicKeyHex.slice(0, -5);
// const modulusBitLength = (modulus.length / 2) * 8
// const alg = modulusBitLength === 2048 ? JoseSignatureAlgorithm.RS256 : modulusBitLength === 3072 ? JoseSignatureAlgorithm.RS384 : modulusBitLength === 4096 ? JoseSignatureAlgorithm.RS512 : undefined
return (0, exports.sanitizedJwk)({
kty: 'RSA',
n: (0, ssi_sdk_ext_x509_utils_1.hexToBase64)(modulus, 'base64url'),
e: (0, ssi_sdk_ext_x509_utils_1.hexToBase64)(exponent, 'base64url'),
// ...(alg && { alg }),
});
};

@@ -477,2 +588,6 @@ const padLeft = (args) => {

return ssi_types_1.JoseSignatureAlgorithm.ES256;
case 'Secp384r1':
return ssi_types_1.JoseSignatureAlgorithm.ES384;
case 'Secp521r1':
return ssi_types_1.JoseSignatureAlgorithm.ES512;
case 'Secp256k1':

@@ -487,5 +602,16 @@ return ssi_types_1.JoseSignatureAlgorithm.ES256K;

const keyTypeFromCryptographicSuite = (args) => {
const { suite } = args;
switch (suite) {
const { crv, kty, alg } = args;
switch (alg) {
case 'RSASSA-PSS':
case 'RS256':
case 'RS384':
case 'RS512':
case 'PS256':
case 'PS384':
case 'PS512':
return 'RSA';
}
switch (crv) {
case 'EdDSA':
case 'Ed25519':
case 'Ed25519Signature2018':

@@ -498,29 +624,143 @@ case 'Ed25519Signature2020':

case 'ECDSA':
case 'P-256':
return 'Secp256r1';
case 'ES384':
case 'P-384':
return 'Secp384r1';
case 'ES512':
case 'P-521':
return 'Secp521r1';
case 'EcdsaSecp256k1Signature2019':
case 'secp256k1':
case 'ES256K':
return 'Secp256k1';
default:
throw new Error(`Cryptographic suite '${suite}' not supported`);
}
if (kty) {
return kty;
}
throw new Error(`Cryptographic suite '${crv}' not supported`);
};
exports.keyTypeFromCryptographicSuite = keyTypeFromCryptographicSuite;
function verifySignatureWithSubtle(_a) {
return __awaiter(this, arguments, void 0, function* ({ data, signature, key, crypto: cryptoArg, }) {
var _b;
let { alg, crv } = key;
if (alg === 'ES256' || !alg) {
alg = 'ECDSA';
function removeNulls(obj) {
Object.keys(obj).forEach((key) => {
if (obj[key] && typeof obj[key] === 'object')
removeNulls(obj[key]);
else if (obj[key] == null)
delete obj[key];
});
return obj;
}
const globalCrypto = (setGlobal, suppliedCrypto) => {
var _a, _b;
let webcrypto;
if (typeof suppliedCrypto !== 'undefined') {
webcrypto = suppliedCrypto;
}
else if (typeof crypto !== 'undefined') {
webcrypto = crypto;
}
else if (typeof global.crypto !== 'undefined') {
webcrypto = global.crypto;
}
else if (typeof ((_b = (_a = global.window) === null || _a === void 0 ? void 0 : _a.crypto) === null || _b === void 0 ? void 0 : _b.subtle) !== 'undefined') {
webcrypto = global.window.crypto;
}
else {
webcrypto = require('crypto');
}
if (setGlobal) {
global.crypto = webcrypto;
}
return webcrypto;
};
exports.globalCrypto = globalCrypto;
const sanitizedJwk = (input) => {
const inputJwk = typeof input['toJsonDTO'] === 'function' ? input['toJsonDTO']() : Object.assign({}, input); // KMP code can expose this. It converts a KMP JWK with mangled names into a clean JWK
const jwk = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, inputJwk), (inputJwk.x && { x: base64ToBase64Url(inputJwk.x) })), (inputJwk.y && { y: base64ToBase64Url(inputJwk.y) })), (inputJwk.d && { d: base64ToBase64Url(inputJwk.d) })), (inputJwk.n && { n: base64ToBase64Url(inputJwk.n) })), (inputJwk.e && { e: base64ToBase64Url(inputJwk.e) })), (inputJwk.k && { k: base64ToBase64Url(inputJwk.k) }));
return removeNulls(jwk);
};
exports.sanitizedJwk = sanitizedJwk;
const base64ToBase64Url = (input) => {
return input.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
};
/**
*
*/
function verifyRawSignature(_a) {
return __awaiter(this, arguments, void 0, function* ({ data, signature, key: inputKey, opts, }) {
var _b, _c;
/**
* Converts a Base64URL-encoded JWK property to a BigInt.
* @param jwkProp - The Base64URL-encoded string.
* @returns The BigInt representation of the decoded value.
*/
function jwkPropertyToBigInt(jwkProp) {
// Decode Base64URL to Uint8Array
const byteArray = u8a.fromString(jwkProp, 'base64url');
// Convert Uint8Array to hexadecimal string and then to BigInt
const hex = u8a.toString(byteArray, 'hex');
return BigInt(`0x${hex}`);
}
const subtle = (_b = cryptoArg === null || cryptoArg === void 0 ? void 0 : cryptoArg.subtle) !== null && _b !== void 0 ? _b : crypto.subtle;
const publicKey = yield subtle.importKey('jwk', key, {
name: alg,
namedCurve: crv,
}, true, ['verify']);
return subtle.verify({
name: alg,
hash: 'SHA-256', // fixme; make arg
}, publicKey, signature, data);
try {
(0, debug_1.default)(`verifyRawSignature for: ${inputKey}`);
const jwk = (0, exports.sanitizedJwk)(inputKey);
(0, jwk_jcs_1.validateJwk)(jwk, { crvOptional: true });
const keyType = (0, exports.keyTypeFromCryptographicSuite)({ crv: jwk.crv, kty: jwk.kty, alg: jwk.alg });
const publicKeyHex = yield (0, exports.jwkToRawHexKey)(jwk);
// TODO: We really should look at the signature alg first if provided! From key type should be the last resort
switch (keyType) {
case 'Secp256k1':
return secp256k1_1.secp256k1.verify(signature, data, publicKeyHex, { format: 'compact', prehash: true });
case 'Secp256r1':
return p256_1.p256.verify(signature, data, publicKeyHex, { format: 'compact', prehash: true });
case 'Secp384r1':
return p384_1.p384.verify(signature, data, publicKeyHex, { format: 'compact', prehash: true });
case 'Secp521r1':
return p521_1.p521.verify(signature, data, publicKeyHex, { format: 'compact', prehash: true });
case 'Ed25519':
return ed25519_1.ed25519.verify(signature, data, u8a.fromString(publicKeyHex, 'hex'));
case 'Bls12381G1':
case 'Bls12381G2':
return bls12_381_1.bls12_381.verify(signature, data, u8a.fromString(publicKeyHex, 'hex'));
case 'RSA': {
const signatureAlgorithm = (_c = (_b = opts === null || opts === void 0 ? void 0 : opts.signatureAlg) !== null && _b !== void 0 ? _b : jwk.alg) !== null && _c !== void 0 ? _c : ssi_types_1.JoseSignatureAlgorithm.PS256;
const hashAlg = signatureAlgorithm === (ssi_types_1.JoseSignatureAlgorithm.RS512 || ssi_types_1.JoseSignatureAlgorithm.PS512)
? sha512_1.sha512
: signatureAlgorithm === (ssi_types_1.JoseSignatureAlgorithm.RS384 || ssi_types_1.JoseSignatureAlgorithm.PS384)
? sha512_1.sha384
: sha256_1.sha256;
switch (signatureAlgorithm) {
case ssi_types_1.JoseSignatureAlgorithm.RS256:
return rsa.PKCS1_SHA256.verify({
n: jwkPropertyToBigInt(jwk.n),
e: jwkPropertyToBigInt(jwk.e),
}, data, signature);
case ssi_types_1.JoseSignatureAlgorithm.RS384:
return rsa.PKCS1_SHA384.verify({
n: jwkPropertyToBigInt(jwk.n),
e: jwkPropertyToBigInt(jwk.e),
}, data, signature);
case ssi_types_1.JoseSignatureAlgorithm.RS512:
return rsa.PKCS1_SHA512.verify({
n: jwkPropertyToBigInt(jwk.n),
e: jwkPropertyToBigInt(jwk.e),
}, data, signature);
case ssi_types_1.JoseSignatureAlgorithm.PS256:
case ssi_types_1.JoseSignatureAlgorithm.PS384:
case ssi_types_1.JoseSignatureAlgorithm.PS512:
return rsa.PSS(hashAlg, rsa.mgf1(hashAlg)).verify({
n: jwkPropertyToBigInt(jwk.n),
e: jwkPropertyToBigInt(jwk.e),
}, data, signature);
}
}
}
throw Error(`Unsupported key type for signature validation: ${keyType}`);
}
catch (error) {
exports.logger.error(`Error: ${error}`);
throw error;
}
});
}
//# sourceMappingURL=functions.js.map

@@ -0,3 +1,16 @@

import { JWK } from '@sphereon/ssi-types';
import type { ByteView } from 'multiformats/codecs/interface';
/**
* Checks if the JWK is valid. It must contain all the required members.
*
* @see https://www.rfc-editor.org/rfc/rfc7518#section-6
* @see https://www.rfc-editor.org/rfc/rfc8037#section-2
*
* @param jwk - The JWK to check.
* @param opts
*/
export declare function validateJwk(jwk: any, opts?: {
crvOptional?: boolean;
}): void;
/**
* Extracts the required members of the JWK and canonicalizes it.

@@ -8,24 +21,3 @@ *

*/
export declare function minimalJwk(jwk: any): {
crv: any;
kty: any;
x: any;
y: any;
e?: undefined;
n?: undefined;
} | {
crv: any;
kty: any;
x: any;
y?: undefined;
e?: undefined;
n?: undefined;
} | {
e: any;
kty: any;
n: any;
crv?: undefined;
x?: undefined;
y?: undefined;
};
export declare function minimalJwk(jwk: any): JWK;
/**

@@ -32,0 +24,0 @@ * Encodes a JWK into a Uint8Array. Only the required JWK members are encoded.

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateJwk = validateJwk;
exports.minimalJwk = minimalJwk;

@@ -11,3 +9,2 @@ exports.jwkJcsEncode = jwkJcsEncode;

const web_encoding_1 = require("web-encoding");
const lodash_isplainobject_1 = __importDefault(require("lodash.isplainobject"));
const textEncoder = new web_encoding_1.TextEncoder();

@@ -20,4 +17,8 @@ const textDecoder = new web_encoding_1.TextDecoder();

* @param description - Description of the value to check.
* @param optional
*/
function check(value, description) {
function check(value, description, optional = false) {
if (optional && !value) {
return;
}
if (typeof value !== 'string' || !value) {

@@ -32,5 +33,5 @@ throw new Error(`${description} missing or invalid`);

*/
function validatePlainObject(value) {
if (!(0, lodash_isplainobject_1.default)(value)) {
throw new Error('JWK must be an object');
function assertObject(value) {
if (!value || typeof value !== 'object') {
throw new Error('Value must be an object');
}

@@ -45,5 +46,8 @@ }

* @param jwk - The JWK to check.
* @param opts
*/
function validateJwk(jwk) {
validatePlainObject(jwk);
function validateJwk(jwk, opts) {
assertObject(jwk);
const { crvOptional = false } = opts !== null && opts !== void 0 ? opts : {};
check(jwk.kty, '"kty" (Key Type) Parameter', false);
// Check JWK required members based on the key type

@@ -55,3 +59,3 @@ switch (jwk.kty) {

case 'EC':
check(jwk.crv, '"crv" (Curve) Parameter');
check(jwk.crv, '"crv" (Curve) Parameter', crvOptional);
check(jwk.x, '"x" (X Coordinate) Parameter');

@@ -64,3 +68,3 @@ check(jwk.y, '"y" (Y Coordinate) Parameter');

case 'OKP':
check(jwk.crv, '"crv" (Subtype of Key Pair) Parameter');
check(jwk.crv, '"crv" (Subtype of Key Pair) Parameter', crvOptional); // Shouldn't this one always be true as crv is not always present?
check(jwk.x, '"x" (Public Key) Parameter');

@@ -90,5 +94,5 @@ break;

case 'EC':
return { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y };
return Object.assign(Object.assign({}, (jwk.crv && { crv: jwk.crv })), { kty: jwk.kty, x: jwk.x, y: jwk.y });
case 'OKP':
return { crv: jwk.crv, kty: jwk.kty, x: jwk.x };
return Object.assign(Object.assign({}, (jwk.crv && { crv: jwk.crv })), { kty: jwk.kty, x: jwk.x });
case 'RSA':

@@ -95,0 +99,0 @@ return { e: jwk.e, kty: jwk.kty, n: jwk.n };

import { IKey, MinimalImportableKey } from '@veramo/core';
export declare const JWK_JCS_PUB_NAME: "jwk_jcs-pub";
export declare const JWK_JCS_PUB_PREFIX = 60241;
export type TKeyType = 'Ed25519' | 'Secp256k1' | 'Secp256r1' | 'X25519' | 'Bls12381G1' | 'Bls12381G2' | 'RSA';
export type TKeyType = 'Ed25519' | 'Secp256k1' | 'Secp256r1' | 'Secp384r1' | 'Secp521r1' | 'X25519' | 'Bls12381G1' | 'Bls12381G2' | 'RSA';
export declare enum Key {

@@ -31,3 +31,3 @@ Ed25519 = "Ed25519",

key?: Partial<MinimalImportableKey>;
type?: TKeyType;
type?: Exclude<TKeyType, 'Secp384r1' | 'Secp521r1'>;
use?: JwkKeyUse;

@@ -43,4 +43,6 @@ x509?: X509Opts;

export type KeyTypeFromCryptographicSuiteArgs = {
suite: string;
crv?: string;
kty?: string;
alg?: string;
};
//# sourceMappingURL=key-util-types.d.ts.map
{
"name": "@sphereon/ssi-sdk-ext.key-utils",
"description": "Sphereon SSI-SDK plugin for key creation.",
"version": "0.26.1-feature.SPRIND.116.9+9afc487",
"version": "0.26.1-feature.SPRIND.116.44+f1862bf",
"source": "src/index.ts",

@@ -14,7 +14,7 @@ "main": "dist/index.js",

"@ethersproject/random": "^5.7.0",
"@sphereon/ssi-sdk-ext.x509-utils": "0.26.1-feature.SPRIND.116.9+9afc487",
"@noble/curves": "1.7.0",
"@noble/hashes": "1.6.1",
"@sphereon/ssi-sdk-ext.x509-utils": "0.26.1-feature.SPRIND.116.44+f1862bf",
"@sphereon/ssi-types": "0.30.2-feature.SDK.41.oidf.support.286",
"@stablelib/ed25519": "^1.0.3",
"@stablelib/sha256": "^1.0.1",
"@stablelib/sha512": "^1.0.1",
"@trust/keyto": "^1.0.1",

@@ -27,2 +27,3 @@ "@veramo/core": "4.2.0",

"lodash.isplainobject": "^4.0.6",
"micro-rsa-dsa-dh": "^0.1.0",
"multiformats": "^9.9.0",

@@ -54,3 +55,3 @@ "uint8arrays": "^3.1.1",

],
"gitHead": "9afc4871e65ef851cd4c479ace388c0651243dca"
"gitHead": "f1862bf57b3488fffaad2222174ed6927e5e3a05"
}

@@ -17,6 +17,7 @@ import {

} from '@sphereon/ssi-types'
import { removeNulls } from './functions'
export function coseKeyToJwk(coseKey: ICoseKeyJson): JWK {
const { x5chain, key_ops, crv, alg, baseIV, kty, ...rest } = coseKey
return {
return removeNulls({
...rest,

@@ -29,3 +30,3 @@ kty: coseToJoseKty(kty),

...(x5chain && { x5c: x5chain }),
} satisfies JWK
}) satisfies JWK
}

@@ -35,3 +36,3 @@

const { x5c, key_ops, crv, alg, iv, kty, ...rest } = jwk
return {
return removeNulls({
...rest,

@@ -44,3 +45,3 @@ kty: joseToCoseKty(kty),

...(x5c && { x5chain: x5c }),
} satisfies ICoseKeyJson
} satisfies ICoseKeyJson)
}

@@ -47,0 +48,0 @@

@@ -1,7 +0,8 @@

import { hash as sha256 } from '@stablelib/sha256'
import { hash as sha512 } from '@stablelib/sha512'
import { sha256 } from '@noble/hashes/sha256'
import { sha384, sha512 } from '@noble/hashes/sha512'
import { Hasher } from '@sphereon/ssi-types'
import * as u8a from 'uint8arrays'
import { SupportedEncodings } from 'uint8arrays/to-string'
export type HashAlgorithm = 'SHA-256' | 'SHA-512'
export type HashAlgorithm = 'SHA-256' | 'SHA-384' | 'SHA-512'
export type TDigestMethod = (input: string, encoding?: SupportedEncodings) => string

@@ -14,2 +15,4 @@

return { hashAlgorithm: 'SHA-256', digestMethod: sha256DigestMethod, hash: sha256 }
} else if (hashAlgorithm === 'SHA-384') {
return { hashAlgorithm: 'SHA-384', digestMethod: sha384DigestMethod, hash: sha384 }
} else {

@@ -20,2 +23,7 @@ return { hashAlgorithm: 'SHA-512', digestMethod: sha512DigestMethod, hash: sha512 }

export const shaHasher: Hasher = (input: string, alg: string): Uint8Array => {
const hashAlgorithm: HashAlgorithm = alg.includes('384') ? 'SHA-384' : alg.includes('512') ? 'SHA-512' : 'SHA-256'
return digestMethodParams(hashAlgorithm).hash(u8a.fromString(input, 'utf-8'))
}
const sha256DigestMethod = (input: string, encoding: SupportedEncodings = 'base16'): string => {

@@ -25,2 +33,6 @@ return u8a.toString(sha256(u8a.fromString(input, 'utf-8')), encoding)

const sha384DigestMethod = (input: string, encoding: SupportedEncodings = 'base16'): string => {
return u8a.toString(sha384(u8a.fromString(input, 'utf-8')), encoding)
}
const sha512DigestMethod = (input: string, encoding: SupportedEncodings = 'base16'): string => {

@@ -27,0 +39,0 @@ return u8a.toString(sha512(u8a.fromString(input, 'utf-8')), encoding)

import { randomBytes } from '@ethersproject/random'
// Do not change these require statements to imports before we change to ESM. Breaks external CJS packages depending on this module
import { bls12_381 } from '@noble/curves/bls12-381'
import { ed25519 } from '@noble/curves/ed25519'
import { p256 } from '@noble/curves/p256'
import { p384 } from '@noble/curves/p384'
import { p521 } from '@noble/curves/p521'
import { secp256k1 } from '@noble/curves/secp256k1'
import { sha256 } from '@noble/hashes/sha256'
import { sha384, sha512 } from '@noble/hashes/sha512'
import { generateRSAKeyAsPEM, hexToBase64, hexToPEM, PEMToJwk, privateKeyHexFromPEM } from '@sphereon/ssi-sdk-ext.x509-utils'
import { JoseCurve, JoseSignatureAlgorithm, JwkKeyType, JWK, Loggers } from '@sphereon/ssi-types'
import { JoseCurve, JoseSignatureAlgorithm, JWK, JwkKeyType, Loggers } from '@sphereon/ssi-types'
import { generateKeyPair as generateSigningKeyPair } from '@stablelib/ed25519'
import { IAgentContext, IKey, IKeyManager, ManagedKeyInfo, MinimalImportableKey } from '@veramo/core'
import debug from 'debug'
import { JsonWebKey } from 'did-resolver'
import elliptic from 'elliptic'
import * as rsa from 'micro-rsa-dsa-dh/rsa.js'
import * as u8a from 'uint8arrays'
import { digestMethodParams } from './digest-methods'
import { validateJwk } from './jwk-jcs'
import {

@@ -171,3 +183,4 @@ ENC_KEY_ALGS,

export const calculateJwkThumbprint = (args: { jwk: JWK; digestAlgorithm?: 'sha256' | 'sha512' }): string => {
const { jwk, digestAlgorithm = 'sha256' } = args
const { digestAlgorithm = 'sha256' } = args
const jwk = sanitizedJwk(args.jwk)
let components

@@ -255,6 +268,98 @@ switch (jwk.kty) {

}
return jwk
return sanitizedJwk(jwk)
}
/**
* Convert a JWK to a raw hex key.
* Currently supports `RSA` and `EC` keys. Extendable for other key types.
* @param jwk - The JSON Web Key object.
* @returns A string representing the key in raw hexadecimal format.
*/
export const jwkToRawHexKey = async (jwk: JWK): Promise<string> => {
// TODO: Probably makes sense to have an option to do the same for private keys
jwk = sanitizedJwk(jwk)
if (jwk.kty === 'RSA') {
return rsaJwkToRawHexKey(jwk)
} else if (jwk.kty === 'EC') {
return ecJwkToRawHexKey(jwk)
} else if (jwk.kty === 'OKP') {
return okpJwkToRawHexKey(jwk)
} else if (jwk.kty === 'oct') {
return octJwkToRawHexKey(jwk)
} else {
throw new Error(`Unsupported key type: ${jwk.kty}`)
}
}
/**
* Convert an RSA JWK to a raw hex key.
* @param jwk - The RSA JWK object.
* @returns A string representing the RSA key in raw hexadecimal format.
*/
function rsaJwkToRawHexKey(jwk: JsonWebKey): string {
jwk = sanitizedJwk(jwk)
if (!jwk.n || !jwk.e) {
throw new Error("RSA JWK must contain 'n' and 'e' properties.")
}
// We are converting from base64 to base64url to be sure. The spec uses base64url, but in the wild we sometimes encounter a base64 string
const modulus = u8a.fromString(jwk.n.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''), 'base64url') // 'n' is the modulus
const exponent = u8a.fromString(jwk.e.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''), 'base64url') // 'e' is the exponent
return u8a.toString(modulus, 'hex') + u8a.toString(exponent, 'hex')
}
/**
* Convert an EC JWK to a raw hex key.
* @param jwk - The EC JWK object.
* @returns A string representing the EC key in raw hexadecimal format.
*/
function ecJwkToRawHexKey(jwk: JsonWebKey): string {
jwk = sanitizedJwk(jwk)
if (!jwk.x || !jwk.y) {
throw new Error("EC JWK must contain 'x' and 'y' properties.")
}
// We are converting from base64 to base64url to be sure. The spec uses base64url, but in the wild we sometimes encounter a base64 string
const x = u8a.fromString(jwk.x.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''), 'base64url')
const y = u8a.fromString(jwk.y.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''), 'base64url')
return '04' + u8a.toString(x, 'hex') + u8a.toString(y, 'hex')
}
/**
* Convert an EC JWK to a raw hex key.
* @param jwk - The EC JWK object.
* @returns A string representing the EC key in raw hexadecimal format.
*/
function okpJwkToRawHexKey(jwk: JsonWebKey): string {
jwk = sanitizedJwk(jwk)
if (!jwk.x) {
throw new Error("OKP JWK must contain 'x' property.")
}
// We are converting from base64 to base64url to be sure. The spec uses base64url, but in the wild we sometimes encounter a base64 string
const x = u8a.fromString(jwk.x.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''), 'base64url')
return u8a.toString(x, 'hex')
}
/**
* Convert an octet JWK to a raw hex key.
* @param jwk - The octet JWK object.
* @returns A string representing the octet key in raw hexadecimal format.
*/
function octJwkToRawHexKey(jwk: JsonWebKey): string {
jwk = sanitizedJwk(jwk)
if (!jwk.k) {
throw new Error("Octet JWK must contain 'k' property.")
}
// We are converting from base64 to base64url to be sure. The spec uses base64url, but in the wild we sometimes encounter a base64 string
const key = u8a.fromString(jwk.k.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''), 'base64url')
return u8a.toString(key, 'hex')
}
/**
* Determines the use param based upon the key/signature type or supplied use value.

@@ -315,3 +420,3 @@ *

return {
return sanitizedJwk({
alg: JoseSignatureAlgorithm.ES256K,

@@ -324,3 +429,3 @@ ...(use !== undefined && { use }),

...(opts?.isPrivateKey && { d: hexToBase64(keyPair.getPrivate('hex'), 'base64url') }),
}
})
}

@@ -348,3 +453,3 @@

const pubPoint = keyPair.getPublic()
return {
return sanitizedJwk({
alg: JoseSignatureAlgorithm.ES256,

@@ -357,3 +462,3 @@ ...(use !== undefined && { use }),

...(opts?.isPrivateKey && { d: hexToBase64(keyPair.getPrivate('hex'), 'base64url') }),
}
})
}

@@ -376,3 +481,3 @@

const { use } = opts ?? {}
return {
return sanitizedJwk({
alg: JoseSignatureAlgorithm.EdDSA,

@@ -383,16 +488,29 @@ ...(use !== undefined && { use }),

x: hexToBase64(publicKeyHex, 'base64url'),
}
})
}
const toRSAJwk = (publicKeyHex: string, opts?: { use?: JwkKeyUse; key?: IKey | MinimalImportableKey }): JWK => {
const { key } = opts ?? {}
const meta = opts?.key?.meta
if (meta?.publicKeyJwk || meta?.publicKeyPEM) {
if (meta?.publicKeyJwk) {
return meta.publicKeyJwk as JWK
}
const publicKeyPEM = meta?.publicKeyPEM ?? hexToPEM(publicKeyHex, 'public')
return PEMToJwk(publicKeyPEM, 'public') as JWK
}
// exponent (e) is 5 chars long, rest is modulus (n)
// const publicKey = publicKeyHex
// assertProperKeyLength(publicKey, [2048, 3072, 4096])
const exponent = publicKeyHex.slice(-5)
const modulus = publicKeyHex.slice(0, -5)
// const modulusBitLength = (modulus.length / 2) * 8
if (key?.meta?.publicKeyJwk) {
return key.meta.publicKeyJwk as JWK
}
const publicKeyPEM = key?.meta?.publicKeyPEM ?? hexToPEM(publicKeyHex, 'public')
return PEMToJwk(publicKeyPEM, 'public') as JWK
// const alg = modulusBitLength === 2048 ? JoseSignatureAlgorithm.RS256 : modulusBitLength === 3072 ? JoseSignatureAlgorithm.RS384 : modulusBitLength === 4096 ? JoseSignatureAlgorithm.RS512 : undefined
return sanitizedJwk({
kty: 'RSA',
n: hexToBase64(modulus, 'base64url'),
e: hexToBase64(exponent, 'base64url'),
// ...(alg && { alg }),
})
}

@@ -538,2 +656,6 @@

return JoseSignatureAlgorithm.ES256
case 'Secp384r1':
return JoseSignatureAlgorithm.ES384
case 'Secp521r1':
return JoseSignatureAlgorithm.ES512
case 'Secp256k1':

@@ -548,5 +670,18 @@ return JoseSignatureAlgorithm.ES256K

export const keyTypeFromCryptographicSuite = (args: KeyTypeFromCryptographicSuiteArgs): TKeyType => {
const { suite } = args
switch (suite) {
const { crv, kty, alg } = args
switch (alg) {
case 'RSASSA-PSS':
case 'RS256':
case 'RS384':
case 'RS512':
case 'PS256':
case 'PS384':
case 'PS512':
return 'RSA'
}
switch (crv) {
case 'EdDSA':
case 'Ed25519':
case 'Ed25519Signature2018':

@@ -559,48 +694,178 @@ case 'Ed25519Signature2020':

case 'ECDSA':
case 'P-256':
return 'Secp256r1'
case 'ES384':
case 'P-384':
return 'Secp384r1'
case 'ES512':
case 'P-521':
return 'Secp521r1'
case 'EcdsaSecp256k1Signature2019':
case 'secp256k1':
case 'ES256K':
return 'Secp256k1'
default:
throw new Error(`Cryptographic suite '${suite}' not supported`)
}
if (kty) {
return kty as TKeyType
}
throw new Error(`Cryptographic suite '${crv}' not supported`)
}
export async function verifySignatureWithSubtle({
export function removeNulls<T>(obj: T | any) {
Object.keys(obj).forEach((key) => {
if (obj[key] && typeof obj[key] === 'object') removeNulls(obj[key])
else if (obj[key] == null) delete obj[key]
})
return obj
}
export const globalCrypto = (setGlobal: boolean, suppliedCrypto?: Crypto): Crypto => {
let webcrypto: Crypto
if (typeof suppliedCrypto !== 'undefined') {
webcrypto = suppliedCrypto
} else if (typeof crypto !== 'undefined') {
webcrypto = crypto
} else if (typeof global.crypto !== 'undefined') {
webcrypto = global.crypto
} else if (typeof global.window?.crypto?.subtle !== 'undefined') {
webcrypto = global.window.crypto
} else {
webcrypto = require('crypto') as Crypto
}
if (setGlobal) {
global.crypto = webcrypto
}
return webcrypto
}
export const sanitizedJwk = (input: JWK | JsonWebKey): JWK => {
const inputJwk = typeof input['toJsonDTO'] === 'function' ? input['toJsonDTO']() : {...input} as JWK // KMP code can expose this. It converts a KMP JWK with mangled names into a clean JWK
const jwk = {
...inputJwk,
...(inputJwk.x && { x: base64ToBase64Url(inputJwk.x as string) }),
...(inputJwk.y && { y: base64ToBase64Url(inputJwk.y as string) }),
...(inputJwk.d && { d: base64ToBase64Url(inputJwk.d as string) }),
...(inputJwk.n && { n: base64ToBase64Url(inputJwk.n as string) }),
...(inputJwk.e && { e: base64ToBase64Url(inputJwk.e as string) }),
...(inputJwk.k && { k: base64ToBase64Url(inputJwk.k as string) }),
} as JWK
return removeNulls(jwk)
}
const base64ToBase64Url = (input: string): string => {
return input.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
}
/**
*
*/
export async function verifyRawSignature({
data,
signature,
key,
crypto: cryptoArg,
key: inputKey,
opts,
}: {
data: Uint8Array
signature: Uint8Array
key: JsonWebKey
crypto?: Crypto
key: JWK
opts?: {
signatureAlg?: JoseSignatureAlgorithm
}
}) {
let { alg, crv } = key
if (alg === 'ES256' || !alg) {
alg = 'ECDSA'
/**
* Converts a Base64URL-encoded JWK property to a BigInt.
* @param jwkProp - The Base64URL-encoded string.
* @returns The BigInt representation of the decoded value.
*/
function jwkPropertyToBigInt(jwkProp: string): bigint {
// Decode Base64URL to Uint8Array
const byteArray = u8a.fromString(jwkProp, 'base64url')
// Convert Uint8Array to hexadecimal string and then to BigInt
const hex = u8a.toString(byteArray, 'hex')
return BigInt(`0x${hex}`)
}
const subtle = cryptoArg?.subtle ?? crypto.subtle
const publicKey = await subtle.importKey(
'jwk',
key,
{
name: alg,
namedCurve: crv,
} as EcKeyImportParams,
true,
['verify']
)
try {
debug(`verifyRawSignature for: ${inputKey}`)
const jwk = sanitizedJwk(inputKey)
validateJwk(jwk, { crvOptional: true })
const keyType = keyTypeFromCryptographicSuite({ crv: jwk.crv, kty: jwk.kty, alg: jwk.alg })
const publicKeyHex = await jwkToRawHexKey(jwk)
return subtle.verify(
{
name: alg as string,
hash: 'SHA-256', // fixme; make arg
},
publicKey,
signature,
data
)
// TODO: We really should look at the signature alg first if provided! From key type should be the last resort
switch (keyType) {
case 'Secp256k1':
return secp256k1.verify(signature, data, publicKeyHex, { format: 'compact', prehash: true })
case 'Secp256r1':
return p256.verify(signature, data, publicKeyHex, { format: 'compact', prehash: true })
case 'Secp384r1':
return p384.verify(signature, data, publicKeyHex, { format: 'compact', prehash: true })
case 'Secp521r1':
return p521.verify(signature, data, publicKeyHex, { format: 'compact', prehash: true })
case 'Ed25519':
return ed25519.verify(signature, data, u8a.fromString(publicKeyHex, 'hex'))
case 'Bls12381G1':
case 'Bls12381G2':
return bls12_381.verify(signature, data, u8a.fromString(publicKeyHex, 'hex'))
case 'RSA': {
const signatureAlgorithm = opts?.signatureAlg ?? jwk.alg as JoseSignatureAlgorithm | undefined ?? JoseSignatureAlgorithm.PS256
const hashAlg =
signatureAlgorithm === (JoseSignatureAlgorithm.RS512 || JoseSignatureAlgorithm.PS512)
? sha512
: signatureAlgorithm === (JoseSignatureAlgorithm.RS384 || JoseSignatureAlgorithm.PS384)
? sha384
: sha256
switch (signatureAlgorithm) {
case JoseSignatureAlgorithm.RS256:
return rsa.PKCS1_SHA256.verify(
{
n: jwkPropertyToBigInt(jwk.n!),
e: jwkPropertyToBigInt(jwk.e!),
},
data,
signature
)
case JoseSignatureAlgorithm.RS384:
return rsa.PKCS1_SHA384.verify(
{
n: jwkPropertyToBigInt(jwk.n!),
e: jwkPropertyToBigInt(jwk.e!),
},
data,
signature
)
case JoseSignatureAlgorithm.RS512:
return rsa.PKCS1_SHA512.verify(
{
n: jwkPropertyToBigInt(jwk.n!),
e: jwkPropertyToBigInt(jwk.e!),
},
data,
signature
)
case JoseSignatureAlgorithm.PS256:
case JoseSignatureAlgorithm.PS384:
case JoseSignatureAlgorithm.PS512:
return rsa.PSS(hashAlg, rsa.mgf1(hashAlg)).verify(
{
n: jwkPropertyToBigInt(jwk.n!),
e: jwkPropertyToBigInt(jwk.e!),
},
data,
signature
)
}
}
}
throw Error(`Unsupported key type for signature validation: ${keyType}`)
} catch (error: any) {
logger.error(`Error: ${error}`)
throw error
}
}

@@ -0,4 +1,4 @@

import { JWK } from '@sphereon/ssi-types'
import type { ByteView } from 'multiformats/codecs/interface'
import { TextDecoder, TextEncoder } from 'web-encoding'
import isPlainObject from 'lodash.isplainobject'
import type { ByteView } from 'multiformats/codecs/interface'

@@ -13,4 +13,8 @@ const textEncoder = new TextEncoder()

* @param description - Description of the value to check.
* @param optional
*/
function check(value: unknown, description: string) {
function check(value: unknown, description: string, optional: boolean = false) {
if (optional && !value) {
return
}
if (typeof value !== 'string' || !value) {

@@ -26,5 +30,5 @@ throw new Error(`${description} missing or invalid`)

*/
function validatePlainObject(value: unknown) {
if (!isPlainObject(value)) {
throw new Error('JWK must be an object')
function assertObject(value: unknown) {
if (!value || typeof value !== 'object') {
throw new Error('Value must be an object')
}

@@ -40,5 +44,9 @@ }

* @param jwk - The JWK to check.
* @param opts
*/
function validateJwk(jwk: any) {
validatePlainObject(jwk)
export function validateJwk(jwk: any, opts?: { crvOptional?: boolean }) {
assertObject(jwk)
const { crvOptional = false } = opts ?? {}
check(jwk.kty, '"kty" (Key Type) Parameter', false)
// Check JWK required members based on the key type

@@ -50,3 +58,3 @@ switch (jwk.kty) {

case 'EC':
check(jwk.crv, '"crv" (Curve) Parameter')
check(jwk.crv, '"crv" (Curve) Parameter', crvOptional)
check(jwk.x, '"x" (X Coordinate) Parameter')

@@ -59,3 +67,3 @@ check(jwk.y, '"y" (Y Coordinate) Parameter')

case 'OKP':
check(jwk.crv, '"crv" (Subtype of Key Pair) Parameter')
check(jwk.crv, '"crv" (Subtype of Key Pair) Parameter', crvOptional) // Shouldn't this one always be true as crv is not always present?
check(jwk.x, '"x" (Public Key) Parameter')

@@ -81,3 +89,3 @@ break

*/
export function minimalJwk(jwk: any) {
export function minimalJwk(jwk: any): JWK {
// "default" case is not needed

@@ -87,5 +95,5 @@ // eslint-disable-next-line default-case

case 'EC':
return { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y }
return { ...(jwk.crv && { crv: jwk.crv }), kty: jwk.kty, x: jwk.x, y: jwk.y }
case 'OKP':
return { crv: jwk.crv, kty: jwk.kty, x: jwk.x }
return { ...(jwk.crv && { crv: jwk.crv }), kty: jwk.kty, x: jwk.x }
case 'RSA':

@@ -92,0 +100,0 @@ return { e: jwk.e, kty: jwk.kty, n: jwk.n }

@@ -6,3 +6,3 @@ import { IKey, MinimalImportableKey } from '@veramo/core'

export type TKeyType = 'Ed25519' | 'Secp256k1' | 'Secp256r1' | 'X25519' | 'Bls12381G1' | 'Bls12381G2' | 'RSA'
export type TKeyType = 'Ed25519' | 'Secp256k1' | 'Secp256r1' | 'Secp384r1' | 'Secp521r1' | 'X25519' | 'Bls12381G1' | 'Bls12381G2' | 'RSA'

@@ -40,3 +40,3 @@ export enum Key {

key?: Partial<MinimalImportableKey> // Optional key to import with only privateKeyHex mandatory. If not specified a key with random kid will be created
type?: TKeyType // The key type. Defaults to Secp256k1
type?: Exclude<TKeyType, 'Secp384r1' | 'Secp521r1'> // The key type. Defaults to Secp256k1. The exclude is there as we do not support it yet for key generation
use?: JwkKeyUse // The key use

@@ -60,3 +60,5 @@ x509?: X509Opts

export type KeyTypeFromCryptographicSuiteArgs = {
suite: string
crv?: string
kty?: string
alg?: string
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc