Skip to content

Commit

Permalink
chore: doc fixes, refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
dirkmc committed Feb 19, 2019
1 parent dece009 commit 03ec3d3
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 53 deletions.
12 changes: 4 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ This module contains all the necessary code for creating, understanding and vali
const ipns = require('ipns')

const entryData = await ipns.create(privateKey, value, sequenceNumber, lifetime)
// your code goes here
```

#### Validate record
Expand All @@ -48,7 +47,6 @@ const entryData = await ipns.create(privateKey, value, sequenceNumber, lifetime)
const ipns = require('ipns')

await ipns.validate(publicKey, ipnsEntry)
// your code goes here
// if no error thrown, the record is valid
```

Expand All @@ -58,7 +56,6 @@ await ipns.validate(publicKey, ipnsEntry)
const ipns = require('ipns')

const ipnsEntryWithEmbedPublicKey = await ipns.embedPublicKey(publicKey, ipnsEntry)
// your code goes here
```

#### Extract public key from record
Expand All @@ -67,7 +64,6 @@ const ipnsEntryWithEmbedPublicKey = await ipns.embedPublicKey(publicKey, ipnsEnt
const ipns = require('ipns')

const publicKey = ipns.extractPublicKey(peerId, ipnsEntry)
// your code goes here
```

#### Datastore key
Expand All @@ -89,7 +85,7 @@ Returns a key to be used for storing the ipns entry locally, that is:
```js
const ipns = require('ipns')

const entryData = ipns.create(privateKey, value, sequenceNumber, lifetime)
const entryData = await ipns.create(privateKey, value, sequenceNumber, lifetime)
// ...
const marshalledData = ipns.marshal(entryData)
// ...
Expand Down Expand Up @@ -117,7 +113,7 @@ const validator = ipns.validator

Contains an object with `validate (marshalledData, key)` and `select (dataA, dataB)` functions.

The `validate` function aims to verify if an IPNS record is valid. First the record is unmarshalled, then the public key is obtained and finally the record is validated (signature and validity are verified).
The `validate` async function aims to verify if an IPNS record is valid. First the record is unmarshalled, then the public key is obtained and finally the record is validated (signature and validity are verified).

The `select` function is responsible for deciding which ipns record is the best (newer) between two records. Both records are unmarshalled and their sequence numbers are compared. If the first record provided is the newer, the operation result will be `0`, otherwise the operation result will be `1`.

Expand Down Expand Up @@ -161,7 +157,7 @@ Validate an IPNS record previously stored in a protocol buffer.
- `publicKey` (`PubKey` [RSA Instance](https://github.com/libp2p/js-libp2p-crypto/blob/master/src/keys/rsa-class.js)): key to be used for cryptographic operations.
- `ipnsEntry` (Object): ipns entry record (obtained using the create function).

Throws an error if the validation was not successful.
Returns a `Promise`, which may be rejected if the validation was not successful.

#### Datastore key

Expand Down Expand Up @@ -196,7 +192,7 @@ Returns the entry data structure after being serialized.
#### Embed public key to record

```js
ipns.embedPublicKey(publicKey, ipnsEntry)
const recordWithPublicKey = await ipns.embedPublicKey(publicKey, ipnsEntry)
```

Embed a public key in an IPNS entry. If it is possible to extract the public key from the `peer-id`, there is no need to embed.
Expand Down
57 changes: 34 additions & 23 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ const ID_MULTIHASH_CODE = multihash.names.id

const namespace = '/ipns/'

/**
* IPNS entry
* @typedef {Object} IpnsEntry
* @property {string} value - value to be stored in the record
* @property {Buffer} signature - signature of the record
* @property {number} validityType - Type of validation being used
* @property {string} validity - expiration datetime for the record in RFC3339 format
* @property {number} sequence - number representing the version of the record
*/

/**
* Creates a new ipns entry and signs it with the given private key.
* The ipns entry validity should follow the [RFC3339]{@link https://www.ietf.org/rfc/rfc3339.txt} with nanoseconds precision.
Expand All @@ -29,7 +39,7 @@ const namespace = '/ipns/'
* @param {string} value value to be stored in the record.
* @param {number} seq number representing the current version of the record.
* @param {number|string} lifetime lifetime of the record (in milliseconds).
* @returns {Object} entry
* @returns {Promise<IpnsEntry>} entry
*/
const create = (privateKey, value, seq, lifetime) => {
// Validity in ISOString with nanoseconds precision and validity type EOL
Expand All @@ -45,38 +55,34 @@ const create = (privateKey, value, seq, lifetime) => {
* @param {string} value value to be stored in the record.
* @param {number} seq number representing the current version of the record.
* @param {string} expiration expiration datetime for record in the [RFC3339]{@link https://www.ietf.org/rfc/rfc3339.txt} with nanoseconds precision.
* @returns {Object} entry
* @returns {Promise<IpnsEntry>} entry
*/
const createWithExpiration = (privateKey, value, seq, expiration) => {
const validityType = ipnsEntryProto.ValidityType.EOL
return _create(privateKey, value, seq, expiration, validityType)
}

const _create = async (privateKey, value, seq, isoValidity, validityType) => {
try {
const signature = await sign(privateKey, value, validityType, isoValidity)

const entry = {
value: value,
signature: signature,
validityType: validityType,
validity: isoValidity,
sequence: seq
}

log(`ipns entry for ${value} created`)
return entry
} catch (error) {
log.error('record signature creation failed')
throw errCode('record signature verification failed', ERRORS.ERR_SIGNATURE_CREATION)
const signature = await sign(privateKey, value, validityType, isoValidity)

const entry = {
value: value,
signature: signature,
validityType: validityType,
validity: isoValidity,
sequence: seq
}

log(`ipns entry for ${value} created`)
return entry
}

/**
* Validates the given ipns entry against the given public key.
*
* @param {Object} publicKey public key for validating the record.
* @param {Object} entry ipns entry record.
* @param {IpnsEntry} entry ipns entry record.
* @returns {Promise}
*/
const validate = async (publicKey, entry) => {
const { value, validityType, validity } = entry
Expand Down Expand Up @@ -128,7 +134,7 @@ const validate = async (publicKey, entry) => {
*
* @param {Object} publicKey public key to embed.
* @param {Object} entry ipns entry record.
* @return {Object} entry with public key embedded
* @return {IpnsEntry} entry with public key embedded
*/
const embedPublicKey = async (publicKey, entry) => {
if (!publicKey || !publicKey.bytes || !entry) {
Expand Down Expand Up @@ -172,7 +178,7 @@ const embedPublicKey = async (publicKey, entry) => {
* Extracts a public key matching `pid` from the ipns record.
*
* @param {Object} peerId peer identifier object.
* @param {Object} entry ipns entry record.
* @param {IpnsEntry} entry ipns entry record.
* @returns {Object} the public key
*/
const extractPublicKey = (peerId, entry) => {
Expand Down Expand Up @@ -229,8 +235,13 @@ const getIdKeys = (pid) => {

// Sign ipns record data
const sign = (privateKey, value, validityType, validity) => {
const dataForSignature = ipnsEntryDataForSig(value, validityType, validity)
return privateKey.sign(dataForSignature)
try {
const dataForSignature = ipnsEntryDataForSig(value, validityType, validity)
return privateKey.sign(dataForSignature)
} catch (error) {
log.error('record signature creation failed')
throw errCode('record signature creation failed: ' + error.message, ERRORS.ERR_SIGNATURE_CREATION)
}
}

// Utility for getting the validity type code name of a validity
Expand Down
43 changes: 21 additions & 22 deletions test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,28 @@ describe('ipns', function () {
let ipfsId = null
let rsa = null

const spawnDaemon = (cb) => {
df.spawn({ initOptions: { bits: 512 } }, (err, _ipfsd) => {
expect(err).to.not.exist()
ipfsd = _ipfsd
ipfs = ipfsd.api

ipfs.id((err, id) => {
if (err) {
return cb(err)
}

ipfsId = id
cb()
const spawnDaemon = () => {
return new Promise((resolve, reject) => {
df.spawn({ initOptions: { bits: 512 } }, (err, _ipfsd) => {
expect(err).to.not.exist()
ipfsd = _ipfsd
ipfs = ipfsd.api

ipfs.id((err, id) => {
if (err) {
return reject(err)
}

ipfsId = id
resolve()
})
})
})
}

before(async () => {
rsa = await crypto.keys.generateKeyPair('RSA', 2048)
return new Promise((resolve, reject) => spawnDaemon(err => err ? reject(err) : resolve()))
return spawnDaemon()
})

after(function (done) {
Expand Down Expand Up @@ -185,17 +187,14 @@ describe('ipns', function () {
// https://github.com/libp2p/go-libp2p-peer/blob/7f219a1e70011a258c5d3e502aef6896c60d03ce/peer.go#L80
// IDFromEd25519PublicKey is not currently implement on js-libp2p-peer
// https://github.com/libp2p/go-libp2p-peer/pull/30
it.skip('should be able to extract a public key directly from the peer', (done) => {
it.skip('should be able to extract a public key directly from the peer', async () => {
const sequence = 0
const validity = 1000000

crypto.keys.generateKeyPair('ed25519', 2048, async (err, ed25519) => {
expect(err).to.not.exist()

const entry = await ipns.create(ed25519, cid, sequence, validity)
const entryWithKey = ipns.embedPublicKey(ed25519.public, entry)
expect(entryWithKey).to.not.exist() // Should be null
})
const ed25519 = await crypto.keys.generateKeyPair('ed25519', 2048)
const entry = await ipns.create(ed25519, cid, sequence, validity)
const entryWithKey = ipns.embedPublicKey(ed25519.public, entry)
expect(entryWithKey).to.not.exist() // Should be null
})

it('should be able to export a previously embed public key from an ipns record', async () => {
Expand Down

0 comments on commit 03ec3d3

Please sign in to comment.