ethers.js/lib.esm/crypto/signature.js

301 lines
9.6 KiB
JavaScript
Raw Normal View History

2022-09-05 23:57:11 +03:00
import { ZeroHash } from "../constants/index.js";
2022-12-31 00:35:04 +03:00
import { concat, dataLength, getBigInt, getBytes, getNumber, hexlify, toBeArray, isHexString, zeroPadValue, assertArgument, assertPrivate } from "../utils/index.js";
2022-09-05 23:57:11 +03:00
// Constants
const BN_0 = BigInt(0);
const BN_1 = BigInt(1);
const BN_2 = BigInt(2);
const BN_27 = BigInt(27);
const BN_28 = BigInt(28);
const BN_35 = BigInt(35);
const _guard = {};
2022-12-31 00:35:04 +03:00
function toUint256(value) {
return zeroPadValue(toBeArray(value), 32);
}
2022-11-30 23:44:23 +03:00
/**
* A Signature @TODO
2023-02-13 06:14:26 +03:00
*
*
* @_docloc: api/crypto:Signing
2022-11-30 23:44:23 +03:00
*/
2022-09-05 23:57:11 +03:00
export class Signature {
2022-11-30 23:44:23 +03:00
#r;
#s;
#v;
#networkV;
/**
* The ``r`` value for a signautre.
*
* This represents the ``x`` coordinate of a "reference" or
* challenge point, from which the ``y`` can be computed.
*/
get r() { return this.#r; }
2022-09-05 23:57:11 +03:00
set r(value) {
2022-11-09 10:57:02 +03:00
assertArgument(dataLength(value) === 32, "invalid r", "value", value);
2022-11-30 23:44:23 +03:00
this.#r = hexlify(value);
2022-09-05 23:57:11 +03:00
}
2022-11-30 23:44:23 +03:00
/**
* The ``s`` value for a signature.
*/
get s() { return this.#s; }
set s(_value) {
2023-03-20 19:53:37 +03:00
assertArgument(dataLength(_value) === 32, "invalid s", "value", _value);
2022-11-30 23:44:23 +03:00
const value = hexlify(_value);
assertArgument(parseInt(value.substring(0, 3)) < 8, "non-canonical s", "value", value);
this.#s = value;
2022-09-05 23:57:11 +03:00
}
2022-11-30 23:44:23 +03:00
/**
* The ``v`` value for a signature.
*
* Since a given ``x`` value for ``r`` has two possible values for
* its correspondin ``y``, the ``v`` indicates which of the two ``y``
* values to use.
*
* It is normalized to the values ``27`` or ``28`` for legacy
* purposes.
*/
get v() { return this.#v; }
2022-09-05 23:57:11 +03:00
set v(value) {
2022-09-16 05:58:45 +03:00
const v = getNumber(value, "value");
2022-11-09 10:57:02 +03:00
assertArgument(v === 27 || v === 28, "invalid v", "v", value);
2022-11-30 23:44:23 +03:00
this.#v = v;
2022-09-05 23:57:11 +03:00
}
2022-11-30 23:44:23 +03:00
/**
* The EIP-155 ``v`` for legacy transactions. For non-legacy
* transactions, this value is ``null``.
*/
get networkV() { return this.#networkV; }
/**
* The chain ID for EIP-155 legacy transactions. For non-legacy
* transactions, this value is ``null``.
*/
2022-09-05 23:57:11 +03:00
get legacyChainId() {
const v = this.networkV;
if (v == null) {
return null;
}
return Signature.getChainId(v);
}
2022-11-30 23:44:23 +03:00
/**
* The ``yParity`` for the signature.
*
* See ``v`` for more details on how this value is used.
*/
2022-09-05 23:57:11 +03:00
get yParity() {
2022-10-20 12:03:32 +03:00
return (this.v === 27) ? 0 : 1;
2022-09-05 23:57:11 +03:00
}
2022-11-30 23:44:23 +03:00
/**
* The [[link-eip-2098]] compact representation of the ``yParity``
* and ``s`` compacted into a single ``bytes32``.
*/
2022-09-05 23:57:11 +03:00
get yParityAndS() {
// The EIP-2098 compact representation
2022-09-16 05:58:45 +03:00
const yParityAndS = getBytes(this.s);
2022-09-05 23:57:11 +03:00
if (this.yParity) {
yParityAndS[0] |= 0x80;
}
return hexlify(yParityAndS);
}
2022-11-30 23:44:23 +03:00
/**
* The [[link-eip-2098]] compact representation.
*/
2022-09-05 23:57:11 +03:00
get compactSerialized() {
return concat([this.r, this.yParityAndS]);
}
2022-11-30 23:44:23 +03:00
/**
* The serialized representation.
*/
2022-09-05 23:57:11 +03:00
get serialized() {
return concat([this.r, this.s, (this.yParity ? "0x1c" : "0x1b")]);
}
2022-11-30 23:44:23 +03:00
/**
* @private
*/
2022-09-05 23:57:11 +03:00
constructor(guard, r, s, v) {
2022-09-16 05:58:45 +03:00
assertPrivate(guard, _guard, "Signature");
2022-11-30 23:44:23 +03:00
this.#r = r;
this.#s = s;
this.#v = v;
this.#networkV = null;
2022-09-05 23:57:11 +03:00
}
[Symbol.for('nodejs.util.inspect.custom')]() {
return `Signature { r: "${this.r}", s: "${this.s}", yParity: ${this.yParity}, networkV: ${this.networkV} }`;
}
2022-11-30 23:44:23 +03:00
/**
* Returns a new identical [[Signature]].
*/
2022-09-05 23:57:11 +03:00
clone() {
const clone = new Signature(_guard, this.r, this.s, this.v);
if (this.networkV) {
2022-11-30 23:44:23 +03:00
clone.#networkV = this.networkV;
2022-09-05 23:57:11 +03:00
}
return clone;
}
2022-11-30 23:44:23 +03:00
/**
* Returns a representation that is compatible with ``JSON.stringify``.
*/
2022-09-05 23:57:11 +03:00
toJSON() {
const networkV = this.networkV;
return {
_type: "signature",
networkV: ((networkV != null) ? networkV.toString() : null),
r: this.r, s: this.s, v: this.v,
};
}
2022-11-30 23:44:23 +03:00
/**
2022-12-10 02:24:58 +03:00
* Compute the chain ID from the ``v`` in a legacy EIP-155 transactions.
*
* @example:
* Signature.getChainId(45)
* //_result:
*
* Signature.getChainId(46)
* //_result:
2022-11-30 23:44:23 +03:00
*/
2022-09-05 23:57:11 +03:00
static getChainId(v) {
2022-09-16 05:58:45 +03:00
const bv = getBigInt(v, "v");
2022-09-05 23:57:11 +03:00
// The v is not an EIP-155 v, so it is the unspecified chain ID
if ((bv == BN_27) || (bv == BN_28)) {
return BN_0;
}
// Bad value for an EIP-155 v
2022-11-09 10:57:02 +03:00
assertArgument(bv >= BN_35, "invalid EIP-155 v", "v", v);
2022-09-05 23:57:11 +03:00
return (bv - BN_35) / BN_2;
}
2022-11-30 23:44:23 +03:00
/**
2022-12-10 02:24:58 +03:00
* Compute the ``v`` for a chain ID for a legacy EIP-155 transactions.
*
* Legacy transactions which use [[link-eip-155]] hijack the ``v``
* property to include the chain ID.
*
* @example:
* Signature.getChainIdV(5, 27)
* //_result:
*
* Signature.getChainIdV(5, 28)
* //_result:
*
2022-11-30 23:44:23 +03:00
*/
2022-09-05 23:57:11 +03:00
static getChainIdV(chainId, v) {
2022-09-16 05:58:45 +03:00
return (getBigInt(chainId) * BN_2) + BigInt(35 + v - 27);
2022-09-05 23:57:11 +03:00
}
2022-11-30 23:44:23 +03:00
/**
2022-12-10 02:24:58 +03:00
* Compute the normalized legacy transaction ``v`` from a ``yParirty``,
* a legacy transaction ``v`` or a legacy [[link-eip-155]] transaction.
*
* @example:
* // The values 0 and 1 imply v is actually yParity
* Signature.getNormalizedV(0)
* //_result:
*
* // Legacy non-EIP-1559 transaction (i.e. 27 or 28)
* Signature.getNormalizedV(27)
* //_result:
*
* // Legacy EIP-155 transaction (i.e. >= 35)
* Signature.getNormalizedV(46)
* //_result:
*
* // Invalid values throw
* Signature.getNormalizedV(5)
* //_error:
2022-11-30 23:44:23 +03:00
*/
2022-09-05 23:57:11 +03:00
static getNormalizedV(v) {
2022-09-16 05:58:45 +03:00
const bv = getBigInt(v);
2022-12-10 02:24:58 +03:00
if (bv === BN_0 || bv === BN_27) {
2022-09-05 23:57:11 +03:00
return 27;
}
2022-12-10 02:24:58 +03:00
if (bv === BN_1 || bv === BN_28) {
2022-09-05 23:57:11 +03:00
return 28;
}
2022-12-10 02:24:58 +03:00
assertArgument(bv >= BN_35, "invalid v", "v", v);
2022-09-05 23:57:11 +03:00
// Otherwise, EIP-155 v means odd is 27 and even is 28
return (bv & BN_1) ? 27 : 28;
}
2022-11-30 23:44:23 +03:00
/**
* Creates a new [[Signature]].
*
* If no %%sig%% is provided, a new [[Signature]] is created
* with default values.
*
* If %%sig%% is a string, it is parsed.
*/
2022-09-05 23:57:11 +03:00
static from(sig) {
2022-11-09 10:57:02 +03:00
function assertError(check, message) {
assertArgument(check, message, "signature", sig);
}
;
2022-10-20 12:03:32 +03:00
if (sig == null) {
return new Signature(_guard, ZeroHash, ZeroHash, 27);
}
2022-09-05 23:57:11 +03:00
if (typeof (sig) === "string") {
2022-09-16 05:58:45 +03:00
const bytes = getBytes(sig, "signature");
2022-09-05 23:57:11 +03:00
if (bytes.length === 64) {
const r = hexlify(bytes.slice(0, 32));
const s = bytes.slice(32, 64);
const v = (s[0] & 0x80) ? 28 : 27;
s[0] &= 0x7f;
return new Signature(_guard, r, hexlify(s), v);
}
2022-10-20 12:03:32 +03:00
if (bytes.length === 65) {
const r = hexlify(bytes.slice(0, 32));
2022-09-05 23:57:11 +03:00
const s = bytes.slice(32, 64);
2022-11-09 10:57:02 +03:00
assertError((s[0] & 0x80) === 0, "non-canonical s");
2022-09-05 23:57:11 +03:00
const v = Signature.getNormalizedV(bytes[64]);
return new Signature(_guard, r, hexlify(s), v);
}
2023-03-04 04:25:07 +03:00
assertError(false, "invalid raw signature length");
2022-09-05 23:57:11 +03:00
}
if (sig instanceof Signature) {
return sig.clone();
}
// Get r
2022-12-31 00:35:04 +03:00
const _r = sig.r;
assertError(_r != null, "missing r");
const r = toUint256(_r);
2022-09-05 23:57:11 +03:00
// Get s; by any means necessary (we check consistency below)
const s = (function (s, yParityAndS) {
if (s != null) {
2022-12-31 00:35:04 +03:00
return toUint256(s);
2022-09-05 23:57:11 +03:00
}
if (yParityAndS != null) {
2022-11-09 10:57:02 +03:00
assertError(isHexString(yParityAndS, 32), "invalid yParityAndS");
2022-09-16 05:58:45 +03:00
const bytes = getBytes(yParityAndS);
2022-09-05 23:57:11 +03:00
bytes[0] &= 0x7f;
return hexlify(bytes);
}
2022-11-09 10:57:02 +03:00
assertError(false, "missing s");
2022-09-05 23:57:11 +03:00
})(sig.s, sig.yParityAndS);
2022-11-09 10:57:02 +03:00
assertError((getBytes(s)[0] & 0x80) == 0, "non-canonical s");
2022-09-05 23:57:11 +03:00
// Get v; by any means necessary (we check consistency below)
const { networkV, v } = (function (_v, yParityAndS, yParity) {
if (_v != null) {
2022-09-16 05:58:45 +03:00
const v = getBigInt(_v);
2022-09-05 23:57:11 +03:00
return {
networkV: ((v >= BN_35) ? v : undefined),
v: Signature.getNormalizedV(v)
};
}
if (yParityAndS != null) {
2022-11-09 10:57:02 +03:00
assertError(isHexString(yParityAndS, 32), "invalid yParityAndS");
2022-09-16 05:58:45 +03:00
return { v: ((getBytes(yParityAndS)[0] & 0x80) ? 28 : 27) };
2022-09-05 23:57:11 +03:00
}
if (yParity != null) {
2023-07-16 07:36:12 +03:00
switch (getNumber(yParity, "sig.yParity")) {
2022-09-05 23:57:11 +03:00
case 0: return { v: 27 };
case 1: return { v: 28 };
}
2022-11-09 10:57:02 +03:00
assertError(false, "invalid yParity");
2022-09-05 23:57:11 +03:00
}
2022-11-09 10:57:02 +03:00
assertError(false, "missing v");
2022-09-05 23:57:11 +03:00
})(sig.v, sig.yParityAndS, sig.yParity);
const result = new Signature(_guard, r, s, v);
if (networkV) {
2022-11-30 23:44:23 +03:00
result.#networkV = networkV;
2022-09-05 23:57:11 +03:00
}
// If multiple of v, yParity, yParityAndS we given, check they match
2023-07-16 07:36:12 +03:00
assertError(sig.yParity == null || getNumber(sig.yParity, "sig.yParity") === result.yParity, "yParity mismatch");
assertError(sig.yParityAndS == null || sig.yParityAndS === result.yParityAndS, "yParityAndS mismatch");
2022-09-05 23:57:11 +03:00
return result;
}
}
//# sourceMappingURL=signature.js.map