• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

handshake-org / hsd / 11142522588

02 Oct 2024 10:56AM UTC coverage: 70.04% (+0.007%) from 70.033%
11142522588

push

github

nodech
Merge PR #902 from 'nodech/update-types'

7761 of 12916 branches covered (60.09%)

Branch coverage included in aggregate %.

67 of 92 new or added lines in 25 files covered. (72.83%)

8 existing lines in 5 files now uncovered.

24790 of 33559 relevant lines covered (73.87%)

34019.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

24.91
/lib/primitives/airdropkey.js
1
/* eslint camelcase: 'off' */
2

3
'use strict';
4

5
const assert = require('bsert');
70✔
6
const bio = require('bufio');
70✔
7
const base16 = require('bcrypto/lib/encoding/base16');
70✔
8
const bech32 = require('bcrypto/lib/encoding/bech32');
70✔
9
const BLAKE2b = require('bcrypto/lib/blake2b');
70✔
10
const SHA256 = require('bcrypto/lib/sha256');
70✔
11
const rsa = require('bcrypto/lib/rsa');
70✔
12
const p256 = require('bcrypto/lib/p256');
70✔
13
const ed25519 = require('bcrypto/lib/ed25519');
70✔
14
const {countLeft} = require('bcrypto/lib/encoding/util');
70✔
15
const Goo = require('goosig');
70✔
16

17
/** @typedef {import('../types').Hash} Hash */
18
/** @typedef {import('../types').Amount} AmountValue */
19
/** @typedef {import('../types').BufioWriter} BufioWriter */
20

21
/*
22
 * Goo
23
 */
24

25
const goo = new Goo(Goo.RSA2048, 2, 3);
70✔
26

27
/*
28
 * Constants
29
 */
30

31
const keyTypes = {
70✔
32
  RSA: 0,
33
  GOO: 1,
34
  P256: 2,
35
  ED25519: 3,
36
  ADDRESS: 4
37
};
38

39
const keyTypesByVal = {
70✔
40
  [keyTypes.RSA]: 'RSA',
41
  [keyTypes.GOO]: 'GOO',
42
  [keyTypes.P256]: 'P256',
43
  [keyTypes.ED25519]: 'ED25519',
44
  [keyTypes.ADDRESS]: 'ADDRESS'
45
};
46

47
const EMPTY = Buffer.alloc(0);
70✔
48

49
/**
50
 * AirdropKey
51
 */
52

53
class AirdropKey extends bio.Struct {
54
  constructor() {
55
    super();
90✔
56
    this.type = keyTypes.RSA;
90✔
57
    this.n = EMPTY;
90✔
58
    this.e = EMPTY;
90✔
59
    this.C1 = EMPTY;
90✔
60
    this.point = EMPTY;
90✔
61
    this.version = 0;
90✔
62
    this.address = EMPTY;
90✔
63
    this.value = 0;
90✔
64
    this.sponsor = false;
90✔
65
    this.nonce = SHA256.zero;
90✔
66
    this.tweak = null;
90✔
67
  }
68

69
  /**
70
   * @param {AirdropKey} key
71
   * @returns {this}
72
   */
73

74
  inject(key) {
75
    assert(key instanceof AirdropKey);
×
76

77
    this.type = key.type;
×
78
    this.n = key.n;
×
79
    this.e = key.e;
×
80
    this.C1 = key.C1;
×
81
    this.point = key.point;
×
82
    this.version = key.version;
×
83
    this.address = key.address;
×
84
    this.value = key.value;
×
85
    this.sponsor = key.sponsor;
×
86
    this.nonce = key.nonce;
×
87
    this.tweak = key.tweak;
×
88

89
    return this;
×
90
  }
91

92
  isRSA() {
93
    return this.type === keyTypes.RSA;
18✔
94
  }
95

96
  isGoo() {
97
    return this.type === keyTypes.GOO;
3✔
98
  }
99

100
  isP256() {
101
    return this.type === keyTypes.P256;
×
102
  }
103

104
  isED25519() {
105
    return this.type === keyTypes.ED25519;
×
106
  }
107

108
  isAddress() {
109
    return this.type === keyTypes.ADDRESS;
16✔
110
  }
111

112
  isWeak() {
113
    if (!this.isRSA())
18!
114
      return false;
18✔
115

116
    return countLeft(this.n) < 2048 - 7;
×
117
  }
118

119
  /**
120
   * @returns {Boolean}
121
   */
122

123
  validate() {
124
    switch (this.type) {
×
125
      case keyTypes.RSA: {
126
        let key;
127

128
        try {
×
129
          key = rsa.publicKeyImport({ n: this.n, e: this.e });
×
130
        } catch (e) {
131
          return false;
×
132
        }
133

134
        const bits = rsa.publicKeyBits(key);
×
135

136
        // Allow 1024 bit RSA for now.
137
        // We can softfork out later.
138
        return bits >= 1024 && bits <= 4096;
×
139
      }
140

141
      case keyTypes.GOO: {
142
        return this.C1.length === goo.size;
×
143
      }
144

145
      case keyTypes.P256: {
146
        return p256.publicKeyVerify(this.point);
×
147
      }
148

149
      case keyTypes.ED25519: {
150
        return ed25519.publicKeyVerify(this.point);
×
151
      }
152

153
      case keyTypes.ADDRESS: {
154
        return true;
×
155
      }
156

157
      default: {
158
        throw new assert.AssertionError('Invalid key type.');
×
159
      }
160
    }
161
  }
162

163
  /**
164
   * @param {Buffer} msg
165
   * @param {Buffer} sig
166
   * @returns {Boolean}
167
   */
168

169
  verify(msg, sig) {
170
    assert(Buffer.isBuffer(msg));
10✔
171
    assert(Buffer.isBuffer(sig));
10✔
172

173
    switch (this.type) {
10!
174
      case keyTypes.RSA: {
175
        let key;
176

177
        try {
×
178
          key = rsa.publicKeyImport({ n: this.n, e: this.e });
×
179
        } catch (e) {
180
          return false;
×
181
        }
182

183
        return rsa.verify(SHA256, msg, sig, key);
×
184
      }
185

186
      case keyTypes.GOO: {
187
        return goo.verify(msg, sig, this.C1);
10✔
188
      }
189

190
      case keyTypes.P256: {
191
        return p256.verify(msg, sig, this.point);
×
192
      }
193

194
      case keyTypes.ED25519: {
195
        return ed25519.verify(msg, sig, this.point);
×
196
      }
197

198
      case keyTypes.ADDRESS: {
199
        return true;
×
200
      }
201

202
      default: {
203
        throw new assert.AssertionError('Invalid key type.');
×
204
      }
205
    }
206
  }
207

208
  /**
209
   * @returns {Hash}
210
   */
211

212
  hash() {
213
    const bw = bio.pool(this.getSize());
×
214
    this.write(bw);
×
215
    return BLAKE2b.digest(bw.render());
×
216
  }
217

218
  getSize() {
219
    let size = 0;
1✔
220

221
    size += 1;
1✔
222

223
    switch (this.type) {
1!
224
      case keyTypes.RSA:
225
        assert(this.n.length <= 0xffff);
×
226
        assert(this.e.length <= 0xff);
×
227
        size += 2;
×
228
        size += this.n.length;
×
229
        size += 1;
×
230
        size += this.e.length;
×
231
        size += 32;
×
232
        break;
×
233
      case keyTypes.GOO:
234
        size += goo.size;
1✔
235
        break;
1✔
236
      case keyTypes.P256:
237
        size += 33;
×
238
        size += 32;
×
239
        break;
×
240
      case keyTypes.ED25519:
241
        size += 32;
×
242
        size += 32;
×
243
        break;
×
244
      case keyTypes.ADDRESS:
245
        size += 1;
×
246
        size += 1;
×
247
        size += this.address.length;
×
248
        size += 8;
×
249
        size += 1;
×
250
        break;
×
251
      default:
252
        throw new assert.AssertionError('Invalid key type.');
×
253
    }
254

255
    return size;
1✔
256
  }
257

258
  /**
259
   * @param {BufioWriter} bw
260
   * @returns {BufioWriter}
261
   */
262

263
  write(bw) {
264
    bw.writeU8(this.type);
1✔
265

266
    switch (this.type) {
1!
267
      case keyTypes.RSA:
268
        bw.writeU16(this.n.length);
×
269
        bw.writeBytes(this.n);
×
270
        bw.writeU8(this.e.length);
×
271
        bw.writeBytes(this.e);
×
272
        bw.writeBytes(this.nonce);
×
273
        break;
×
274
      case keyTypes.GOO:
275
        bw.writeBytes(this.C1);
1✔
276
        break;
1✔
277
      case keyTypes.P256:
278
      case keyTypes.ED25519:
279
        bw.writeBytes(this.point);
×
280
        bw.writeBytes(this.nonce);
×
281
        break;
×
282
      case keyTypes.ADDRESS:
283
        bw.writeU8(this.version);
×
284
        bw.writeU8(this.address.length);
×
285
        bw.writeBytes(this.address);
×
286
        bw.writeU64(this.value);
×
287
        bw.writeU8(this.sponsor ? 1 : 0);
×
288
        break;
×
289
      default:
290
        throw new assert.AssertionError('Invalid key type.');
×
291
    }
292

293
    return bw;
1✔
294
  }
295

296
  /**
297
   * @param {bio.BufferReader} br
298
   * @returns {this}
299
   */
300

301
  read(br) {
302
    this.type = br.readU8();
90✔
303

304
    switch (this.type) {
90!
305
      case keyTypes.RSA: {
306
        this.n = br.readBytes(br.readU16());
×
307
        this.e = br.readBytes(br.readU8());
×
308
        this.nonce = br.readBytes(32);
×
309
        break;
×
310
      }
311

312
      case keyTypes.GOO: {
313
        this.C1 = br.readBytes(goo.size);
25✔
314
        break;
25✔
315
      }
316

317
      case keyTypes.P256: {
318
        this.point = br.readBytes(33);
×
319
        this.nonce = br.readBytes(32);
×
320
        break;
×
321
      }
322

323
      case keyTypes.ED25519: {
324
        this.point = br.readBytes(32);
×
325
        this.nonce = br.readBytes(32);
×
326
        break;
×
327
      }
328

329
      case keyTypes.ADDRESS: {
330
        this.version = br.readU8();
65✔
331
        this.address = br.readBytes(br.readU8());
65✔
332
        this.value = br.readU64();
65✔
333
        this.sponsor = br.readU8() === 1;
65✔
334
        break;
65✔
335
      }
336

337
      default: {
338
        throw new Error('Unknown key type.');
×
339
      }
340
    }
341

342
    return this;
90✔
343
  }
344

345
  /**
346
   * @param {String} addr
347
   * @param {AmountValue} value
348
   * @param {Boolean} sponsor
349
   * @returns {this}
350
   */
351

352
  fromAddress(addr, value, sponsor = false) {
×
353
    assert(typeof addr === 'string');
×
354
    assert(Number.isSafeInteger(value) && value >= 0);
×
355
    assert(typeof sponsor === 'boolean');
×
356

357
    const [hrp, version, hash] = bech32.decode(addr);
×
358

359
    assert(hrp === 'hs' || hrp === 'ts' || hrp === 'rs');
×
360
    assert(version === 0);
×
361
    assert(hash.length === 20 || hash.length === 32);
×
362

363
    this.type = keyTypes.ADDRESS;
×
364
    this.version = version;
×
365
    this.address = hash;
×
366
    this.value = value;
×
367
    this.sponsor = sponsor;
×
368

369
    return this;
×
370
  }
371

372
  getJSON() {
373
    return {
×
374
      type: keyTypesByVal[this.type] || 'UNKNOWN',
×
375
      n: this.n.length > 0
×
376
        ? this.n.toString('hex')
377
        : undefined,
378
      e: this.e.length > 0
×
379
        ? this.e.toString('hex')
380
        : undefined,
381
      C1: this.C1.length > 0
×
382
        ? this.C1.toString('hex')
383
        : undefined,
384
      point: this.point.length > 0
×
385
        ? this.point.toString('hex')
386
        : undefined,
387
      version: this.address.length > 0
×
388
        ? this.version
389
        : undefined,
390
      address: this.address.length > 0
×
391
        ? this.address.toString('hex')
392
        : undefined,
393
      value: this.value || undefined,
×
394
      sponsor: this.value
×
395
        ? this.sponsor
396
        : undefined,
397
      nonce: !this.isGoo() && !this.isAddress()
×
398
        ? this.nonce.toString('hex')
399
        : undefined
400
    };
401
  }
402

403
  /**
404
   * @param {Object} json
405
   * @returns {this}
406
   */
407

408
  fromJSON(json) {
409
    assert(json && typeof json === 'object');
×
410
    assert(typeof json.type === 'string');
×
411
    assert(Object.prototype.hasOwnProperty.call(keyTypes, json.type));
×
412

413
    this.type = keyTypes[json.type];
×
414

NEW
415
    console.log(base16.decode.toString());
×
UNCOV
416
    switch (this.type) {
×
417
      case keyTypes.RSA: {
418
        this.n = base16.decode(json.n);
×
419
        this.e = base16.decode(json.e);
×
NEW
420
        this.nonce = base16.decode(json.nonce);
×
421
        break;
×
422
      }
423

424
      case keyTypes.GOO: {
425
        this.C1 = base16.decode(json.C1);
×
426
        break;
×
427
      }
428

429
      case keyTypes.P256: {
NEW
430
        this.point = base16.decode(json.point);
×
NEW
431
        this.nonce = base16.decode(json.nonce);
×
UNCOV
432
        break;
×
433
      }
434

435
      case keyTypes.ED25519: {
NEW
436
        this.point = base16.decode(json.point);
×
NEW
437
        this.nonce = base16.decode(json.nonce);
×
UNCOV
438
        break;
×
439
      }
440

441
      case keyTypes.ADDRESS: {
442
        assert((json.version & 0xff) === json.version);
×
443
        assert(Number.isSafeInteger(json.value) && json.value >= 0);
×
444
        assert(typeof json.sponsor === 'boolean');
×
445
        this.version = json.version;
×
446
        this.address = base16.decode(json.address);
×
447
        this.value = json.value;
×
448
        this.sponsor = json.sponsor;
×
449
        break;
×
450
      }
451

452
      default: {
453
        throw new Error('Unknown key type.');
×
454
      }
455
    }
456

457
    return this;
×
458
  }
459

460
  /**
461
   * @param {String} addr
462
   * @param {AmountValue} value
463
   * @param {Boolean} sponsor
464
   * @returns {AirdropKey}
465
   */
466

467
  static fromAddress(addr, value, sponsor) {
468
    return new this().fromAddress(addr, value, sponsor);
×
469
  }
470
}
471

472
/*
473
 * Static
474
 */
475

476
AirdropKey.keyTypes = keyTypes;
70✔
477
AirdropKey.keyTypesByVal = keyTypesByVal;
70✔
478

479
/*
480
 * Expose
481
 */
482

483
module.exports = AirdropKey;
70✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc