mirror of
https://github.com/swc-project/swc.git
synced 2024-12-25 22:56:11 +03:00
fix(es/minifier): Preserve unused special properties (#9005)
**Related issue:** - https://github.com/vercel/next.js/issues/66378
This commit is contained in:
parent
74a1fd4341
commit
e764ff6f64
@ -849,11 +849,19 @@ impl Optimizer<'_> {
|
|||||||
match &**prop {
|
match &**prop {
|
||||||
Prop::KeyValue(p) => match &p.key {
|
Prop::KeyValue(p) => match &p.key {
|
||||||
PropName::Str(s) => {
|
PropName::Str(s) => {
|
||||||
|
if !can_remove_property(&s.value) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(v) = unknown_used_props.get_mut(&s.value) {
|
if let Some(v) = unknown_used_props.get_mut(&s.value) {
|
||||||
*v = 0;
|
*v = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PropName::Ident(i) => {
|
PropName::Ident(i) => {
|
||||||
|
if !can_remove_property(&i.sym) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(v) = unknown_used_props.get_mut(&i.sym) {
|
if let Some(v) = unknown_used_props.get_mut(&i.sym) {
|
||||||
*v = 0;
|
*v = 0;
|
||||||
}
|
}
|
||||||
@ -861,6 +869,10 @@ impl Optimizer<'_> {
|
|||||||
_ => return None,
|
_ => return None,
|
||||||
},
|
},
|
||||||
Prop::Shorthand(p) => {
|
Prop::Shorthand(p) => {
|
||||||
|
if !can_remove_property(&p.sym) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(v) = unknown_used_props.get_mut(&p.sym) {
|
if let Some(v) = unknown_used_props.get_mut(&p.sym) {
|
||||||
*v = 0;
|
*v = 0;
|
||||||
}
|
}
|
||||||
@ -914,6 +926,10 @@ impl Optimizer<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn can_remove_property(sym: &str) -> bool {
|
||||||
|
!matches!(sym, "toString" | "valueOf")
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct ThisPropertyVisitor {
|
struct ThisPropertyVisitor {
|
||||||
properties: FxHashSet<JsWord>,
|
properties: FxHashSet<JsWord>,
|
||||||
|
353
crates/swc_ecma_minifier/tests/fixture/next/66378/input.js
Normal file
353
crates/swc_ecma_minifier/tests/fixture/next/66378/input.js
Normal file
@ -0,0 +1,353 @@
|
|||||||
|
import { concat, fromString, toString } from "uint8arrays";
|
||||||
|
import { x25519 } from "@noble/curves/ed25519";
|
||||||
|
import { varint } from "multiformats";
|
||||||
|
import { decode, encode } from "multibase";
|
||||||
|
import { secp256k1 } from "@noble/curves/secp256k1";
|
||||||
|
import { p256 } from "@noble/curves/p256";
|
||||||
|
const u8a = {
|
||||||
|
toString,
|
||||||
|
fromString,
|
||||||
|
concat
|
||||||
|
};
|
||||||
|
export function bytesToBase64url(b) {
|
||||||
|
return u8a.toString(b, 'base64url');
|
||||||
|
}
|
||||||
|
export function base64ToBytes(s) {
|
||||||
|
const inputBase64Url = s.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
||||||
|
return u8a.fromString(inputBase64Url, 'base64url');
|
||||||
|
}
|
||||||
|
export function bytesToBase64(b) {
|
||||||
|
return u8a.toString(b, 'base64pad');
|
||||||
|
}
|
||||||
|
export function base58ToBytes(s) {
|
||||||
|
return u8a.fromString(s, 'base58btc');
|
||||||
|
}
|
||||||
|
export function bytesToBase58(b) {
|
||||||
|
return u8a.toString(b, 'base58btc');
|
||||||
|
}
|
||||||
|
export const SUPPORTED_PUBLIC_KEY_TYPES = {
|
||||||
|
ES256: [
|
||||||
|
'JsonWebKey2020',
|
||||||
|
'Multikey',
|
||||||
|
'EcdsaSecp256r1VerificationKey2019'
|
||||||
|
],
|
||||||
|
ES256K: [
|
||||||
|
'EcdsaSecp256k1VerificationKey2019',
|
||||||
|
/**
|
||||||
|
* Equivalent to EcdsaSecp256k1VerificationKey2019 when key is an ethereumAddress
|
||||||
|
*/ 'EcdsaSecp256k1RecoveryMethod2020',
|
||||||
|
/**
|
||||||
|
* @deprecated, supported for backward compatibility. Equivalent to EcdsaSecp256k1VerificationKey2019 when key is
|
||||||
|
* not an ethereumAddress
|
||||||
|
*/ 'Secp256k1VerificationKey2018',
|
||||||
|
/**
|
||||||
|
* @deprecated, supported for backward compatibility. Equivalent to EcdsaSecp256k1VerificationKey2019 when key is
|
||||||
|
* not an ethereumAddress
|
||||||
|
*/ 'Secp256k1SignatureVerificationKey2018',
|
||||||
|
/**
|
||||||
|
* @deprecated, supported for backward compatibility. Equivalent to EcdsaSecp256k1VerificationKey2019 when key is
|
||||||
|
* not an ethereumAddress
|
||||||
|
*/ 'EcdsaPublicKeySecp256k1',
|
||||||
|
/**
|
||||||
|
* TODO - support R1 key as well
|
||||||
|
* 'ConditionalProof2022',
|
||||||
|
*/ 'JsonWebKey2020',
|
||||||
|
'Multikey'
|
||||||
|
],
|
||||||
|
'ES256K-R': [
|
||||||
|
'EcdsaSecp256k1VerificationKey2019',
|
||||||
|
/**
|
||||||
|
* Equivalent to EcdsaSecp256k1VerificationKey2019 when key is an ethereumAddress
|
||||||
|
*/ 'EcdsaSecp256k1RecoveryMethod2020',
|
||||||
|
/**
|
||||||
|
* @deprecated, supported for backward compatibility. Equivalent to EcdsaSecp256k1VerificationKey2019 when key is
|
||||||
|
* not an ethereumAddress
|
||||||
|
*/ 'Secp256k1VerificationKey2018',
|
||||||
|
/**
|
||||||
|
* @deprecated, supported for backward compatibility. Equivalent to EcdsaSecp256k1VerificationKey2019 when key is
|
||||||
|
* not an ethereumAddress
|
||||||
|
*/ 'Secp256k1SignatureVerificationKey2018',
|
||||||
|
/**
|
||||||
|
* @deprecated, supported for backward compatibility. Equivalent to EcdsaSecp256k1VerificationKey2019 when key is
|
||||||
|
* not an ethereumAddress
|
||||||
|
*/ 'EcdsaPublicKeySecp256k1',
|
||||||
|
'ConditionalProof2022',
|
||||||
|
'JsonWebKey2020',
|
||||||
|
'Multikey'
|
||||||
|
],
|
||||||
|
Ed25519: [
|
||||||
|
'ED25519SignatureVerification',
|
||||||
|
'Ed25519VerificationKey2018',
|
||||||
|
'Ed25519VerificationKey2020',
|
||||||
|
'JsonWebKey2020',
|
||||||
|
'Multikey'
|
||||||
|
],
|
||||||
|
EdDSA: [
|
||||||
|
'ED25519SignatureVerification',
|
||||||
|
'Ed25519VerificationKey2018',
|
||||||
|
'Ed25519VerificationKey2020',
|
||||||
|
'JsonWebKey2020',
|
||||||
|
'Multikey'
|
||||||
|
]
|
||||||
|
};
|
||||||
|
export const VM_TO_KEY_TYPE = {
|
||||||
|
Secp256k1SignatureVerificationKey2018: 'Secp256k1',
|
||||||
|
Secp256k1VerificationKey2018: 'Secp256k1',
|
||||||
|
EcdsaSecp256k1VerificationKey2019: 'Secp256k1',
|
||||||
|
EcdsaPublicKeySecp256k1: 'Secp256k1',
|
||||||
|
EcdsaSecp256k1RecoveryMethod2020: 'Secp256k1',
|
||||||
|
EcdsaSecp256r1VerificationKey2019: 'P-256',
|
||||||
|
Ed25519VerificationKey2018: 'Ed25519',
|
||||||
|
Ed25519VerificationKey2020: 'Ed25519',
|
||||||
|
ED25519SignatureVerification: 'Ed25519',
|
||||||
|
X25519KeyAgreementKey2019: 'X25519',
|
||||||
|
X25519KeyAgreementKey2020: 'X25519',
|
||||||
|
ConditionalProof2022: undefined,
|
||||||
|
JsonWebKey2020: undefined,
|
||||||
|
Multikey: undefined
|
||||||
|
};
|
||||||
|
// this is from the multicodec table https://github.com/multiformats/multicodec/blob/master/table.csv
|
||||||
|
export const supportedCodecs = {
|
||||||
|
'ed25519-pub': 0xed,
|
||||||
|
'x25519-pub': 0xec,
|
||||||
|
'secp256k1-pub': 0xe7,
|
||||||
|
'bls12_381-g1-pub': 0xea,
|
||||||
|
'bls12_381-g2-pub': 0xeb,
|
||||||
|
'p256-pub': 0x1200
|
||||||
|
};
|
||||||
|
export const CODEC_TO_KEY_TYPE = {
|
||||||
|
'bls12_381-g1-pub': 'Bls12381G1',
|
||||||
|
'bls12_381-g2-pub': 'Bls12381G2',
|
||||||
|
'ed25519-pub': 'Ed25519',
|
||||||
|
'p256-pub': 'P-256',
|
||||||
|
'secp256k1-pub': 'Secp256k1',
|
||||||
|
'x25519-pub': 'X25519'
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Extracts the raw byte representation of a public key from a VerificationMethod along with an inferred key type
|
||||||
|
* @param pk a VerificationMethod entry from a DIDDocument
|
||||||
|
* @return an object containing the `keyBytes` of the public key and an inferred `keyType`
|
||||||
|
*/ export function extractPublicKeyBytes(pk) {
|
||||||
|
if (pk.publicKeyBase58) {
|
||||||
|
return {
|
||||||
|
keyBytes: base58ToBytes(pk.publicKeyBase58),
|
||||||
|
keyType: VM_TO_KEY_TYPE[pk.type]
|
||||||
|
};
|
||||||
|
} else if (pk.publicKeyBase64) {
|
||||||
|
return {
|
||||||
|
keyBytes: base64ToBytes(pk.publicKeyBase64),
|
||||||
|
keyType: VM_TO_KEY_TYPE[pk.type]
|
||||||
|
};
|
||||||
|
} else if (pk.publicKeyHex) {
|
||||||
|
return {
|
||||||
|
keyBytes: hexToBytes(pk.publicKeyHex),
|
||||||
|
keyType: VM_TO_KEY_TYPE[pk.type]
|
||||||
|
};
|
||||||
|
} else if (pk.publicKeyJwk && pk.publicKeyJwk.crv === 'secp256k1' && pk.publicKeyJwk.x && pk.publicKeyJwk.y) {
|
||||||
|
return {
|
||||||
|
keyBytes: secp256k1.ProjectivePoint.fromAffine({
|
||||||
|
x: bytesToBigInt(base64ToBytes(pk.publicKeyJwk.x)),
|
||||||
|
y: bytesToBigInt(base64ToBytes(pk.publicKeyJwk.y))
|
||||||
|
}).toRawBytes(false),
|
||||||
|
keyType: 'Secp256k1'
|
||||||
|
};
|
||||||
|
} else if (pk.publicKeyJwk && pk.publicKeyJwk.crv === 'P-256' && pk.publicKeyJwk.x && pk.publicKeyJwk.y) {
|
||||||
|
return {
|
||||||
|
keyBytes: p256.ProjectivePoint.fromAffine({
|
||||||
|
x: bytesToBigInt(base64ToBytes(pk.publicKeyJwk.x)),
|
||||||
|
y: bytesToBigInt(base64ToBytes(pk.publicKeyJwk.y))
|
||||||
|
}).toRawBytes(false),
|
||||||
|
keyType: 'P-256'
|
||||||
|
};
|
||||||
|
} else if (pk.publicKeyJwk && pk.publicKeyJwk.kty === 'OKP' && [
|
||||||
|
'Ed25519',
|
||||||
|
'X25519'
|
||||||
|
].includes(pk.publicKeyJwk.crv ?? '') && pk.publicKeyJwk.x) {
|
||||||
|
return {
|
||||||
|
keyBytes: base64ToBytes(pk.publicKeyJwk.x),
|
||||||
|
keyType: pk.publicKeyJwk.crv
|
||||||
|
};
|
||||||
|
} else if (pk.publicKeyMultibase) {
|
||||||
|
const { keyBytes, keyType } = multibaseToBytes(pk.publicKeyMultibase);
|
||||||
|
return {
|
||||||
|
keyBytes,
|
||||||
|
keyType: keyType ?? VM_TO_KEY_TYPE[pk.type]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
keyBytes: new Uint8Array()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Encodes the given byte array to a multibase string (defaulting to base58btc).
|
||||||
|
* If a codec is provided, the corresponding multicodec prefix will be added.
|
||||||
|
*
|
||||||
|
* @param b - the Uint8Array to be encoded
|
||||||
|
* @param base - the base to use for encoding (defaults to base58btc)
|
||||||
|
* @param codec - the codec to use for encoding (defaults to no codec)
|
||||||
|
*
|
||||||
|
* @returns the multibase encoded string
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/ export function bytesToMultibase(b, base = 'base58btc', codec) {
|
||||||
|
if (!codec) {
|
||||||
|
return u8a.toString(encode(base, b), 'utf-8');
|
||||||
|
} else {
|
||||||
|
const codecCode = typeof codec === 'string' ? supportedCodecs[codec] : codec;
|
||||||
|
const prefixLength = varint.encodingLength(codecCode);
|
||||||
|
const multicodecEncoding = new Uint8Array(prefixLength + b.length);
|
||||||
|
varint.encodeTo(codecCode, multicodecEncoding) // set prefix
|
||||||
|
;
|
||||||
|
multicodecEncoding.set(b, prefixLength) // add the original bytes
|
||||||
|
;
|
||||||
|
return u8a.toString(encode(base, multicodecEncoding), 'utf-8');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Converts a multibase string to the Uint8Array it represents.
|
||||||
|
* This method will assume the byte array that is multibase encoded is a multicodec and will attempt to decode it.
|
||||||
|
*
|
||||||
|
* @param s - the string to be converted
|
||||||
|
*
|
||||||
|
* @throws if the string is not formatted correctly.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/ export function multibaseToBytes(s) {
|
||||||
|
const bytes = decode(s);
|
||||||
|
// look for known key lengths first
|
||||||
|
// Ed25519/X25519, secp256k1/P256 compressed or not, BLS12-381 G1/G2 compressed
|
||||||
|
if ([
|
||||||
|
32,
|
||||||
|
33,
|
||||||
|
48,
|
||||||
|
64,
|
||||||
|
65,
|
||||||
|
96
|
||||||
|
].includes(bytes.length)) {
|
||||||
|
return {
|
||||||
|
keyBytes: bytes
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// then assume multicodec, otherwise return the bytes
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const [codec, length] = varint.decode(bytes);
|
||||||
|
const possibleCodec = Object.entries(supportedCodecs).filter(([, code]) => code === codec)?.[0][0] ?? '';
|
||||||
|
return {
|
||||||
|
keyBytes: bytes.slice(length),
|
||||||
|
keyType: CODEC_TO_KEY_TYPE[possibleCodec]
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
// not a multicodec, return the bytes
|
||||||
|
return {
|
||||||
|
keyBytes: bytes
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function hexToBytes(s, minLength) {
|
||||||
|
let input = s.startsWith('0x') ? s.substring(2) : s;
|
||||||
|
if (input.length % 2 !== 0) {
|
||||||
|
input = `0${input}`;
|
||||||
|
}
|
||||||
|
if (minLength) {
|
||||||
|
const paddedLength = Math.max(input.length, minLength * 2);
|
||||||
|
input = input.padStart(paddedLength, '00');
|
||||||
|
}
|
||||||
|
return u8a.fromString(input.toLowerCase(), 'base16');
|
||||||
|
}
|
||||||
|
export function encodeBase64url(s) {
|
||||||
|
return bytesToBase64url(u8a.fromString(s));
|
||||||
|
}
|
||||||
|
export function decodeBase64url(s) {
|
||||||
|
return u8a.toString(base64ToBytes(s));
|
||||||
|
}
|
||||||
|
export function bytesToHex(b) {
|
||||||
|
return u8a.toString(b, 'base16');
|
||||||
|
}
|
||||||
|
export function bytesToBigInt(b) {
|
||||||
|
return BigInt(`0x` + u8a.toString(b, 'base16'));
|
||||||
|
}
|
||||||
|
export function bigintToBytes(n, minLength) {
|
||||||
|
return hexToBytes(n.toString(16), minLength);
|
||||||
|
}
|
||||||
|
export function stringToBytes(s) {
|
||||||
|
return u8a.fromString(s, 'utf-8');
|
||||||
|
}
|
||||||
|
export function toJose({ r, s, recoveryParam }, recoverable) {
|
||||||
|
const jose = new Uint8Array(recoverable ? 65 : 64);
|
||||||
|
jose.set(u8a.fromString(r, 'base16'), 0);
|
||||||
|
jose.set(u8a.fromString(s, 'base16'), 32);
|
||||||
|
if (recoverable) {
|
||||||
|
if (typeof recoveryParam === 'undefined') {
|
||||||
|
throw new Error('Signer did not return a recoveryParam');
|
||||||
|
}
|
||||||
|
jose[64] = recoveryParam;
|
||||||
|
}
|
||||||
|
return bytesToBase64url(jose);
|
||||||
|
}
|
||||||
|
export function fromJose(signature) {
|
||||||
|
const signatureBytes = base64ToBytes(signature);
|
||||||
|
if (signatureBytes.length < 64 || signatureBytes.length > 65) {
|
||||||
|
throw new TypeError(`Wrong size for signature. Expected 64 or 65 bytes, but got ${signatureBytes.length}`);
|
||||||
|
}
|
||||||
|
const r = bytesToHex(signatureBytes.slice(0, 32));
|
||||||
|
const s = bytesToHex(signatureBytes.slice(32, 64));
|
||||||
|
const recoveryParam = signatureBytes.length === 65 ? signatureBytes[64] : undefined;
|
||||||
|
return {
|
||||||
|
r,
|
||||||
|
s,
|
||||||
|
recoveryParam
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function toSealed(ciphertext, tag) {
|
||||||
|
return u8a.concat([
|
||||||
|
base64ToBytes(ciphertext),
|
||||||
|
tag ? base64ToBytes(tag) : new Uint8Array(0)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
export function leftpad(data, size = 64) {
|
||||||
|
if (data.length === size) return data;
|
||||||
|
return '0'.repeat(size - data.length) + data;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Generate random x25519 key pair.
|
||||||
|
*/ export function generateKeyPair() {
|
||||||
|
const secretKey = x25519.utils.randomPrivateKey();
|
||||||
|
const publicKey = x25519.getPublicKey(secretKey);
|
||||||
|
return {
|
||||||
|
secretKey: secretKey,
|
||||||
|
publicKey: publicKey
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Generate private-public x25519 key pair from `seed`.
|
||||||
|
*/ export function generateKeyPairFromSeed(seed) {
|
||||||
|
if (seed.length !== 32) {
|
||||||
|
throw new Error(`x25519: seed must be ${32} bytes`);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
publicKey: x25519.getPublicKey(seed),
|
||||||
|
secretKey: seed
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function genX25519EphemeralKeyPair() {
|
||||||
|
const epk = generateKeyPair();
|
||||||
|
return {
|
||||||
|
publicKeyJWK: {
|
||||||
|
kty: 'OKP',
|
||||||
|
crv: 'X25519',
|
||||||
|
x: bytesToBase64url(epk.publicKey)
|
||||||
|
},
|
||||||
|
secretKey: epk.secretKey
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Checks if a variable is defined and not null.
|
||||||
|
* After this check, typescript sees the variable as defined.
|
||||||
|
*
|
||||||
|
* @param arg - The input to be verified
|
||||||
|
*
|
||||||
|
* @returns true if the input variable is defined.
|
||||||
|
*/ export function isDefined(arg) {
|
||||||
|
return arg !== null && typeof arg !== 'undefined';
|
||||||
|
}
|
255
crates/swc_ecma_minifier/tests/fixture/next/66378/output.js
Normal file
255
crates/swc_ecma_minifier/tests/fixture/next/66378/output.js
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
import { concat, fromString, toString } from "uint8arrays";
|
||||||
|
import { x25519 } from "@noble/curves/ed25519";
|
||||||
|
import { varint } from "multiformats";
|
||||||
|
import { decode, encode } from "multibase";
|
||||||
|
import { secp256k1 } from "@noble/curves/secp256k1";
|
||||||
|
import { p256 } from "@noble/curves/p256";
|
||||||
|
const u8a = {
|
||||||
|
toString,
|
||||||
|
fromString,
|
||||||
|
concat
|
||||||
|
};
|
||||||
|
export function bytesToBase64url(b) {
|
||||||
|
return u8a.toString(b, 'base64url');
|
||||||
|
}
|
||||||
|
export function base64ToBytes(s) {
|
||||||
|
const inputBase64Url = s.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
||||||
|
return u8a.fromString(inputBase64Url, 'base64url');
|
||||||
|
}
|
||||||
|
export function bytesToBase64(b) {
|
||||||
|
return u8a.toString(b, 'base64pad');
|
||||||
|
}
|
||||||
|
export function base58ToBytes(s) {
|
||||||
|
return u8a.fromString(s, 'base58btc');
|
||||||
|
}
|
||||||
|
export function bytesToBase58(b) {
|
||||||
|
return u8a.toString(b, 'base58btc');
|
||||||
|
}
|
||||||
|
export const SUPPORTED_PUBLIC_KEY_TYPES = {
|
||||||
|
ES256: [
|
||||||
|
'JsonWebKey2020',
|
||||||
|
'Multikey',
|
||||||
|
'EcdsaSecp256r1VerificationKey2019'
|
||||||
|
],
|
||||||
|
ES256K: [
|
||||||
|
'EcdsaSecp256k1VerificationKey2019',
|
||||||
|
'EcdsaSecp256k1RecoveryMethod2020',
|
||||||
|
'Secp256k1VerificationKey2018',
|
||||||
|
'Secp256k1SignatureVerificationKey2018',
|
||||||
|
'EcdsaPublicKeySecp256k1',
|
||||||
|
'JsonWebKey2020',
|
||||||
|
'Multikey'
|
||||||
|
],
|
||||||
|
'ES256K-R': [
|
||||||
|
'EcdsaSecp256k1VerificationKey2019',
|
||||||
|
'EcdsaSecp256k1RecoveryMethod2020',
|
||||||
|
'Secp256k1VerificationKey2018',
|
||||||
|
'Secp256k1SignatureVerificationKey2018',
|
||||||
|
'EcdsaPublicKeySecp256k1',
|
||||||
|
'ConditionalProof2022',
|
||||||
|
'JsonWebKey2020',
|
||||||
|
'Multikey'
|
||||||
|
],
|
||||||
|
Ed25519: [
|
||||||
|
'ED25519SignatureVerification',
|
||||||
|
'Ed25519VerificationKey2018',
|
||||||
|
'Ed25519VerificationKey2020',
|
||||||
|
'JsonWebKey2020',
|
||||||
|
'Multikey'
|
||||||
|
],
|
||||||
|
EdDSA: [
|
||||||
|
'ED25519SignatureVerification',
|
||||||
|
'Ed25519VerificationKey2018',
|
||||||
|
'Ed25519VerificationKey2020',
|
||||||
|
'JsonWebKey2020',
|
||||||
|
'Multikey'
|
||||||
|
]
|
||||||
|
};
|
||||||
|
export const VM_TO_KEY_TYPE = {
|
||||||
|
Secp256k1SignatureVerificationKey2018: 'Secp256k1',
|
||||||
|
Secp256k1VerificationKey2018: 'Secp256k1',
|
||||||
|
EcdsaSecp256k1VerificationKey2019: 'Secp256k1',
|
||||||
|
EcdsaPublicKeySecp256k1: 'Secp256k1',
|
||||||
|
EcdsaSecp256k1RecoveryMethod2020: 'Secp256k1',
|
||||||
|
EcdsaSecp256r1VerificationKey2019: 'P-256',
|
||||||
|
Ed25519VerificationKey2018: 'Ed25519',
|
||||||
|
Ed25519VerificationKey2020: 'Ed25519',
|
||||||
|
ED25519SignatureVerification: 'Ed25519',
|
||||||
|
X25519KeyAgreementKey2019: 'X25519',
|
||||||
|
X25519KeyAgreementKey2020: 'X25519',
|
||||||
|
ConditionalProof2022: void 0,
|
||||||
|
JsonWebKey2020: void 0,
|
||||||
|
Multikey: void 0
|
||||||
|
};
|
||||||
|
export const supportedCodecs = {
|
||||||
|
'ed25519-pub': 0xed,
|
||||||
|
'x25519-pub': 0xec,
|
||||||
|
'secp256k1-pub': 0xe7,
|
||||||
|
'bls12_381-g1-pub': 0xea,
|
||||||
|
'bls12_381-g2-pub': 0xeb,
|
||||||
|
'p256-pub': 0x1200
|
||||||
|
};
|
||||||
|
export const CODEC_TO_KEY_TYPE = {
|
||||||
|
'bls12_381-g1-pub': 'Bls12381G1',
|
||||||
|
'bls12_381-g2-pub': 'Bls12381G2',
|
||||||
|
'ed25519-pub': 'Ed25519',
|
||||||
|
'p256-pub': 'P-256',
|
||||||
|
'secp256k1-pub': 'Secp256k1',
|
||||||
|
'x25519-pub': 'X25519'
|
||||||
|
};
|
||||||
|
export function extractPublicKeyBytes(pk) {
|
||||||
|
if (pk.publicKeyBase58) return {
|
||||||
|
keyBytes: base58ToBytes(pk.publicKeyBase58),
|
||||||
|
keyType: VM_TO_KEY_TYPE[pk.type]
|
||||||
|
};
|
||||||
|
if (pk.publicKeyBase64) return {
|
||||||
|
keyBytes: base64ToBytes(pk.publicKeyBase64),
|
||||||
|
keyType: VM_TO_KEY_TYPE[pk.type]
|
||||||
|
};
|
||||||
|
if (pk.publicKeyHex) return {
|
||||||
|
keyBytes: hexToBytes(pk.publicKeyHex),
|
||||||
|
keyType: VM_TO_KEY_TYPE[pk.type]
|
||||||
|
};
|
||||||
|
if (pk.publicKeyJwk && 'secp256k1' === pk.publicKeyJwk.crv && pk.publicKeyJwk.x && pk.publicKeyJwk.y) return {
|
||||||
|
keyBytes: secp256k1.ProjectivePoint.fromAffine({
|
||||||
|
x: bytesToBigInt(base64ToBytes(pk.publicKeyJwk.x)),
|
||||||
|
y: bytesToBigInt(base64ToBytes(pk.publicKeyJwk.y))
|
||||||
|
}).toRawBytes(!1),
|
||||||
|
keyType: 'Secp256k1'
|
||||||
|
};
|
||||||
|
if (pk.publicKeyJwk && 'P-256' === pk.publicKeyJwk.crv && pk.publicKeyJwk.x && pk.publicKeyJwk.y) return {
|
||||||
|
keyBytes: p256.ProjectivePoint.fromAffine({
|
||||||
|
x: bytesToBigInt(base64ToBytes(pk.publicKeyJwk.x)),
|
||||||
|
y: bytesToBigInt(base64ToBytes(pk.publicKeyJwk.y))
|
||||||
|
}).toRawBytes(!1),
|
||||||
|
keyType: 'P-256'
|
||||||
|
};
|
||||||
|
if (pk.publicKeyJwk && 'OKP' === pk.publicKeyJwk.kty && [
|
||||||
|
'Ed25519',
|
||||||
|
'X25519'
|
||||||
|
].includes(pk.publicKeyJwk.crv ?? '') && pk.publicKeyJwk.x) return {
|
||||||
|
keyBytes: base64ToBytes(pk.publicKeyJwk.x),
|
||||||
|
keyType: pk.publicKeyJwk.crv
|
||||||
|
};
|
||||||
|
if (pk.publicKeyMultibase) {
|
||||||
|
const { keyBytes, keyType } = multibaseToBytes(pk.publicKeyMultibase);
|
||||||
|
return {
|
||||||
|
keyBytes,
|
||||||
|
keyType: keyType ?? VM_TO_KEY_TYPE[pk.type]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
keyBytes: new Uint8Array()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function bytesToMultibase(b, base = 'base58btc', codec) {
|
||||||
|
if (!codec) return u8a.toString(encode(base, b), 'utf-8');
|
||||||
|
{
|
||||||
|
const codecCode = 'string' == typeof codec ? supportedCodecs[codec] : codec, prefixLength = varint.encodingLength(codecCode), multicodecEncoding = new Uint8Array(prefixLength + b.length);
|
||||||
|
return varint.encodeTo(codecCode, multicodecEncoding), multicodecEncoding.set(b, prefixLength), u8a.toString(encode(base, multicodecEncoding), 'utf-8');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function multibaseToBytes(s) {
|
||||||
|
const bytes = decode(s);
|
||||||
|
if ([
|
||||||
|
32,
|
||||||
|
33,
|
||||||
|
48,
|
||||||
|
64,
|
||||||
|
65,
|
||||||
|
96
|
||||||
|
].includes(bytes.length)) return {
|
||||||
|
keyBytes: bytes
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const [codec, length] = varint.decode(bytes), possibleCodec = Object.entries(supportedCodecs).filter(([, code])=>code === codec)?.[0][0] ?? '';
|
||||||
|
return {
|
||||||
|
keyBytes: bytes.slice(length),
|
||||||
|
keyType: CODEC_TO_KEY_TYPE[possibleCodec]
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
keyBytes: bytes
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function hexToBytes(s, minLength) {
|
||||||
|
let input = s.startsWith('0x') ? s.substring(2) : s;
|
||||||
|
if (input.length % 2 != 0 && (input = `0${input}`), minLength) {
|
||||||
|
const paddedLength = Math.max(input.length, 2 * minLength);
|
||||||
|
input = input.padStart(paddedLength, '00');
|
||||||
|
}
|
||||||
|
return u8a.fromString(input.toLowerCase(), 'base16');
|
||||||
|
}
|
||||||
|
export function encodeBase64url(s) {
|
||||||
|
return bytesToBase64url(u8a.fromString(s));
|
||||||
|
}
|
||||||
|
export function decodeBase64url(s) {
|
||||||
|
return u8a.toString(base64ToBytes(s));
|
||||||
|
}
|
||||||
|
export function bytesToHex(b) {
|
||||||
|
return u8a.toString(b, 'base16');
|
||||||
|
}
|
||||||
|
export function bytesToBigInt(b) {
|
||||||
|
return BigInt("0x" + u8a.toString(b, 'base16'));
|
||||||
|
}
|
||||||
|
export function bigintToBytes(n, minLength) {
|
||||||
|
return hexToBytes(n.toString(16), minLength);
|
||||||
|
}
|
||||||
|
export function stringToBytes(s) {
|
||||||
|
return u8a.fromString(s, 'utf-8');
|
||||||
|
}
|
||||||
|
export function toJose({ r, s, recoveryParam }, recoverable) {
|
||||||
|
const jose = new Uint8Array(recoverable ? 65 : 64);
|
||||||
|
if (jose.set(u8a.fromString(r, 'base16'), 0), jose.set(u8a.fromString(s, 'base16'), 32), recoverable) {
|
||||||
|
if (void 0 === recoveryParam) throw Error('Signer did not return a recoveryParam');
|
||||||
|
jose[64] = recoveryParam;
|
||||||
|
}
|
||||||
|
return bytesToBase64url(jose);
|
||||||
|
}
|
||||||
|
export function fromJose(signature) {
|
||||||
|
const signatureBytes = base64ToBytes(signature);
|
||||||
|
if (signatureBytes.length < 64 || signatureBytes.length > 65) throw TypeError(`Wrong size for signature. Expected 64 or 65 bytes, but got ${signatureBytes.length}`);
|
||||||
|
return {
|
||||||
|
r: bytesToHex(signatureBytes.slice(0, 32)),
|
||||||
|
s: bytesToHex(signatureBytes.slice(32, 64)),
|
||||||
|
recoveryParam: 65 === signatureBytes.length ? signatureBytes[64] : void 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function toSealed(ciphertext, tag) {
|
||||||
|
return u8a.concat([
|
||||||
|
base64ToBytes(ciphertext),
|
||||||
|
tag ? base64ToBytes(tag) : new Uint8Array(0)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
export function leftpad(data, size = 64) {
|
||||||
|
return data.length === size ? data : '0'.repeat(size - data.length) + data;
|
||||||
|
}
|
||||||
|
export function generateKeyPair() {
|
||||||
|
const secretKey = x25519.utils.randomPrivateKey(), publicKey = x25519.getPublicKey(secretKey);
|
||||||
|
return {
|
||||||
|
secretKey: secretKey,
|
||||||
|
publicKey: publicKey
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function generateKeyPairFromSeed(seed) {
|
||||||
|
if (32 !== seed.length) throw Error("x25519: seed must be 32 bytes");
|
||||||
|
return {
|
||||||
|
publicKey: x25519.getPublicKey(seed),
|
||||||
|
secretKey: seed
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function genX25519EphemeralKeyPair() {
|
||||||
|
const epk = generateKeyPair();
|
||||||
|
return {
|
||||||
|
publicKeyJWK: {
|
||||||
|
kty: 'OKP',
|
||||||
|
crv: 'X25519',
|
||||||
|
x: bytesToBase64url(epk.publicKey)
|
||||||
|
},
|
||||||
|
secretKey: epk.secretKey
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function isDefined(arg) {
|
||||||
|
return null != arg;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user