-
Notifications
You must be signed in to change notification settings - Fork 795
/
validate.js
348 lines (294 loc) · 13.9 KB
/
validate.js
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../..');
const chai = require('chai');
const BN = require('bn.js');
chai.use(require('chai-as-promised'));
const expect = chai.expect;
const armoredDSAKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
lQNTBF69PO8RCACHP4KLQcYOPGsGV9owTZvxnvHvvrY8W0v8xDUL3y6CLc05srF1
kQp/81iUfP5g57BEiDpJV95kMh+ulBthIOGnuMCkodJjuBICB4K6BtFTV4Fw1Q5S
S7aLC9beCaMvvGHXsK6MbknYl+IVJY7Zmml1qUSrBIQFGp5kqdhIX4o+OrzZ1zYj
ALicqzD7Zx2VRjGNQv7UKv4CkBOC8ncdnq/4/OQeOYFzVbCOf+sJhTgz6yxjHJVC
fLk7w8l2v1zV11VJuc8cQiQ9g8tjbKgLMsbyzy7gl4m9MSCdinG36XZuPibZrSm0
H8gKAdd1FT84a3/qU2rtLLR0y8tCxBj89Xx/AQCv7CDmwoU+/yGpBVVl1mh0ZUkA
/VJUhnJfv5MIOIi3AQf8CS9HrEmYJg/A3z0DcvcwIu/9gqpRLTqH1iT5o4BCg2j+
Cog2ExYkQl1OEPkEQ1lKJSnD8MDwO3BlkJ4cD0VSKxlnwd9dsu9m2+F8T+K1hoA7
PfH89TjD5HrEaGAYIdivLYSwoTNOO+fY8FoVC0RR9pFNOmjiTU5PZZedOxAql5Os
Hp2bYhky0G9trjo8Mt6CGhvgA3dAKyONftLQr9HSM0GKacFV+nRd9TGCPNZidKU8
MDa/SB/08y1bBGX5FK5wwiZ6H5qD8VAUobH3kwKlrg0nL00/EqtYHJqvJ2gkT5/v
h8+z4R4TuYiy4kKF2FLPd5OjdA31IVDoVgCwF0WHLgf/X9AiTr/DPs/5dIYN1+hf
UJwqjzr3dlokRwx3CVDcOVsdkWRwb8cvxubbsIorvUrF02IhYjHJMjIHT/zFt2zA
+VPzO4zabUlawWVepPEwrCtXgvn9aXqjhAYbilG3UZamhfstGUmbmvWVDadALwby
EO8u2pfLhI2lep63V/+KtUOLhfk8jKRSvxvxlYAvMi7sK8kB+lYy17XKN+IMYgf8
gMFV6XGKpdmMSV3jOvat8cI6vnRO0i+g3jANP3PfrFEivat/rVgxo67r4rxezfFn
J29qwB9rgbRgMBGsbDvIlQNV/NWFvHy2uQAEKn5eX4CoLsCZoR2VfK3BwBCxhYDp
/wAA/0GSmI9MlMnLadFNlcX2Bm4i15quZAGF8JxwHbj1dhdUEYq0E1Rlc3QgPHRl
c3RAdGVzdC5pbz6IlAQTEQgAPBYhBAq6lCI5EfrbHP1qZCxnOy/rlEGVBQJevTzv
AhsDBQsJCAcCAyICAQYVCgkICwIEFgIDAQIeBwIXgAAKCRAsZzsv65RBlUPoAP9Q
aTCWpHWZkvZzC8VU64O76fHp31rLWlcZFttuDNLyeAEAhOxkQHk6GR88R+EF5mrn
clr63t9Q4wreqOlO0NR5/9k=
=UW2O
-----END PGP PRIVATE KEY BLOCK-----
`;
const armoredElGamalKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
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
vqBGKJzmO5q3cECw
=X9kJ
-----END PGP PRIVATE KEY BLOCK-----`;
function cloneKeyPacket(key) {
const keyPacket = new openpgp.SecretKeyPacket();
keyPacket.read(key.keyPacket.write());
return keyPacket;
}
module.exports = () => {
describe('EdDSA parameter validation', function() {
let eddsaKey;
before(async () => {
eddsaKey = (await openpgp.generateKey({ curve: 'ed25519', userIds: [{ name: 'Test', email: 'test@test.com' }] })).key;
});
it('EdDSA params should be valid', async function() {
await expect(eddsaKey.keyPacket.validate()).to.not.be.rejected;
});
it('detect invalid edDSA Q', async function() {
const eddsaKeyPacket = cloneKeyPacket(eddsaKey);
const Q = eddsaKeyPacket.publicParams.Q;
Q[0]++;
await expect(eddsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
const infQ = new Uint8Array(Q.length);
eddsaKeyPacket.publicParams.Q = infQ;
await expect(eddsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
});
});
describe('ECC curve validation', function() {
let eddsaKey;
let ecdhKey;
let ecdsaKey;
before(async () => {
eddsaKey = (await openpgp.generateKey({ curve: 'ed25519', userIds: [{ name: 'Test', email: 'test@test.com' }] })).key;
ecdhKey = eddsaKey.subKeys[0];
ecdsaKey = (await openpgp.generateKey({ curve: 'p256', userIds: [{ name: 'Test', email: 'test@test.com' }] })).key;
});
it('EdDSA params are not valid for ECDH', async function() {
const { oid, Q } = eddsaKey.keyPacket.publicParams;
const { seed } = eddsaKey.keyPacket.privateParams;
const ecdhKeyPacket = cloneKeyPacket(ecdhKey);
const ecdhOID = ecdhKeyPacket.publicParams.oid;
ecdhKeyPacket.publicParams.oid = oid;
await expect(ecdhKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
ecdhKeyPacket.publicParams.oid = ecdhOID;
ecdhKeyPacket.publicParams.Q = Q;
ecdhKeyPacket.privateParams.d = seed;
await expect(ecdhKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
});
it('EdDSA params are not valid for EcDSA', async function() {
const { oid, Q } = eddsaKey.keyPacket.publicParams;
const { seed } = eddsaKey.keyPacket.privateParams;
const ecdsaKeyPacket = cloneKeyPacket(ecdsaKey);
const ecdsaOID = ecdsaKeyPacket.publicParams.oid;
ecdsaKeyPacket.publicParams.oid = oid;
await expect(ecdsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
ecdsaKeyPacket.publicParams.oid = ecdsaOID;
ecdsaKeyPacket.publicParams.Q = Q;
ecdsaKeyPacket.privateParams.d = seed;
await expect(ecdsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
});
it('ECDH x25519 params are not valid for EcDSA', async function() {
const { oid, Q } = ecdhKey.keyPacket.publicParams;
const { d } = ecdhKey.keyPacket.privateParams;
const ecdsaKeyPacket = cloneKeyPacket(ecdsaKey);
ecdsaKeyPacket.publicParams.oid = oid;
ecdsaKeyPacket.publicParams.Q = Q;
ecdsaKeyPacket.privateParams.d = d;
await expect(ecdsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
});
it('EcDSA params are not valid for EdDSA', async function() {
const { oid, Q } = ecdsaKey.keyPacket.publicParams;
const { d } = ecdsaKey.keyPacket.privateParams;
const eddsaKeyPacket = cloneKeyPacket(eddsaKey);
const eddsaOID = eddsaKeyPacket.publicParams.oid;
eddsaKeyPacket.publicParams.oid = oid;
await expect(eddsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
eddsaKeyPacket.publicParams.oid = eddsaOID;
eddsaKeyPacket.publicParams.Q = Q;
eddsaKeyPacket.privateParams.seed = d;
await expect(eddsaKeyPacket.validate()).to.be.rejected;
});
it('ECDH x25519 params are not valid for EdDSA', async function() {
const { oid, Q } = ecdhKey.keyPacket.publicParams;
const { d } = ecdhKey.keyPacket.privateParams;
const eddsaKeyPacket = cloneKeyPacket(eddsaKey);
const eddsaOID = eddsaKeyPacket.publicParams.oid;
eddsaKeyPacket.publicParams.oid = oid;
await expect(eddsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
eddsaKeyPacket.publicParams.oid = eddsaOID;
eddsaKeyPacket.publicParams.Q = Q;
eddsaKeyPacket.privateParams.seed = d;
await expect(eddsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
});
});
const curves = ['curve25519', 'p256', 'p384', 'p521', 'secp256k1', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1'];
curves.forEach(curve => {
describe(`ECC ${curve} parameter validation`, () => {
let ecdsaKey;
let ecdhKey;
before(async () => {
if (curve !== 'curve25519') {
ecdsaKey = (await openpgp.generateKey({ curve, userIds: [{ name: 'Test', email: 'test@test.com' }] })).key;
ecdhKey = ecdsaKey.subKeys[0];
} else {
const eddsaKey = (await openpgp.generateKey({ curve: 'ed25519', userIds: [{ name: 'Test', email: 'test@test.com' }] })).key;
ecdhKey = eddsaKey.subKeys[0];
}
});
if (ecdsaKey) {
it(`EcDSA ${curve} params should be valid`, async function() {
await expect(ecdsaKey.keyPacket.validate()).to.not.be.rejected;
});
it('detect invalid EcDSA Q', async function() {
const keyPacket = cloneKeyPacket(ecdsaKey);
const Q = keyPacket.publicParams.Q;
Q[0]++;
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
const infQ = new Uint8Array(Q.length);
keyPacket.publicParams.Q = infQ;
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
});
}
it(`ECDH ${curve} params should be valid`, async function() {
await expect(ecdhKey.keyPacket.validate()).to.not.be.rejected;
});
it('detect invalid ECDH Q', async function() {
const keyPacket = cloneKeyPacket(ecdhKey);
const Q = keyPacket.publicParams.Q;
Q[16]++;
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
const infQ = new Uint8Array(Q.length);
keyPacket.publicParams.Q = infQ;
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
});
});
});
describe('RSA parameter validation', function() {
let rsaKey;
before(async () => {
rsaKey = (await openpgp.generateKey({ type: "rsa", rsaBits: 2048, userIds: [{ name: 'Test', email: 'test@test.com' }] })).key;
});
it('generated RSA params are valid', async function() {
await expect(rsaKey.keyPacket.validate()).to.not.be.rejected;
});
it('detect invalid RSA n', async function() {
const keyPacket = cloneKeyPacket(rsaKey);
const n = keyPacket.publicParams.n;
n[0]++;
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
});
it('detect invalid RSA e', async function() {
const keyPacket = cloneKeyPacket(rsaKey);
const e = keyPacket.publicParams.e;
e[0]++;
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
});
});
describe('DSA parameter validation', function() {
let dsaKey;
before(async () => {
dsaKey = await openpgp.readArmoredKey(armoredDSAKey);
});
it('DSA params should be valid', async function() {
await expect(dsaKey.keyPacket.validate()).to.not.be.rejected;
});
it('detect invalid DSA p', async function() {
const keyPacket = cloneKeyPacket(dsaKey);
const p = keyPacket.publicParams.p;
p[0]++;
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
});
it('detect invalid DSA y', async function() {
const keyPacket = cloneKeyPacket(dsaKey);
const y = keyPacket.publicParams.y;
y[0]++;
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
});
it('detect invalid DSA g', async function() {
const keyPacket = cloneKeyPacket(dsaKey);
const g = keyPacket.publicParams.g;
g[0]++;
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
keyPacket.publicParams.g = new Uint8Array([1]);
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
});
});
describe('ElGamal parameter validation', function() {
let egKey;
before(async () => {
egKey = (await openpgp.readArmoredKey(armoredElGamalKey)).subKeys[0];
});
it('params should be valid', async function() {
await expect(egKey.keyPacket.validate()).to.not.be.rejected;
});
it('detect invalid p', async function() {
const keyPacket = cloneKeyPacket(egKey);
const p = keyPacket.publicParams.p;
p[0]++;
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
});
it('detect invalid y', async function() {
const keyPacket = cloneKeyPacket(egKey);
const y = keyPacket.publicParams.y;
y[0]++;
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
});
it('detect invalid g', async function() {
const keyPacket = cloneKeyPacket(egKey);
const g = keyPacket.publicParams.g;
g[0]++;
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
keyPacket.publicParams.g = new Uint8Array([1]);
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
});
it('detect g with small order', async function() {
const keyPacket = cloneKeyPacket(egKey);
const p = keyPacket.publicParams.p;
const g = keyPacket.publicParams.g;
const pBN = new BN(p);
const gModP = new BN(g).toRed(new BN.red(pBN));
// g**(p-1)/2 has order 2
const gOrd2 = gModP.redPow(pBN.subn(1).shrn(1));
keyPacket.publicParams.g = gOrd2.toArrayLike(Uint8Array, 'be');
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
});
});
};