commit 710384e2390ab59b834a9a4f1b56fe19066f2458
parent 7a9b48c55e356d643f549e3d7c375c17d3273c1e
Author: Jared Tobin <jared@jtobin.io>
Date: Fri, 25 Sep 2020 12:06:37 -0230
Merge branch 'drbg' into master
Diffstat:
5 files changed, 103 insertions(+), 15 deletions(-)
diff --git a/README.md b/README.md
@@ -7,6 +7,11 @@
Securely generate [UP8][up8p]-compatible, `@q`-encoded master tickets. Split
and combine tickets via a k/n Shamir's Secret Sharing scheme.
+If you plan on generating a master ticket for a galaxy wallet, for example, you
+might want to use `gen_ticket_drbg(384)` to generate the ticket, and then
+`shard(.., 5, 3)` to split it into five shares (any three of which can be used
+to recover it).
+
## Install
Grab it from npm like so:
@@ -48,7 +53,8 @@ Type ".help" for more information.
### gen\_ticket\_simple
-Generate a 256-bit master ticket via a simple CSPRNG:
+Generate a 256-bit master ticket via a simple CSPRNG (`crypto` or
+`window.crypto`):
```
> up8.gen_ticket_simple(256)
@@ -60,15 +66,15 @@ argument as a Buffer. It will simply be XOR'd with the random bytes produced
internally:
```
-> up8.gen_ticket_simple(256, Buffer.from("a very very random string"))
+> up8.gen_ticket_simple(256, Buffer.from("a very random string"))
'~donryd-mallur-wanrex-fidrex-nidwyt-dildul-padryd-talfen-panneb-nocbep-norwep-mispel-ralryc-fiddun-tomsup-toltex'
```
### gen\_ticket\_more
-Do the same thing, but use [more-entropy][ment] to generate the ticket using
-additional entropy. Note that it returns a Promise (and takes a little
-longer):
+Do the same thing, but also use [more-entropy][ment] to produce additional
+entropy when generating the ticket. Note that it returns a Promise (and takes
+a little longer):
```
> await up8.gen_ticket_more(256)
@@ -82,6 +88,27 @@ You can similarly pass your own entropy in as an additional Buffer here:
'~rivmer-ticnyd-mirfet-rolbyt-tarlus-ricrun-fitmec-losrul-barhep-misfet-pidfen-foshep-ronrem-natlyx-tarlet-sipdeb'
```
+### gen\_ticket\_drbg
+
+Do the same thing, but use a HMAC-DRBG function to combine the entropy produced
+by the underlying CSPRNG and more-entropy. Like `gen_ticket_more`, it returns
+a Promise, and takes longer.
+
+Note that you must use at least 192 bits of entropy for this method.
+
+```
+> await up8.gen_ticket_drbg(256)
+'~morten-davnys-ronpes-hidtyd-pittev-donsug-fonpel-sornet-wacmeb-harbyl-monduc-linmur-racled-namdec-tildul-palmyn'
+```
+
+As with the other functions, you can pass your own entropy in as an additional
+Buffer:
+
+```
+> let ticket = await up8.gen_ticket_drbg(384, Buffer.from('a personalization string'))
+'~siller-hopryc-ripfyn-laglec-linpur-mogpun-poldux-bicmul-radnum-dapnup-monnub-dilwex-pacrym-samrup-ragryc-samdyt-timdys-hartul-lonrun-posmev-molrum-miclur-doznus-fasnut'
+```
+
### shard
Split a ticket into 'shards' using a k/n Shamir's Secret Sharing scheme.
@@ -89,7 +116,7 @@ Specify the number of shards to create and the number of shards required to
reassemble the original ticket, along with the ticket itself:
```
-> let ticket = await up8.gen_ticket_more(384)
+> let ticket = await up8.gen_ticket_drbg(384)
> let shards = up8.shard(ticket, 3, 2)
> shards
[
diff --git a/package-lock.json b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "up8-ticket",
- "version": "0.2.0",
+ "version": "0.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -1723,7 +1723,6 @@
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
- "dev": true,
"requires": {
"inherits": "^2.0.3",
"minimalistic-assert": "^1.0.1"
@@ -1749,7 +1748,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
"integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
- "dev": true,
"requires": {
"hash.js": "^1.0.3",
"minimalistic-assert": "^1.0.0",
@@ -1826,8 +1824,7 @@
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "dev": true
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"inline-source-map": {
"version": "0.6.2",
@@ -2329,14 +2326,12 @@
"minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
- "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
- "dev": true
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
},
"minimalistic-crypto-utils": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
- "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
- "dev": true
+ "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
},
"minimatch": {
"version": "3.0.4",
diff --git a/package.json b/package.json
@@ -26,6 +26,8 @@
"author": "~nidsut-tomdun",
"license": "MIT",
"dependencies": {
+ "hash.js": "^1.1.7",
+ "hmac-drbg": "^1.0.1",
"lodash.chunk": "^4.2.0",
"lodash.flatmap": "^4.5.0",
"lodash.zipwith": "^4.2.0",
diff --git a/src/index.js b/src/index.js
@@ -1,5 +1,7 @@
const chunk = require('lodash.chunk')
const flatMap = require('lodash.flatmap')
+const hash = require('hash.js')
+const DRBG = require('hmac-drbg')
const more = require('more-entropy')
const ob = require('urbit-ob')
const secrets = require('secrets.js-grempe')
@@ -93,6 +95,42 @@ const gen_ticket_more = (nbits, addl) => {
}
/*
+ * Generate a master ticket of the desired bitlength.
+ *
+ * Uses both 'crypto.rng' and 'more-entropy' to produce the required entropy
+ * and nonce for input to a HMAC-DRBG generator, respectively.
+ *
+ * A buffer provided as the second argument will be used as the DRBG
+ * personalisation string.
+ *
+ * @param {Number} nbits desired bitlength of ticket (minimum 192)
+ * @param {Buffer} addl an optional buffer of additional bytes
+ * @return {Promise<String>} a @q-encoded master ticket, wrapped in a Promise
+ */
+const gen_ticket_drbg = async (nbits, addl) => {
+ const nbytes = nbits / 8
+ const entropy = crypto.rng(nbytes)
+
+ const prng = new more.Generator()
+ const nonce = await new Promise((resolve, reject) => {
+ prng.generate(nbits, result => {
+ resolve(result.toString('hex'))
+ reject("entropy generation failed")
+ })
+ })
+
+ const d = new DRBG({
+ hash: hash.sha256,
+ entropy: entropy,
+ nonce: nonce,
+ pers: Buffer.isBuffer(addl) ? addl.toString('hex') : null
+ })
+
+ const bytes = d.generate(nbytes, 'hex')
+ return ob.hex2patq(bytes)
+}
+
+/*
* Shard a ticket via a k/n Shamir's Secret Sharing scheme.
*
* Provided with a ticket, a desired number of shards 'n', and threshold value
@@ -136,6 +174,7 @@ const combine = shards => {
module.exports = {
gen_ticket_simple,
gen_ticket_more,
+ gen_ticket_drbg,
shard,
combine
diff --git a/test/test.js b/test/test.js
@@ -70,6 +70,31 @@ describe('gen_ticket_more', () => {
})
+describe('gen_ticket_drbg', () => {
+
+ context("when no additional entropy provided", () => {
+ it('produces tickets of the correct bitlength', async () => {
+ let given_bits = 384
+ let ticket = await mtg.gen_ticket_drbg(given_bits)
+ let hex = ob.patq2hex(ticket)
+ let expected_bits = hex.length * 4
+ expect(expected_bits).to.equal(given_bits)
+ })
+ })
+
+ context("when additional entropy provided", () => {
+ it('produces tickets of the correct bitlength', async () => {
+ let given_bits = 384
+ let addl = Buffer.from("you'll really never predict this")
+ let ticket = await mtg.gen_ticket_drbg(given_bits, addl)
+ let hex = ob.patq2hex(ticket)
+ let expected_bits = hex.length * 4
+ expect(expected_bits).to.equal(given_bits)
+ })
+ })
+
+})
+
describe('shard', () => {
it('throws on non-@q input', () => {