Source: primitives/airdropkey.js

  1. /* eslint camelcase: 'off' */
  2. 'use strict';
  3. const assert = require('bsert');
  4. const bio = require('bufio');
  5. const base16 = require('bcrypto/lib/encoding/base16');
  6. const bech32 = require('bcrypto/lib/encoding/bech32');
  7. const BLAKE2b = require('bcrypto/lib/blake2b');
  8. const SHA256 = require('bcrypto/lib/sha256');
  9. const rsa = require('bcrypto/lib/rsa');
  10. const p256 = require('bcrypto/lib/p256');
  11. const ed25519 = require('bcrypto/lib/ed25519');
  12. const {countLeft} = require('bcrypto/lib/encoding/util');
  13. const Goo = require('goosig');
  14. /** @typedef {import('../types').Hash} Hash */
  15. /** @typedef {import('../types').Amount} AmountValue */
  16. /** @typedef {import('../types').BufioWriter} BufioWriter */
  17. /*
  18. * Goo
  19. */
  20. const goo = new Goo(Goo.RSA2048, 2, 3);
  21. /*
  22. * Constants
  23. */
  24. const keyTypes = {
  25. RSA: 0,
  26. GOO: 1,
  27. P256: 2,
  28. ED25519: 3,
  29. ADDRESS: 4
  30. };
  31. const keyTypesByVal = {
  32. [keyTypes.RSA]: 'RSA',
  33. [keyTypes.GOO]: 'GOO',
  34. [keyTypes.P256]: 'P256',
  35. [keyTypes.ED25519]: 'ED25519',
  36. [keyTypes.ADDRESS]: 'ADDRESS'
  37. };
  38. const EMPTY = Buffer.alloc(0);
  39. /**
  40. * AirdropKey
  41. */
  42. class AirdropKey extends bio.Struct {
  43. constructor() {
  44. super();
  45. this.type = keyTypes.RSA;
  46. this.n = EMPTY;
  47. this.e = EMPTY;
  48. this.C1 = EMPTY;
  49. this.point = EMPTY;
  50. this.version = 0;
  51. this.address = EMPTY;
  52. this.value = 0;
  53. this.sponsor = false;
  54. this.nonce = SHA256.zero;
  55. this.tweak = null;
  56. }
  57. /**
  58. * @param {AirdropKey} key
  59. * @returns {this}
  60. */
  61. inject(key) {
  62. assert(key instanceof AirdropKey);
  63. this.type = key.type;
  64. this.n = key.n;
  65. this.e = key.e;
  66. this.C1 = key.C1;
  67. this.point = key.point;
  68. this.version = key.version;
  69. this.address = key.address;
  70. this.value = key.value;
  71. this.sponsor = key.sponsor;
  72. this.nonce = key.nonce;
  73. this.tweak = key.tweak;
  74. return this;
  75. }
  76. isRSA() {
  77. return this.type === keyTypes.RSA;
  78. }
  79. isGoo() {
  80. return this.type === keyTypes.GOO;
  81. }
  82. isP256() {
  83. return this.type === keyTypes.P256;
  84. }
  85. isED25519() {
  86. return this.type === keyTypes.ED25519;
  87. }
  88. isAddress() {
  89. return this.type === keyTypes.ADDRESS;
  90. }
  91. isWeak() {
  92. if (!this.isRSA())
  93. return false;
  94. return countLeft(this.n) < 2048 - 7;
  95. }
  96. /**
  97. * @returns {Boolean}
  98. */
  99. validate() {
  100. switch (this.type) {
  101. case keyTypes.RSA: {
  102. let key;
  103. try {
  104. key = rsa.publicKeyImport({ n: this.n, e: this.e });
  105. } catch (e) {
  106. return false;
  107. }
  108. const bits = rsa.publicKeyBits(key);
  109. // Allow 1024 bit RSA for now.
  110. // We can softfork out later.
  111. return bits >= 1024 && bits <= 4096;
  112. }
  113. case keyTypes.GOO: {
  114. return this.C1.length === goo.size;
  115. }
  116. case keyTypes.P256: {
  117. return p256.publicKeyVerify(this.point);
  118. }
  119. case keyTypes.ED25519: {
  120. return ed25519.publicKeyVerify(this.point);
  121. }
  122. case keyTypes.ADDRESS: {
  123. return true;
  124. }
  125. default: {
  126. throw new assert.AssertionError('Invalid key type.');
  127. }
  128. }
  129. }
  130. /**
  131. * @param {Buffer} msg
  132. * @param {Buffer} sig
  133. * @returns {Boolean}
  134. */
  135. verify(msg, sig) {
  136. assert(Buffer.isBuffer(msg));
  137. assert(Buffer.isBuffer(sig));
  138. switch (this.type) {
  139. case keyTypes.RSA: {
  140. let key;
  141. try {
  142. key = rsa.publicKeyImport({ n: this.n, e: this.e });
  143. } catch (e) {
  144. return false;
  145. }
  146. return rsa.verify(SHA256, msg, sig, key);
  147. }
  148. case keyTypes.GOO: {
  149. return goo.verify(msg, sig, this.C1);
  150. }
  151. case keyTypes.P256: {
  152. return p256.verify(msg, sig, this.point);
  153. }
  154. case keyTypes.ED25519: {
  155. return ed25519.verify(msg, sig, this.point);
  156. }
  157. case keyTypes.ADDRESS: {
  158. return true;
  159. }
  160. default: {
  161. throw new assert.AssertionError('Invalid key type.');
  162. }
  163. }
  164. }
  165. /**
  166. * @returns {Hash}
  167. */
  168. hash() {
  169. const bw = bio.pool(this.getSize());
  170. this.write(bw);
  171. return BLAKE2b.digest(bw.render());
  172. }
  173. getSize() {
  174. let size = 0;
  175. size += 1;
  176. switch (this.type) {
  177. case keyTypes.RSA:
  178. assert(this.n.length <= 0xffff);
  179. assert(this.e.length <= 0xff);
  180. size += 2;
  181. size += this.n.length;
  182. size += 1;
  183. size += this.e.length;
  184. size += 32;
  185. break;
  186. case keyTypes.GOO:
  187. size += goo.size;
  188. break;
  189. case keyTypes.P256:
  190. size += 33;
  191. size += 32;
  192. break;
  193. case keyTypes.ED25519:
  194. size += 32;
  195. size += 32;
  196. break;
  197. case keyTypes.ADDRESS:
  198. size += 1;
  199. size += 1;
  200. size += this.address.length;
  201. size += 8;
  202. size += 1;
  203. break;
  204. default:
  205. throw new assert.AssertionError('Invalid key type.');
  206. }
  207. return size;
  208. }
  209. /**
  210. * @param {BufioWriter} bw
  211. * @returns {BufioWriter}
  212. */
  213. write(bw) {
  214. bw.writeU8(this.type);
  215. switch (this.type) {
  216. case keyTypes.RSA:
  217. bw.writeU16(this.n.length);
  218. bw.writeBytes(this.n);
  219. bw.writeU8(this.e.length);
  220. bw.writeBytes(this.e);
  221. bw.writeBytes(this.nonce);
  222. break;
  223. case keyTypes.GOO:
  224. bw.writeBytes(this.C1);
  225. break;
  226. case keyTypes.P256:
  227. case keyTypes.ED25519:
  228. bw.writeBytes(this.point);
  229. bw.writeBytes(this.nonce);
  230. break;
  231. case keyTypes.ADDRESS:
  232. bw.writeU8(this.version);
  233. bw.writeU8(this.address.length);
  234. bw.writeBytes(this.address);
  235. bw.writeU64(this.value);
  236. bw.writeU8(this.sponsor ? 1 : 0);
  237. break;
  238. default:
  239. throw new assert.AssertionError('Invalid key type.');
  240. }
  241. return bw;
  242. }
  243. /**
  244. * @param {bio.BufferReader} br
  245. * @returns {this}
  246. */
  247. read(br) {
  248. this.type = br.readU8();
  249. switch (this.type) {
  250. case keyTypes.RSA: {
  251. this.n = br.readBytes(br.readU16());
  252. this.e = br.readBytes(br.readU8());
  253. this.nonce = br.readBytes(32);
  254. break;
  255. }
  256. case keyTypes.GOO: {
  257. this.C1 = br.readBytes(goo.size);
  258. break;
  259. }
  260. case keyTypes.P256: {
  261. this.point = br.readBytes(33);
  262. this.nonce = br.readBytes(32);
  263. break;
  264. }
  265. case keyTypes.ED25519: {
  266. this.point = br.readBytes(32);
  267. this.nonce = br.readBytes(32);
  268. break;
  269. }
  270. case keyTypes.ADDRESS: {
  271. this.version = br.readU8();
  272. this.address = br.readBytes(br.readU8());
  273. this.value = br.readU64();
  274. this.sponsor = br.readU8() === 1;
  275. break;
  276. }
  277. default: {
  278. throw new Error('Unknown key type.');
  279. }
  280. }
  281. return this;
  282. }
  283. /**
  284. * @param {String} addr
  285. * @param {AmountValue} value
  286. * @param {Boolean} sponsor
  287. * @returns {this}
  288. */
  289. fromAddress(addr, value, sponsor = false) {
  290. assert(typeof addr === 'string');
  291. assert(Number.isSafeInteger(value) && value >= 0);
  292. assert(typeof sponsor === 'boolean');
  293. const [hrp, version, hash] = bech32.decode(addr);
  294. assert(hrp === 'hs' || hrp === 'ts' || hrp === 'rs');
  295. assert(version === 0);
  296. assert(hash.length === 20 || hash.length === 32);
  297. this.type = keyTypes.ADDRESS;
  298. this.version = version;
  299. this.address = hash;
  300. this.value = value;
  301. this.sponsor = sponsor;
  302. return this;
  303. }
  304. getJSON() {
  305. return {
  306. type: keyTypesByVal[this.type] || 'UNKNOWN',
  307. n: this.n.length > 0
  308. ? this.n.toString('hex')
  309. : undefined,
  310. e: this.e.length > 0
  311. ? this.e.toString('hex')
  312. : undefined,
  313. C1: this.C1.length > 0
  314. ? this.C1.toString('hex')
  315. : undefined,
  316. point: this.point.length > 0
  317. ? this.point.toString('hex')
  318. : undefined,
  319. version: this.address.length > 0
  320. ? this.version
  321. : undefined,
  322. address: this.address.length > 0
  323. ? this.address.toString('hex')
  324. : undefined,
  325. value: this.value || undefined,
  326. sponsor: this.value
  327. ? this.sponsor
  328. : undefined,
  329. nonce: !this.isGoo() && !this.isAddress()
  330. ? this.nonce.toString('hex')
  331. : undefined
  332. };
  333. }
  334. /**
  335. * @param {Object} json
  336. * @returns {this}
  337. */
  338. fromJSON(json) {
  339. assert(json && typeof json === 'object');
  340. assert(typeof json.type === 'string');
  341. assert(Object.prototype.hasOwnProperty.call(keyTypes, json.type));
  342. this.type = keyTypes[json.type];
  343. console.log(base16.decode.toString());
  344. switch (this.type) {
  345. case keyTypes.RSA: {
  346. this.n = base16.decode(json.n);
  347. this.e = base16.decode(json.e);
  348. this.nonce = base16.decode(json.nonce);
  349. break;
  350. }
  351. case keyTypes.GOO: {
  352. this.C1 = base16.decode(json.C1);
  353. break;
  354. }
  355. case keyTypes.P256: {
  356. this.point = base16.decode(json.point);
  357. this.nonce = base16.decode(json.nonce);
  358. break;
  359. }
  360. case keyTypes.ED25519: {
  361. this.point = base16.decode(json.point);
  362. this.nonce = base16.decode(json.nonce);
  363. break;
  364. }
  365. case keyTypes.ADDRESS: {
  366. assert((json.version & 0xff) === json.version);
  367. assert(Number.isSafeInteger(json.value) && json.value >= 0);
  368. assert(typeof json.sponsor === 'boolean');
  369. this.version = json.version;
  370. this.address = base16.decode(json.address);
  371. this.value = json.value;
  372. this.sponsor = json.sponsor;
  373. break;
  374. }
  375. default: {
  376. throw new Error('Unknown key type.');
  377. }
  378. }
  379. return this;
  380. }
  381. /**
  382. * @param {String} addr
  383. * @param {AmountValue} value
  384. * @param {Boolean} sponsor
  385. * @returns {AirdropKey}
  386. */
  387. static fromAddress(addr, value, sponsor) {
  388. return new this().fromAddress(addr, value, sponsor);
  389. }
  390. }
  391. /*
  392. * Static
  393. */
  394. AirdropKey.keyTypes = keyTypes;
  395. AirdropKey.keyTypesByVal = keyTypesByVal;
  396. /*
  397. * Expose
  398. */
  399. module.exports = AirdropKey;