@libp2p/crypto
Version:
Crypto primitives for libp2p
165 lines • 6.4 kB
JavaScript
import { InvalidParametersError } from '@libp2p/interface';
import { Uint8ArrayList } from 'uint8arraylist';
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string';
import { toString as uint8ArrayToString } from 'uint8arrays/to-string';
import { decodeDer, encodeBitString, encodeInteger, encodeOctetString, encodeSequence } from '../rsa/der.js';
import { ECDSAPrivateKey as ECDSAPrivateKeyClass, ECDSAPublicKey as ECDSAPublicKeyClass } from './ecdsa.js';
import { generateECDSAKey } from './index.js';
// 1.2.840.10045.3.1.7 prime256v1 (ANSI X9.62 named elliptic curve)
const OID_256 = Uint8Array.from([0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07]);
// 1.3.132.0.34 secp384r1 (SECG (Certicom) named elliptic curve)
const OID_384 = Uint8Array.from([0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22]);
// 1.3.132.0.35 secp521r1 (SECG (Certicom) named elliptic curve)
const OID_521 = Uint8Array.from([0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x23]);
const P_256_KEY_JWK = {
ext: true,
kty: 'EC',
crv: 'P-256'
};
const P_384_KEY_JWK = {
ext: true,
kty: 'EC',
crv: 'P-384'
};
const P_521_KEY_JWK = {
ext: true,
kty: 'EC',
crv: 'P-521'
};
const P_256_KEY_LENGTH = 32;
const P_384_KEY_LENGTH = 48;
const P_521_KEY_LENGTH = 66;
export function unmarshalECDSAPrivateKey(bytes) {
const message = decodeDer(bytes);
return pkiMessageToECDSAPrivateKey(message);
}
export function pkiMessageToECDSAPrivateKey(message) {
const privateKey = message[1];
const d = uint8ArrayToString(privateKey, 'base64url');
const coordinates = message[2][1][0];
const offset = 1;
let x;
let y;
if (privateKey.byteLength === P_256_KEY_LENGTH) {
x = uint8ArrayToString(coordinates.subarray(offset, offset + P_256_KEY_LENGTH), 'base64url');
y = uint8ArrayToString(coordinates.subarray(offset + P_256_KEY_LENGTH), 'base64url');
return new ECDSAPrivateKeyClass({
...P_256_KEY_JWK,
key_ops: ['sign'],
d,
x,
y
});
}
if (privateKey.byteLength === P_384_KEY_LENGTH) {
x = uint8ArrayToString(coordinates.subarray(offset, offset + P_384_KEY_LENGTH), 'base64url');
y = uint8ArrayToString(coordinates.subarray(offset + P_384_KEY_LENGTH), 'base64url');
return new ECDSAPrivateKeyClass({
...P_384_KEY_JWK,
key_ops: ['sign'],
d,
x,
y
});
}
if (privateKey.byteLength === P_521_KEY_LENGTH) {
x = uint8ArrayToString(coordinates.subarray(offset, offset + P_521_KEY_LENGTH), 'base64url');
y = uint8ArrayToString(coordinates.subarray(offset + P_521_KEY_LENGTH), 'base64url');
return new ECDSAPrivateKeyClass({
...P_521_KEY_JWK,
key_ops: ['sign'],
d,
x,
y
});
}
throw new InvalidParametersError(`Private key length was wrong length, got ${privateKey.byteLength}, expected 32, 48 or 66`);
}
export function unmarshalECDSAPublicKey(bytes) {
const message = decodeDer(bytes);
return pkiMessageToECDSAPublicKey(message);
}
export function pkiMessageToECDSAPublicKey(message) {
const coordinates = message[1][1][0];
const offset = 1;
let x;
let y;
if (coordinates.byteLength === ((P_256_KEY_LENGTH * 2) + 1)) {
x = uint8ArrayToString(coordinates.subarray(offset, offset + P_256_KEY_LENGTH), 'base64url');
y = uint8ArrayToString(coordinates.subarray(offset + P_256_KEY_LENGTH), 'base64url');
return new ECDSAPublicKeyClass({
...P_256_KEY_JWK,
key_ops: ['verify'],
x,
y
});
}
if (coordinates.byteLength === ((P_384_KEY_LENGTH * 2) + 1)) {
x = uint8ArrayToString(coordinates.subarray(offset, offset + P_384_KEY_LENGTH), 'base64url');
y = uint8ArrayToString(coordinates.subarray(offset + P_384_KEY_LENGTH), 'base64url');
return new ECDSAPublicKeyClass({
...P_384_KEY_JWK,
key_ops: ['verify'],
x,
y
});
}
if (coordinates.byteLength === ((P_521_KEY_LENGTH * 2) + 1)) {
x = uint8ArrayToString(coordinates.subarray(offset, offset + P_521_KEY_LENGTH), 'base64url');
y = uint8ArrayToString(coordinates.subarray(offset + P_521_KEY_LENGTH), 'base64url');
return new ECDSAPublicKeyClass({
...P_521_KEY_JWK,
key_ops: ['verify'],
x,
y
});
}
throw new InvalidParametersError(`coordinates were wrong length, got ${coordinates.byteLength}, expected 65, 97 or 133`);
}
export function privateKeyToPKIMessage(privateKey) {
return encodeSequence([
encodeInteger(Uint8Array.from([1])), // header
encodeOctetString(uint8ArrayFromString(privateKey.d ?? '', 'base64url')), // body
encodeSequence([
getOID(privateKey.crv)
], 0xA0),
encodeSequence([
encodeBitString(new Uint8ArrayList(Uint8Array.from([0x04]), uint8ArrayFromString(privateKey.x ?? '', 'base64url'), uint8ArrayFromString(privateKey.y ?? '', 'base64url')))
], 0xA1)
]).subarray();
}
export function publicKeyToPKIMessage(publicKey) {
return encodeSequence([
encodeInteger(Uint8Array.from([1])), // header
encodeSequence([
getOID(publicKey.crv)
], 0xA0),
encodeSequence([
encodeBitString(new Uint8ArrayList(Uint8Array.from([0x04]), uint8ArrayFromString(publicKey.x ?? '', 'base64url'), uint8ArrayFromString(publicKey.y ?? '', 'base64url')))
], 0xA1)
]).subarray();
}
function getOID(curve) {
if (curve === 'P-256') {
return OID_256;
}
if (curve === 'P-384') {
return OID_384;
}
if (curve === 'P-521') {
return OID_521;
}
throw new InvalidParametersError(`Invalid curve ${curve}`);
}
export async function generateECDSAKeyPair(curve = 'P-256') {
const key = await generateECDSAKey(curve);
return new ECDSAPrivateKeyClass(key.privateKey);
}
export function ensureECDSAKey(key, length) {
key = Uint8Array.from(key ?? []);
if (key.length !== length) {
throw new InvalidParametersError(`Key must be a Uint8Array of length ${length}, got ${key.length}`);
}
return key;
}
//# sourceMappingURL=utils.js.map