UNPKG

@libp2p/crypto

Version:
172 lines • 6.11 kB
import { InvalidParametersError, InvalidPublicKeyError } from '@libp2p/interface'; import { sha256 } from '@noble/hashes/sha256'; import { create } from 'multiformats/hashes/digest'; import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'; import { toString as uint8ArrayToString } from 'uint8arrays/to-string'; import * as pb from '../keys.js'; import { decodeDer, encodeBitString, encodeInteger, encodeSequence } from './der.js'; import { RSAPrivateKey as RSAPrivateKeyClass, RSAPublicKey as RSAPublicKeyClass } from './rsa.js'; import { generateRSAKey, rsaKeySize } from './index.js'; export const MAX_RSA_KEY_SIZE = 8192; const SHA2_256_CODE = 0x12; const MAX_RSA_JWK_SIZE = 1062; const RSA_ALGORITHM_IDENTIFIER = Uint8Array.from([ 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 ]); /** * Convert a PKCS#1 in ASN1 DER format to a JWK private key */ export function pkcs1ToJwk(bytes) { const message = decodeDer(bytes); return pkcs1MessageToJwk(message); } /** * Convert a PKCS#1 in ASN1 DER format to a JWK private key */ export function pkcs1MessageToJwk(message) { return { n: uint8ArrayToString(message[1], 'base64url'), e: uint8ArrayToString(message[2], 'base64url'), d: uint8ArrayToString(message[3], 'base64url'), p: uint8ArrayToString(message[4], 'base64url'), q: uint8ArrayToString(message[5], 'base64url'), dp: uint8ArrayToString(message[6], 'base64url'), dq: uint8ArrayToString(message[7], 'base64url'), qi: uint8ArrayToString(message[8], 'base64url'), kty: 'RSA' }; } /** * Convert a JWK private key into PKCS#1 in ASN1 DER format */ export function jwkToPkcs1(jwk) { if (jwk.n == null || jwk.e == null || jwk.d == null || jwk.p == null || jwk.q == null || jwk.dp == null || jwk.dq == null || jwk.qi == null) { throw new InvalidParametersError('JWK was missing components'); } return encodeSequence([ encodeInteger(Uint8Array.from([0])), encodeInteger(uint8ArrayFromString(jwk.n, 'base64url')), encodeInteger(uint8ArrayFromString(jwk.e, 'base64url')), encodeInteger(uint8ArrayFromString(jwk.d, 'base64url')), encodeInteger(uint8ArrayFromString(jwk.p, 'base64url')), encodeInteger(uint8ArrayFromString(jwk.q, 'base64url')), encodeInteger(uint8ArrayFromString(jwk.dp, 'base64url')), encodeInteger(uint8ArrayFromString(jwk.dq, 'base64url')), encodeInteger(uint8ArrayFromString(jwk.qi, 'base64url')) ]).subarray(); } /** * Convert a PKIX in ASN1 DER format to a JWK public key */ export function pkixToJwk(bytes) { const message = decodeDer(bytes, { offset: 0 }); return pkixMessageToJwk(message); } export function pkixMessageToJwk(message) { const keys = decodeDer(message[1], { offset: 0 }); // this looks fragile but DER is a canonical format so we are safe to have // deeply property chains like this return { kty: 'RSA', n: uint8ArrayToString(keys[0], 'base64url'), e: uint8ArrayToString(keys[1], 'base64url') }; } /** * Convert a JWK public key to PKIX in ASN1 DER format */ export function jwkToPkix(jwk) { if (jwk.n == null || jwk.e == null) { throw new InvalidParametersError('JWK was missing components'); } const subjectPublicKeyInfo = encodeSequence([ RSA_ALGORITHM_IDENTIFIER, encodeBitString(encodeSequence([ encodeInteger(uint8ArrayFromString(jwk.n, 'base64url')), encodeInteger(uint8ArrayFromString(jwk.e, 'base64url')) ])) ]); return subjectPublicKeyInfo.subarray(); } /** * Turn PKCS#1 DER bytes into a PrivateKey */ export function pkcs1ToRSAPrivateKey(bytes) { const message = decodeDer(bytes); return pkcs1MessageToRSAPrivateKey(message); } /** * Turn PKCS#1 DER bytes into a PrivateKey */ export function pkcs1MessageToRSAPrivateKey(message) { const jwk = pkcs1MessageToJwk(message); return jwkToRSAPrivateKey(jwk); } /** * Turn a PKIX message into a PublicKey */ export function pkixToRSAPublicKey(bytes, digest) { if (bytes.byteLength >= MAX_RSA_JWK_SIZE) { throw new InvalidPublicKeyError('Key size is too large'); } const message = decodeDer(bytes, { offset: 0 }); return pkixMessageToRSAPublicKey(message, bytes, digest); } export function pkixMessageToRSAPublicKey(message, bytes, digest) { const jwk = pkixMessageToJwk(message); if (digest == null) { const hash = sha256(pb.PublicKey.encode({ Type: pb.KeyType.RSA, Data: bytes })); digest = create(SHA2_256_CODE, hash); } return new RSAPublicKeyClass(jwk, digest); } export function jwkToRSAPrivateKey(jwk) { if (rsaKeySize(jwk) > MAX_RSA_KEY_SIZE) { throw new InvalidParametersError('Key size is too large'); } const keys = jwkToJWKKeyPair(jwk); const hash = sha256(pb.PublicKey.encode({ Type: pb.KeyType.RSA, Data: jwkToPkix(keys.publicKey) })); const digest = create(SHA2_256_CODE, hash); return new RSAPrivateKeyClass(keys.privateKey, new RSAPublicKeyClass(keys.publicKey, digest)); } export async function generateRSAKeyPair(bits) { if (bits > MAX_RSA_KEY_SIZE) { throw new InvalidParametersError('Key size is too large'); } const keys = await generateRSAKey(bits); const hash = sha256(pb.PublicKey.encode({ Type: pb.KeyType.RSA, Data: jwkToPkix(keys.publicKey) })); const digest = create(SHA2_256_CODE, hash); return new RSAPrivateKeyClass(keys.privateKey, new RSAPublicKeyClass(keys.publicKey, digest)); } /** * Takes a jwk key and returns a JWK KeyPair */ export function jwkToJWKKeyPair(key) { if (key == null) { throw new InvalidParametersError('Missing key parameter'); } return { privateKey: key, publicKey: { kty: key.kty, n: key.n, e: key.e } }; } //# sourceMappingURL=utils.js.map