-
Notifications
You must be signed in to change notification settings - Fork 59
/
Copy pathvalidate.ts
161 lines (134 loc) · 4.35 KB
/
validate.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
151
152
153
154
155
156
157
158
159
160
161
import {
ERROR_BAD_PRIVATE,
ERROR_BAD_POINT,
ERROR_BAD_TWEAK,
throwError,
ERROR_BAD_HASH,
ERROR_BAD_EXTRA_DATA,
ERROR_BAD_SIGNATURE,
ERROR_BAD_PARITY,
ERROR_BAD_RECOVERY_ID,
} from "./validate_error.js";
export const PRIVATE_KEY_SIZE = 32;
export const PUBLIC_KEY_COMPRESSED_SIZE = 33;
export const PUBLIC_KEY_UNCOMPRESSED_SIZE = 65;
export const X_ONLY_PUBLIC_KEY_SIZE = 32;
export const TWEAK_SIZE = 32;
export const HASH_SIZE = 32;
export const EXTRA_DATA_SIZE = 32;
export const SIGNATURE_SIZE = 64;
const BN32_ZERO = new Uint8Array(32);
const BN32_N = new Uint8Array([
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
254, 186, 174, 220, 230, 175, 72, 160, 59, 191, 210, 94, 140, 208, 54, 65, 65,
]);
// Difference between field and order
const BN32_P_MINUS_N = new Uint8Array([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 69, 81, 35, 25, 80, 183, 95,
196, 64, 45, 161, 114, 47, 201, 186, 238,
]);
function isUint8Array(value: Uint8Array): boolean {
return value instanceof Uint8Array;
}
function cmpBN32(data1: Uint8Array, data2: Uint8Array): number {
for (let i = 0; i < 32; ++i) {
if (data1[i] !== data2[i]) {
return data1[i] < data2[i] ? -1 : 1;
}
}
return 0;
}
export function isZero(x: Uint8Array): boolean {
return cmpBN32(x, BN32_ZERO) === 0;
}
export function isPrivate(x: Uint8Array): boolean {
return (
isUint8Array(x) &&
x.length === PRIVATE_KEY_SIZE &&
cmpBN32(x, BN32_ZERO) > 0 &&
cmpBN32(x, BN32_N) < 0
);
}
export function isPoint(p: Uint8Array): boolean {
return (
isUint8Array(p) &&
(p.length === PUBLIC_KEY_COMPRESSED_SIZE ||
p.length === PUBLIC_KEY_UNCOMPRESSED_SIZE ||
p.length === X_ONLY_PUBLIC_KEY_SIZE)
);
}
export function isXOnlyPoint(p: Uint8Array): boolean {
return isUint8Array(p) && p.length === X_ONLY_PUBLIC_KEY_SIZE;
}
export function isDERPoint(p: Uint8Array): boolean {
return (
isUint8Array(p) &&
(p.length === PUBLIC_KEY_COMPRESSED_SIZE ||
p.length === PUBLIC_KEY_UNCOMPRESSED_SIZE)
);
}
export function isPointCompressed(p: Uint8Array): boolean {
return isUint8Array(p) && p.length === PUBLIC_KEY_COMPRESSED_SIZE;
}
function isTweak(tweak: Uint8Array): boolean {
return (
isUint8Array(tweak) &&
tweak.length === TWEAK_SIZE &&
cmpBN32(tweak, BN32_N) < 0
);
}
function isHash(h: Uint8Array): boolean {
return isUint8Array(h) && h.length === HASH_SIZE;
}
function isExtraData(e?: Uint8Array): boolean {
return e === undefined || (isUint8Array(e) && e.length === EXTRA_DATA_SIZE);
}
function isSignature(signature: Uint8Array): boolean {
return (
isUint8Array(signature) &&
signature.length === 64 &&
cmpBN32(signature.subarray(0, 32), BN32_N) < 0 &&
cmpBN32(signature.subarray(32, 64), BN32_N) < 0
);
}
function isSigrLessThanPMinusN(signature: Uint8Array): boolean {
return (
isUint8Array(signature) &&
signature.length === 64 &&
cmpBN32(signature.subarray(0, 32), BN32_P_MINUS_N) < 0
);
}
export function validateParity(p: 1 | 0): void {
if (p !== 0 && p !== 1) throwError(ERROR_BAD_PARITY);
}
export function validatePrivate(d: Uint8Array): void {
if (!isPrivate(d)) throwError(ERROR_BAD_PRIVATE);
}
export function validatePoint(p: Uint8Array): void {
if (!isPoint(p)) throwError(ERROR_BAD_POINT);
}
export function validateXOnlyPoint(p: Uint8Array): void {
if (!isXOnlyPoint(p)) throwError(ERROR_BAD_POINT);
}
export function validateTweak(tweak: Uint8Array): void {
if (!isTweak(tweak)) throwError(ERROR_BAD_TWEAK);
}
export function validateHash(h: Uint8Array): void {
if (!isHash(h)) throwError(ERROR_BAD_HASH);
}
export function validateExtraData(e?: Uint8Array): void {
if (!isExtraData(e)) throwError(ERROR_BAD_EXTRA_DATA);
}
export function validateSignature(signature: Uint8Array): void {
if (!isSignature(signature)) throwError(ERROR_BAD_SIGNATURE);
}
export function validateSignatureCustom(validatorFn: () => boolean): void {
if (!validatorFn()) throwError(ERROR_BAD_SIGNATURE);
}
export function validateSignatureNonzeroRS(signature: Uint8Array): void {
if (isZero(signature.subarray(0, 32))) throwError(ERROR_BAD_SIGNATURE);
if (isZero(signature.subarray(32, 64))) throwError(ERROR_BAD_SIGNATURE);
}
export function validateSigrPMinusN(signature: Uint8Array): void {
if (!isSigrLessThanPMinusN(signature)) throwError(ERROR_BAD_RECOVERY_ID);
}