Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions PROVE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# bns-prove

### Generate DNSSEC ownership proofs using local keys or hardware signing module (HSM).

_What is a DNSSEC ownership proof?_

The proof is a chain of DNSSEC keys and signatures starting with the root zone
(ICANN KSK2017) and ending with a signed TXT record in the target zone
([example](test/data/test-claim.proof)). To claim a reserved name in the
[Handshake blockchain network](https://handshake.org),
a cryptocurrency wallet address is encoded into a TXT record which is then signed
and passed to the network for decentralized verification of ownership.

Usage:

```
bns-prove [options] domain-name txt-string
```

To prove ownership of the domain name `example.com`, pass `bns-prove` the path
to the directory containing the zone signing key (ZSK). The private key must
exist in the specified directory in BIND’s private key format (v1.3)
([example](test/data/Khns-claim-test-2.xyz.+008+27259.private))

```
$ bns-prove -b -K /path/to/DNSSEC/keys/ example.com hns-regtest:aakgpzi7wgivq75...
```

### Options

`-x`: Output the proof formatted as a hex string (deprecated).

`-b`: Output the proof formatted as a base64 string (expected by [Handshake full node API](https://hsd-dev.org/api-docs/#sendrawclaim)).

`-s`: Do not upgrade insecure algorithms like `RSASHA1` or `RSASHA1NSEC3SHA1` if the signature
indicates a stronger algorithm (like `RSASHA256`).

`-r`: Do not allow weak keys like `RSA-1024`

`-t <number>`: Specify the expiration time in seconds for the RRSIG (default is one year).

`-K <string>`: Specify the directory containing the expected DNSSEC private key.

### HSM Mode Options

This software has been tested with
[SoftHSMv2](https://github.com/opendnssec/SoftHSMv2)
and should operate correctly with any device with a PKCS#11 interface.

`--hsm-module <string>`: Path to the HSM manufacturer's PKCS11 library.

`--hsm-slot <number>`: HSM slot where expected DNSSEC private key can be found.

`--hsm-pin <string>`: Normal user PIN for logging in to specified slot.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
DNS library, server, and validating recursive resolver for node.js, in pure
javascript.

To use this package for DNSSEC-ownership proofs (required for reserved name
claims on the [Handshake blockchain network](https://handshake.org)) see the
[bns-prove guide](PROVE.md).

## Server Usage

### Base Server
Expand Down
51 changes: 49 additions & 2 deletions bin/bns-prove
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const Resolver = require('../lib/resolver/stub');
const Ownership = require('../lib/ownership');
const util = require('../lib/util');
const wire = require('../lib/wire');
const hsm = require('../lib/hsm');

const {
keyFlags,
Expand All @@ -37,6 +38,10 @@ let dir = '.';
let name = null;
let txt = null;

let hsmModule = null;
let hsmSlot = null;
let hsmPin = null;

for (let i = 2; i < process.argv.length; i++) {
const arg = process.argv[i];
const next = i !== process.argv.length - 1
Expand Down Expand Up @@ -65,7 +70,7 @@ for (let i = 2; i < process.argv.length; i++) {
}

case '-t': {
lifespan = util.parseU32(lifespan);
lifespan = util.parseU32(next);
i += 1;
break;
}
Expand All @@ -80,6 +85,30 @@ for (let i = 2; i < process.argv.length; i++) {
break;
}

case '--hsm-module': {
if (!next)
throw new Error('Invalid module path.');

hsmModule = next;
i += 1;
break;
}

case '--hsm-slot': {
hsmSlot = util.parseU32(next);
i += 1;
break;
}

case '--hsm-pin': {
if (!next)
throw new Error('Invalid pin.');

hsmPin = next;
i += 1;
break;
}

case '-h':
case '--help':
case '-?':
Expand All @@ -102,6 +131,13 @@ for (let i = 2; i < process.argv.length; i++) {
}
}

if ((hsmModule || hsmPin || hsmSlot) &&
(!hsmModule || !hsmPin || !hsmSlot)) {
throw new Error(
'HSM mode requires all three options: --hsm-module --hsm-slot --hsm-pin'
);
}

if (!name || name === '.')
throw new Error('No name provided.');

Expand Down Expand Up @@ -157,7 +193,18 @@ if (!txt)

rd.txt.push(txt);

const sig = dnssec.sign(key, priv, [rr], lifespan);
let sig;
if (!hsmModule) {
sig = dnssec.sign(key, priv, [rr], lifespan);
} else {
const user = new hsm.HSMUser({
module: hsmModule,
slot: hsmSlot,
pin: hsmPin
});
user.open();
sig = user.sign(key, [rr]);
}

zone.claim.push(rr);
zone.claim.push(sig);
Expand Down
16 changes: 16 additions & 0 deletions lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,21 @@ const algHashes = {
[algs.ED448]: null
};

/**
* PKCS1v1.5+ASN.1 DigestInfo prefixes. (copied from bcrypto rsa.js)
* @see [RFC8017] Page 45, Section 9.2.
* @see [RFC8017] Page 63, Section B.1.
* @const {Object}
*/

const hashPrefixes = {
[hashes.SHA1]: Buffer.from('3021300906052b0e03021a05000414', 'hex'),
[hashes.SHA256]: Buffer.from('3031300d060960864801650304020105000420', 'hex'),
[hashes.GOST94]: Buffer.from('302e300a06062a850302021405000420', 'hex'),
[hashes.SHA384]: Buffer.from('3041300d060960864801650304020205000430', 'hex'),
[hashes.SHA512]: Buffer.from('3051300d060960864801650304020305000440', 'hex')
};

/**
* NSEC3 hashes.
* @enum {Number}
Expand Down Expand Up @@ -1170,6 +1185,7 @@ exports.algsByVal = algsByVal;
exports.hashes = hashes;
exports.hashesByVal = hashesByVal;
exports.algHashes = algHashes;
exports.hashPrefixes = hashPrefixes;
exports.nsecHashes = nsecHashes;
exports.nsecHashesByVal = nsecHashesByVal;
exports.certTypes = certTypes;
Expand Down
Loading