From 06ee5357f6cefd0e12356c4ee204fefe7124b468 Mon Sep 17 00:00:00 2001 From: Alex Potsides Date: Fri, 14 Aug 2020 09:11:54 +0100 Subject: [PATCH] fix: replace node buffers with uint8arrays (#67) BREAKING CHANGES: - All deps of this module use Uint8Arrays instead of Buffers - value and validity fields of IPNSEntries are now Uint8Arrays instead of Strings as they are `bytes` in the protobuf definition --- .aegir.js | 13 +++++++++++++ README.md | 38 ++++++++++++++++++++++++-------------- package.json | 16 ++++++++-------- src/index.js | 42 +++++++++++++++++++++++++----------------- test/index.spec.js | 20 ++++++++++---------- 5 files changed, 80 insertions(+), 49 deletions(-) create mode 100644 .aegir.js diff --git a/.aegir.js b/.aegir.js new file mode 100644 index 0000000..6ac5eb7 --- /dev/null +++ b/.aegir.js @@ -0,0 +1,13 @@ +'use strict' + +module.exports = { + webpack: { + node: { + // needed by the ipfs-repo-migrations module + path: true, + + // needed by the abstract-leveldown module + Buffer: true + } + } +} \ No newline at end of file diff --git a/README.md b/README.md index 76896b1..2906ddf 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# IPNS +# IPNS [![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://protocol.ai) [![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) @@ -12,20 +12,30 @@ This module contains all the necessary code for creating, understanding and validating IPNS records. -## Lead Maintainer +## Lead Maintainer [Vasco Santos](https://github.com/vasco-santos). -## Table of Contents +## Table of Contents -- [Install](#install) - [Usage](#usage) - - [Create Record](#create-record) - - [Validate Record](#validate-record) - - [Embed public key to record](#embed-public-key-to-record) - - [Extract public key from record](#extract-public-key-from-record) - - [Datastore key](#datastore-key) + - [Create record](#create-record) + - [Validate record](#validate-record) + - [Embed public key to record](#embed-public-key-to-record) + - [Extract public key from record](#extract-public-key-from-record) + - [Datastore key](#datastore-key) + - [Marshal data with proto buffer](#marshal-data-with-proto-buffer) + - [Unmarshal data from proto buffer](#unmarshal-data-from-proto-buffer) + - [Validator](#validator) - [API](#api) + - [Create record](#create-record-1) + - [Validate record](#validate-record-1) + - [Datastore key](#datastore-key-1) + - [Marshal data with proto buffer](#marshal-data-with-proto-buffer-1) + - [Unmarshal data from proto buffer](#unmarshal-data-from-proto-buffer-1) + - [Embed public key to record](#embed-public-key-to-record-1) + - [Extract public key from record](#extract-public-key-from-record-1) + - [Namespace](#namespace) - [Contribute](#contribute) - [License](#license) @@ -136,13 +146,13 @@ Create an IPNS record for being stored in a protocol buffer. - `lifetime` (string): lifetime of the record (in milliseconds). Returns a `Promise` that resolves to an object with the entry's properties eg: - + ```js { - value: '/ipfs/QmWEekX7EZLUd9VXRNMRXW3LXe4F6x7mB8oPxY5XLptrBq', - signature: Buffer, + value: Uint8Array, + signature: Uint8Array, validityType: 0, - validity: '2018-06-27T14:49:14.074000000Z', + validity: Uint8Array, sequence: 2 } ``` @@ -189,7 +199,7 @@ const data = ipns.unmarshal(storedData) Returns the entry data structure after being serialized. -- `storedData` (Buffer): ipns entry record serialized. +- `storedData` (Uint8Array): ipns entry record serialized. #### Embed public key to record diff --git a/package.json b/package.json index 6045d08..88be4df 100644 --- a/package.json +++ b/package.json @@ -33,26 +33,26 @@ }, "homepage": "https://github.com/ipfs/js-ipns#readme", "dependencies": { - "buffer": "^5.6.0", "debug": "^4.1.1", "err-code": "^2.0.0", - "interface-datastore": "^1.0.2", - "libp2p-crypto": "^0.17.1", + "interface-datastore": "^2.0.0", + "libp2p-crypto": "^0.18.0", "multibase": "^3.0.0", "multihashes": "^3.0.1", - "peer-id": "^0.13.6", - "protons": "^1.0.1", - "timestamp-nano": "^1.0.0" + "peer-id": "^0.14.0", + "protons": "^2.0.0", + "timestamp-nano": "^1.0.0", + "uint8arrays": "^1.1.0" }, "devDependencies": { - "aegir": "^23.0.0", + "aegir": "^25.1.0", "chai": "^4.2.0", "chai-bytes": "~0.1.2", "chai-string": "^1.5.0", "dirty-chai": "^2.0.1", "ipfs": "^0.49.0", "ipfs-http-client": "^46.0.0", - "ipfsd-ctl": "^4.0.1" + "ipfsd-ctl": "^6.0.0" }, "contributors": [ "Vasco Santos ", diff --git a/src/index.js b/src/index.js index 812f1a8..7ea3a6a 100644 --- a/src/index.js +++ b/src/index.js @@ -6,8 +6,10 @@ const crypto = require('libp2p-crypto') const PeerId = require('peer-id') const multihash = require('multihashes') const errCode = require('err-code') -const { Buffer } = require('buffer') const multibase = require('multibase') +const uint8ArrayFromString = require('uint8arrays/from-string') +const uint8ArrayToString = require('uint8arrays/to-string') +const uint8ArrayConcat = require('uint8arrays/concat') const debug = require('debug') const log = debug('jsipns') @@ -25,7 +27,7 @@ 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 {Uint8Array} 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 @@ -67,10 +69,10 @@ const _create = async (privateKey, value, seq, isoValidity, validityType) => { const signature = await sign(privateKey, value, validityType, isoValidity) const entry = { - value: value, + value: uint8ArrayFromString(value), signature: signature, validityType: validityType, - validity: isoValidity, + validity: uint8ArrayFromString(isoValidity), sequence: seq } @@ -106,7 +108,7 @@ const validate = async (publicKey, entry) => { let validityDate try { - validityDate = parseRFC3339(validity.toString()) + validityDate = parseRFC3339(uint8ArrayToString(validity)) } catch (e) { log.error('unrecognized validity format (not an rfc3339 format)') throw errCode(new Error('unrecognized validity format (not an rfc3339 format)'), ERRORS.ERR_UNRECOGNIZED_FORMAT) @@ -214,7 +216,7 @@ const rawStdEncoding = (key) => multibase.encode('base32', key).toString().slice * Get key for storing the record locally. * Format: /ipns/${base32()} * - * @param {Buffer} key peer identifier object. + * @param {Uint8Array} key peer identifier object. * @returns {string} */ const getLocalKey = (key) => new Key(`/ipns/${rawStdEncoding(key)}`) @@ -223,18 +225,18 @@ const getLocalKey = (key) => new Key(`/ipns/${rawStdEncoding(key)}`) * Get key for sharing the record in the routing mechanism. * Format: ${base32(/ipns/)}, ${base32(/pk/)} * - * @param {Buffer} pid peer identifier represented by the multihash of the public key as Buffer. + * @param {Uint8Array} pid peer identifier represented by the multihash of the public key as Uint8Array. * @returns {Object} containing the `nameKey` and the `ipnsKey`. */ const getIdKeys = (pid) => { - const pkBuffer = Buffer.from('/pk/') - const ipnsBuffer = Buffer.from('/ipns/') + const pkBuffer = uint8ArrayFromString('/pk/') + const ipnsBuffer = uint8ArrayFromString('/ipns/') return { - routingPubKey: new Key(Buffer.concat([pkBuffer, pid]), false), // Added on https://github.com/ipfs/js-ipns/pull/8#issue-213857876 (pkKey will be deprecated in a future release) - pkKey: new Key(rawStdEncoding(Buffer.concat([pkBuffer, pid]))), - routingKey: new Key(Buffer.concat([ipnsBuffer, pid]), false), // Added on https://github.com/ipfs/js-ipns/pull/6#issue-213631461 (ipnsKey will be deprecated in a future release) - ipnsKey: new Key(rawStdEncoding(Buffer.concat([ipnsBuffer, pid]))) + routingPubKey: new Key(uint8ArrayConcat([pkBuffer, pid]), false), // Added on https://github.com/ipfs/js-ipns/pull/8#issue-213857876 (pkKey will be deprecated in a future release) + pkKey: new Key(rawStdEncoding(uint8ArrayConcat([pkBuffer, pid]))), + routingKey: new Key(uint8ArrayConcat([ipnsBuffer, pid]), false), // Added on https://github.com/ipfs/js-ipns/pull/6#issue-213631461 (ipnsKey will be deprecated in a future release) + ipnsKey: new Key(rawStdEncoding(uint8ArrayConcat([ipnsBuffer, pid]))) } } @@ -263,11 +265,17 @@ const getValidityType = (validityType) => { // Utility for creating the record data for being signed const ipnsEntryDataForSig = (value, validityType, validity) => { - const valueBuffer = Buffer.from(value) - const validityTypeBuffer = Buffer.from(getValidityType(validityType)) - const validityBuffer = Buffer.from(validity) + if (!(value instanceof Uint8Array)) { + value = uint8ArrayFromString(value) + } + + if (!(validity instanceof Uint8Array)) { + validity = uint8ArrayFromString(validity) + } + + const validityTypeBuffer = uint8ArrayFromString(getValidityType(validityType)) - return Buffer.concat([valueBuffer, validityBuffer, validityTypeBuffer]) + return uint8ArrayConcat([value, validity, validityTypeBuffer]) } // Utility for extracting the public key from a peer-id diff --git a/test/index.spec.js b/test/index.spec.js index eed414f..100a798 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -5,12 +5,12 @@ const chai = require('chai') const dirtyChai = require('dirty-chai') const chaiBytes = require('chai-bytes') const chaiString = require('chai-string') -const { Buffer } = require('buffer') const expect = chai.expect chai.use(dirtyChai) chai.use(chaiBytes) chai.use(chaiString) const { toB58String } = require('multihashes') +const uint8ArrayFromString = require('uint8arrays/from-string') const ipfs = require('ipfs') const ipfsHttpClient = require('ipfs-http-client') @@ -56,7 +56,7 @@ describe('ipns', function () { const entry = await ipns.create(rsa, cid, sequence, validity) expect(entry).to.deep.include({ - value: cid, + value: uint8ArrayFromString(cid), sequence: sequence }) expect(entry).to.have.a.property('validity') @@ -73,7 +73,7 @@ describe('ipns', function () { await ipns.validate(rsa.public, entry) expect(entry).to.have.a.property('validity') - expect(entry.validity).to.equal('2033-05-18T03:33:20.000000000Z') + expect(entry.validity).to.equalBytes(uint8ArrayFromString('2033-05-18T03:33:20.000000000Z')) }) it('should create an ipns record and validate it correctly', async () => { @@ -129,8 +129,8 @@ describe('ipns', function () { const marshalledData = ipns.marshal(entryDataCreated) const unmarshalledData = ipns.unmarshal(marshalledData) - expect(entryDataCreated.value).to.equal(unmarshalledData.value.toString()) - expect(entryDataCreated.validity).to.equal(unmarshalledData.validity.toString()) + expect(entryDataCreated.value).to.equalBytes(unmarshalledData.value) + expect(entryDataCreated.validity).to.equalBytes(unmarshalledData.validity) expect(entryDataCreated.validityType).to.equal(unmarshalledData.validityType) expect(entryDataCreated.signature).to.equalBytes(unmarshalledData.signature) expect(entryDataCreated.sequence).to.equal(unmarshalledData.sequence) @@ -166,7 +166,7 @@ describe('ipns', function () { keys.forEach(key => { const { routingKey } = ipns.getIdKeys(fromB58String(key)) - const id = toB58String(routingKey.toBuffer().slice(ipns.namespaceLength)) + const id = toB58String(routingKey.uint8Array().subarray(ipns.namespaceLength)) expect(id).to.equal(key) }) @@ -206,7 +206,7 @@ describe('ipns', function () { const entry = await ipns.create(rsa, cid, sequence, validity) const marshalledData = ipns.marshal(entry) - const key = Buffer.from(`/ipns/${ipfsId.id}`) + const key = uint8ArrayFromString(`/ipns/${ipfsId.id}`) try { await ipns.validator.validate(marshalledData, key) @@ -235,7 +235,7 @@ describe('ipns', function () { await ipns.embedPublicKey(rsa.public, entry) const marshalledData = ipns.marshal(entry) - const key = Buffer.from(`/ipns/${ipfsId.id}`) + const key = uint8ArrayFromString(`/ipns/${ipfsId.id}`) const valid = await ipns.validator.validate(marshalledData, key) expect(valid).to.equal(true) @@ -249,9 +249,9 @@ describe('ipns', function () { await ipns.embedPublicKey(rsa.public, entry) // corrupt the record by changing the value to random bytes - entry.value = crypto.randomBytes(46).toString() + entry.value = crypto.randomBytes(46) const marshalledData = ipns.marshal(entry) - const key = Buffer.from(`/ipns/${ipfsId.id}`) + const key = uint8ArrayFromString(`/ipns/${ipfsId.id}`) try { await ipns.validator.validate(marshalledData, key)