Skip to content

Commit

Permalink
feat!: opt-in V2-only records, IPIP-428 verification (#234)
Browse files Browse the repository at this point in the history
Adds a `v1Compatible` option when creating IPNS records that will cause the V1 signature
to be added to the record.  This option defaults to true, in the future it will be changed to
false.

The value types have also been updated to make it harder to create invalid records.  It now
accepts only CIDs, PeerIds, or arbitrary path strings prefixed with `"/"`.

BREAKING CHANGE: all /ipns/* keys are now encoded as base36 encoded CIDv1 libp2p-cid

Closes #217

---------

Co-authored-by: Marcin Rataj <lidel@lidel.org>
Co-authored-by: Alex Potsides <alex@achingbrain.net>
  • Loading branch information
3 people authored Sep 15, 2023
1 parent b510da8 commit df71fed
Show file tree
Hide file tree
Showing 19 changed files with 776 additions and 321 deletions.
63 changes: 26 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[![codecov](https://img.shields.io/codecov/c/github/ipfs/js-ipns.svg?style=flat-square)](https://codecov.io/gh/ipfs/js-ipns)
[![CI](https://img.shields.io/github/actions/workflow/status/ipfs/js-ipns/js-test-and-release.yml?branch=master\&style=flat-square)](https://github.com/ipfs/js-ipns/actions/workflows/js-test-and-release.yml?query=branch%3Amaster)

> ipns record definitions
> IPNS Record definitions
## Table of contents <!-- omit in toc -->

Expand Down Expand Up @@ -54,15 +54,15 @@ This module contains all the necessary code for creating, understanding and vali
```js
import * as ipns from 'ipns'

const entryData = await ipns.create(privateKey, value, sequenceNumber, lifetime)
const ipnsRecord = await ipns.create(privateKey, value, sequenceNumber, lifetime)
```

### Validate record

```js
import * as ipns from 'ipns'

await ipns.validate(publicKey, ipnsEntry)
await ipns.validate(publicKey, marshalledData)
// if no error thrown, the record is valid
```

Expand All @@ -71,15 +71,15 @@ await ipns.validate(publicKey, ipnsEntry)
```js
import * as ipns from 'ipns'

const ipnsEntryWithEmbedPublicKey = await ipns.embedPublicKey(publicKey, ipnsEntry)
const ipnsRecordWithEmbeddedPublicKey = await ipns.embedPublicKey(publicKey, ipnsRecord)
```

### Extract public key from record

```js
import * as ipns from 'ipns'

const publicKey = await ipns.extractPublicKey(peerId, ipnsEntry)
const publicKey = await ipns.extractPublicKey(peerId, ipnsRecord)
```

### Datastore key
Expand All @@ -90,7 +90,7 @@ import * as ipns from 'ipns'
ipns.getLocalKey(peerId)
```

Returns a key to be used for storing the ipns entry locally, that is:
Returns a key to be used for storing the IPNS record locally, that is:

```
/ipns/${base32(<HASH>)}
Expand All @@ -101,23 +101,23 @@ Returns a key to be used for storing the ipns entry locally, that is:
```js
import * as ipns from 'ipns'

const entryData = await ipns.create(privateKey, value, sequenceNumber, lifetime)
const ipnsRecord = await ipns.create(privateKey, value, sequenceNumber, lifetime)
// ...
const marshalledData = ipns.marshal(entryData)
const marshalledData = ipns.marshal(ipnsRecord)
// ...
```

Returns the entry data serialized.
Returns the record data serialized.

### Unmarshal data from proto buffer

```js
import * as ipns from 'ipns'

const data = ipns.unmarshal(storedData)
const ipnsRecord = ipns.unmarshal(storedData)
```

Returns the entry data structure after being serialized.
Returns the `IPNSRecord` after being deserialized.

### Validator

Expand All @@ -131,81 +131,70 @@ Contains an object with `validate (marshalledData, key)` and `select (dataA, dat

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 (`signatureV2` of CBOR `data` is 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`.
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`.

## API

### Create record

```js

ipns.create(privateKey, value, sequenceNumber, lifetime)
ipns.create(privateKey, value, sequenceNumber, lifetime, options)
```

Create an IPNS record for being stored in a protocol buffer.

- `privateKey` (`PrivKey` [RSA Instance](https://github.com/libp2p/js-libp2p-crypto/blob/master/src/keys/rsa-class.js)): key to be used for cryptographic operations.
- `value` (Uint8Array): ipfs path of the object to be published.
- `value` (string): IPFS path of the object to be published.
- `sequenceNumber` (Number): number representing the current version of the record.
- `lifetime` (Number): lifetime of the record (in milliseconds).
- `options` (CreateOptions): additional creation options.

Returns a `Promise` that resolves to an object with the entry's properties eg:

```js
{
value: Uint8Array,
signature: Uint8Array, // V1 (legacy, ignored)
validityType: 0,
validity: Uint8Array,
sequence: 2,
signatureV2: Uint8Array, // V2 signature of data field
data: Uint8Array // DAG-CBOR that was signed
}
```
Returns a `Promise` that resolves to an object with a `IPNSRecord`.

### Validate record

```js
ipns.validate(publicKey, ipnsEntry)
ipns.validate(publicKey, ipnsRecord)
```

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).
- `ipnsRecord` (`IPNSRecord`): IPNS record (obtained using the create function).

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

### Marshal data with proto buffer

```js
const marshalledData = ipns.marshal(entryData)
const marshalledData = ipns.marshal(ipnsRecord)
```

Returns the entry data serialized.
Returns the serialized IPNS record.

- `entryData` (Object): ipns entry record (obtained using the create function).
- `ipnsRecord` (`IPNSRecord`): ipns record (obtained using the create function).

### Unmarshal data from proto buffer

```js
const data = ipns.unmarshal(storedData)
```

Returns the entry data structure after being serialized.
Returns a `IPNSRecord` after being serialized.

- `storedData` (Uint8Array): ipns entry record serialized.
- `storedData` (Uint8Array): ipns record serialized.

### Extract public key from record

```js
const publicKey = await ipns.extractPublicKey(peerId, ipnsEntry)
const publicKey = await ipns.extractPublicKey(peerId, ipnsRecord)
```

Extract a public key from an IPNS entry.
Extract a public key from an IPNS record.

- `peerId` (`PeerId` [Instance](https://github.com/libp2p/js-libp2p-peer-id/tree/master/packages/libp2p-peer-id)): peer identifier object.
- `ipnsEntry` (Object): ipns entry record (obtained using the create function).
- `ipnsRecord` (`IPNSRecord`): ipns record (obtained using the create function).

Returns a `Promise` which resolves to public key ([`PublicKey`](https://github.com/libp2p/js-libp2p-interfaces/blob/master/packages/interface-keys/src/index.ts) ): may be used for cryptographic operations.

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "ipns",
"version": "6.0.7",
"description": "ipns record definitions",
"description": "IPNS record definitions",
"author": "Vasco Santos <vasco.santos@moxy.studio>",
"license": "Apache-2.0 OR MIT",
"homepage": "https://github.com/ipfs/js-ipns#readme",
Expand Down
1 change: 1 addition & 0 deletions src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const ERR_PEER_ID_FROM_PUBLIC_KEY = 'ERR_PEER_ID_FROM_PUBLIC_KEY'
export const ERR_PUBLIC_KEY_FROM_ID = 'ERR_PUBLIC_KEY_FROM_ID'
export const ERR_UNDEFINED_PARAMETER = 'ERR_UNDEFINED_PARAMETER'
export const ERR_INVALID_RECORD_DATA = 'ERR_INVALID_RECORD_DATA'
export const ERR_INVALID_VALUE = 'ERR_INVALID_VALUE'
export const ERR_INVALID_EMBEDDED_KEY = 'ERR_INVALID_EMBEDDED_KEY'
export const ERR_MISSING_PRIVATE_KEY = 'ERR_MISSING_PRIVATE_KEY'
export const ERR_RECORD_TOO_LARGE = 'ERR_RECORD_TOO_LARGE'
Loading

0 comments on commit df71fed

Please sign in to comment.