-
-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathsignature.ts
150 lines (127 loc) · 5.82 KB
/
signature.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import blst from "@chainsafe/blst";
import {bytesToHex, hexToBytes} from "../helpers/index.js";
import {SignatureSet, CoordType, PointFormat, Signature as ISignature, PublicKeyArg, SignatureArg} from "../types.js";
import {PublicKey} from "./publicKey.js";
import {EmptyAggregateError, ZeroSignatureError} from "../errors.js";
export class Signature implements ISignature {
private constructor(private readonly value: blst.Signature) {}
/** @param type Defaults to `CoordType.affine` */
static fromBytes(bytes: Uint8Array, type?: CoordType, validate = true): Signature {
// need to hack the CoordType so @chainsafe/blst is not a required dep
const sig = blst.Signature.deserialize(bytes, (type as unknown) as blst.CoordType);
if (validate) sig.sigValidate();
return new Signature(sig);
}
static fromHex(hex: string): Signature {
return this.fromBytes(hexToBytes(hex));
}
static aggregate(signatures: SignatureArg[]): Signature {
if (signatures.length === 0) {
throw new EmptyAggregateError();
}
const agg = blst.aggregateSignatures(signatures.map(Signature.convertToBlstSignatureArg));
return new Signature(agg);
}
static verifyMultipleSignatures(sets: SignatureSet[]): boolean {
return blst.verifyMultipleAggregateSignatures(
sets.map((set) => ({
message: set.message,
publicKey: PublicKey.convertToBlstPublicKeyArg(set.publicKey),
signature: Signature.convertToBlstSignatureArg(set.signature),
}))
);
}
static asyncVerifyMultipleSignatures(sets: SignatureSet[]): Promise<boolean> {
return blst.asyncVerifyMultipleAggregateSignatures(
sets.map((set) => ({
message: set.message,
publicKey: PublicKey.convertToBlstPublicKeyArg(set.publicKey),
signature: Signature.convertToBlstSignatureArg(set.signature),
}))
);
}
static convertToBlstSignatureArg(signature: SignatureArg): blst.SignatureArg {
// Need to cast to blst-native Signature instead of ISignature
return signature instanceof Uint8Array ? signature : (signature as Signature).value;
}
/**
* Implemented for SecretKey to be able to call .sign()
*/
private static friendBuild(sig: blst.Signature): Signature {
return new Signature(sig);
}
verify(publicKey: PublicKeyArg, message: Uint8Array): boolean {
// TODO (@matthewkeil) The note in aggregateVerify and the checks in this method
// do not seem to go together. Need to check the spec further.
// Individual infinity signatures are NOT okay. Aggregated signatures MAY be infinity
if (this.value.isInfinity()) {
throw new ZeroSignatureError();
}
return blst.verify(message, PublicKey.convertToBlstPublicKeyArg(publicKey), this.value);
}
verifyAggregate(publicKeys: PublicKeyArg[], message: Uint8Array): boolean {
return blst.fastAggregateVerify(message, publicKeys.map(PublicKey.convertToBlstPublicKeyArg), this.value);
}
verifyMultiple(publicKeys: PublicKeyArg[], messages: Uint8Array[]): boolean {
return this.aggregateVerify(publicKeys, messages, false);
}
async asyncVerify(publicKey: PublicKeyArg, message: Uint8Array): Promise<boolean> {
// TODO (@matthewkeil) The note in aggregateVerify and the checks in this method
// do not seem to go together. Need to check the spec further.
// Individual infinity signatures are NOT okay. Aggregated signatures MAY be infinity
if (this.value.isInfinity()) {
throw new ZeroSignatureError();
}
return blst.asyncVerify(message, PublicKey.convertToBlstPublicKeyArg(publicKey), this.value);
}
async asyncVerifyAggregate(publicKeys: PublicKeyArg[], message: Uint8Array): Promise<boolean> {
return blst.asyncFastAggregateVerify(message, publicKeys.map(PublicKey.convertToBlstPublicKeyArg), this.value);
}
async asyncVerifyMultiple(publicKeys: PublicKeyArg[], messages: Uint8Array[]): Promise<boolean> {
return this.aggregateVerify(publicKeys, messages, true);
}
toBytes(format?: PointFormat): Uint8Array {
if (format === PointFormat.uncompressed) {
return this.value.serialize(false);
} else {
return this.value.serialize(true);
}
}
toHex(format?: PointFormat): string {
return bytesToHex(this.toBytes(format));
}
multiplyBy(bytes: Uint8Array): Signature {
return new Signature(this.value.multiplyBy(bytes));
}
private aggregateVerify<T extends false>(publicKeys: PublicKeyArg[], messages: Uint8Array[], runAsync: T): boolean;
private aggregateVerify<T extends true>(
publicKeys: PublicKeyArg[],
messages: Uint8Array[],
runAsync: T
): Promise<boolean>;
private aggregateVerify<T extends boolean>(
publicKeys: PublicKeyArg[],
messages: Uint8Array[],
runAsync: T
): Promise<boolean> | boolean {
// TODO (@matthewkeil) The note in verify and the checks in this method
// do not seem to go together. Need to check the spec further.
// If this set is simply an infinity signature and infinity publicKey then skip verification.
// This has the effect of always declaring that this sig/publicKey combination is valid.
// for Eth2.0 specs tests
if (publicKeys.length === 1) {
const publicKey = publicKeys[0];
// eslint-disable-next-line prettier/prettier
const pk: PublicKey = publicKey instanceof Uint8Array
? PublicKey.fromBytes(publicKey)
: (publicKey as PublicKey); // need to cast to blst-native key instead of IPublicKey
// @ts-expect-error Need to hack type to get access to the private `value`
if (this.value.isInfinity() && pk.value.isInfinity()) {
return runAsync ? Promise.resolve(true) : true;
}
}
return runAsync
? blst.asyncAggregateVerify(messages, publicKeys.map(PublicKey.convertToBlstPublicKeyArg), this.value)
: blst.aggregateVerify(messages, publicKeys.map(PublicKey.convertToBlstPublicKeyArg), this.value);
}
}