Skip to content
This repository has been archived by the owner on Feb 12, 2024. It is now read-only.

Commit

Permalink
fix: support keychain without pass (#3212)
Browse files Browse the repository at this point in the history
- add support for ed25519 and secp256k1 keys for the ipfs PeerId
- add support for using ed25519 and secp256k1 keys with ipns
- add support for keychain without a pass, this fixes several keychain commands
- fix `name publish`, ttl should be optional but wasn't being allowed

Includes changes in #3208

Key gen and key listing now works:
```sh
$ jsipfs key gen --type=ed25519 my-ed-key
generated QmUPJZ3ghsnkRVgYjrMrSC8MbbZTzcezn6mH7vY9vTbrMY my-ed-key

$ jsipfs key list
QmUPJZ3ghsnkRVgYjrMrSC8MbbZTzcezn6mH7vY9vTbrMY my-ed-key
QmYPxJJb8Mpa6JjQj7hCTF4ajn2SE1HFLRoAnCbymTGzyC self
```

IPNS Publishing now works properly, including using other key types:
```sh
jsipfs name publish -k my-ed-key /ipfs/QmP7WDyEdkFu2nfj35SUEB7fk3iKCHpcrCVbGk1HXZU22a
Published to 12D3KooWGmSq6u3yZeXqeqSmQpJWKQGGdCxrZQAUZBzsbSM2kCE6: /ipfs/QmP7WDyEdkFu2nfj35SUEB7fk3iKCHpcrCVbGk1HXZU22a
```

I also fixed a couple of the example tests, they weren't waiting for IPFS to start before executing the tests, which could cause them to intermittently fail.

BREAKING CHANGE: remove support for key.export over the http api
  • Loading branch information
jacobheun committed Aug 10, 2020
1 parent b1a2713 commit 7e0e85c
Show file tree
Hide file tree
Showing 26 changed files with 150 additions and 198 deletions.
2 changes: 1 addition & 1 deletion examples/browser-browserify/public/index.html
Expand Up @@ -15,7 +15,7 @@
<body>
<h1>JS IPFS - Add data to IPFS from the browser</h1>
<textarea id="source" placeholder="Enter some text here"></textarea>
<button id="store">Add text to ipfs</button>
<button id="store" style="display: none">Add text to ipfs</button>
<div id="output" style="display: none">
<div>found in ipfs:</div>
<div class="content" id="cid">[ipfs cid]</div>
Expand Down
10 changes: 8 additions & 2 deletions examples/browser-browserify/src/index.js
Expand Up @@ -3,7 +3,12 @@
const IPFS = require('ipfs')

document.addEventListener('DOMContentLoaded', async () => {
const node = await IPFS.create({ repo: String(Math.random() + Date.now()) })
const node = await IPFS.create({
repo: String(Math.random() + Date.now()),
init: { alogorithm: 'ed25519' }

})
const button = document.getElementById('store')

console.log('IPFS node is ready')

Expand All @@ -25,5 +30,6 @@ document.addEventListener('DOMContentLoaded', async () => {
}
}

document.getElementById('store').onclick = store
button.onclick = store
button.setAttribute('style', 'display: inline')
})
3 changes: 1 addition & 2 deletions examples/browser-browserify/test.js
Expand Up @@ -9,9 +9,8 @@ module.exports = {
.waitForElementVisible('#source')
.setValue('#source', 'hello')
.waitForElementVisible('#store')
.pause(1000)
.click('#store')
.waitForElementVisible('#output')
.waitForElementVisible('#output', 5e3, 100)

browser.expect.element('#cid').text.to.contain('QmWfVY9y3xjsixTgbd9AorQxH7VtMpzfx2HaWtsoUYecaX')
browser.expect.element('#content').text.to.contain('hello')
Expand Down
2 changes: 1 addition & 1 deletion examples/circuit-relaying/index.html
Expand Up @@ -46,7 +46,7 @@ <h1>IPFS Simple Messaging</h1>
<div class="row">
<div class="box row">
<label>Peer id:</label>
<ul id="peer-id"></ul>
<ul id="peer-id" style="display:none"></ul>
</div>
<div class="box addrs-box">
<label>Addresses:</label>
Expand Down
1 change: 1 addition & 0 deletions examples/circuit-relaying/src/app.js
Expand Up @@ -54,6 +54,7 @@ document.addEventListener('DOMContentLoaded', async () => {

let room = createRoom(roomName)

$peerId.setAttribute('style', '')
$peerId.innerHTML = `<li>${info.id}</li>`

$send.addEventListener('click', () => {
Expand Down
1 change: 0 additions & 1 deletion examples/circuit-relaying/src/helpers.js
Expand Up @@ -7,7 +7,6 @@ const $msgs = document.querySelector('#msgs')
const $addrs = document.querySelector('#addrs')
const $peers = document.querySelector('#peers')
const $pAddrs = document.querySelector('#peers-addrs')
const delay = require('delay')

const NAMESPACE = 'ipfs-quick-msg'

Expand Down
4 changes: 2 additions & 2 deletions examples/circuit-relaying/test.js
Expand Up @@ -91,10 +91,10 @@ module.exports[pkg.name] = function (browser) {

browser
.url(process.env.IPFS_EXAMPLE_TEST_URL)
.waitForElementVisible('#peer')
.waitForElementVisible('#peer-id') // Wait for ipfs to start
.clearValue('#peer')
.setValue('#peer', process.env.IPFS_RELAY_ADDRESS)
.pause(1000)
.pause(100)
.click('#connect')

browser.expect.element('#peers-addrs').text.to.contain(process.env.IPFS_RELAY_ID)
Expand Down
1 change: 1 addition & 0 deletions packages/interface-ipfs-core/package.json
Expand Up @@ -52,6 +52,7 @@
"it-drain": "^1.0.1",
"it-last": "^1.0.1",
"it-pushable": "^1.3.1",
"libp2p-crypto": "^0.17.9",
"multiaddr": "^7.4.3",
"multibase": "^1.0.1",
"multihashing-async": "^1.0.0",
Expand Down
37 changes: 0 additions & 37 deletions packages/interface-ipfs-core/src/key/export.js

This file was deleted.

19 changes: 10 additions & 9 deletions packages/interface-ipfs-core/src/key/import.js
Expand Up @@ -2,6 +2,7 @@
'use strict'

const { nanoid } = require('nanoid')
const keys = require('libp2p-crypto/src/keys')
const { getDescribe, getIt, expect } = require('../utils/mocha')
const testTimeout = require('../utils/test-timeout')

Expand All @@ -26,24 +27,24 @@ module.exports = (common, options) => {
it('should respect timeout option when importing a key', async () => {
const password = nanoid()

const pem = await ipfs.key.export('self', password)
expect(pem).to.exist()
const key = await keys.generateKeyPair('ed25519')
const exported = key.export(password)

await testTimeout(() => ipfs.key.import('derp', pem, password, {
await testTimeout(() => ipfs.key.import('derp', exported, password, {
timeout: 1
}))
})

it('should import an exported key', async () => {
const password = nanoid()

const pem = await ipfs.key.export('self', password)
expect(pem).to.exist()
const key = await keys.generateKeyPair('ed25519')
const exported = await key.export(password)

const key = await ipfs.key.import('clone', pem, password)
expect(key).to.exist()
expect(key).to.have.property('name', 'clone')
expect(key).to.have.property('id')
const importedKey = await ipfs.key.import('clone', exported, password)
expect(importedKey).to.exist()
expect(importedKey).to.have.property('name', 'clone')
expect(importedKey).to.have.property('id')
})
})
}
1 change: 0 additions & 1 deletion packages/interface-ipfs-core/src/key/index.js
Expand Up @@ -6,7 +6,6 @@ const tests = {
list: require('./list'),
rename: require('./rename'),
rm: require('./rm'),
export: require('./export'),
import: require('./import')
}

Expand Down
26 changes: 0 additions & 26 deletions packages/ipfs-http-client/src/key/export.js

This file was deleted.

1 change: 0 additions & 1 deletion packages/ipfs-http-client/src/key/index.js
Expand Up @@ -5,6 +5,5 @@ module.exports = config => ({
list: require('./list')(config),
rename: require('./rename')(config),
rm: require('./rm')(config),
export: require('./export')(config),
import: require('./import')(config)
})
3 changes: 2 additions & 1 deletion packages/ipfs/docs/MODULE.md
Expand Up @@ -94,7 +94,8 @@ Note that *initializing* a repo is different from creating an instance of [`ipfs
Instead of a boolean, you may provide an object with custom initialization options. All properties are optional:

- `emptyRepo` (boolean) Whether to remove built-in assets, like the instructional tour and empty mutable file system, from the repo. (Default: `false`)
- `bits` (number) Number of bits to use in the generated key pair. (Default: `2048`)
- `algorithm` (string) The type of key to use. Supports `rsa`, `ed25519`, `secp256k1`. (Default: `rsa`)
- `bits` (number) Number of bits to use in the generated key pair (rsa only). (Default: `2048`)
- `privateKey` (string/PeerId) A pre-generated private key to use. Can be either a base64 string or a [PeerId](https://github.com/libp2p/js-peer-id) instance. **NOTE: This overrides `bits`.**
```js
// Generating a Peer ID:
Expand Down
4 changes: 2 additions & 2 deletions packages/ipfs/package.json
Expand Up @@ -129,9 +129,9 @@
"iterable-ndjson": "^1.1.0",
"jsondiffpatch": "^0.4.1",
"just-safe-set": "^2.1.0",
"libp2p": "^0.28.5",
"libp2p": "^0.28.10",
"libp2p-bootstrap": "^0.11.0",
"libp2p-crypto": "^0.17.8",
"libp2p-crypto": "^0.17.9",
"libp2p-delegated-content-routing": "^0.5.0",
"libp2p-delegated-peer-routing": "^0.5.0",
"libp2p-floodsub": "^0.21.0",
Expand Down
7 changes: 7 additions & 0 deletions packages/ipfs/src/cli/commands/init.js
Expand Up @@ -17,6 +17,12 @@ module.exports = {
describe: 'Node config, this should be a path to a file or JSON and will be merged with the default config. See https://github.com/ipfs/js-ipfs#optionsconfig',
type: 'string'
})
.option('algorithm', {
type: 'string',
alias: 'a',
default: 'rsa',
describe: 'Cryptographic algorithm to use for key generation. Supports [rsa, ed25519, secp256k1]'
})
.option('bits', {
type: 'number',
alias: 'b',
Expand Down Expand Up @@ -72,6 +78,7 @@ module.exports = {

try {
await node.init({
algorithm: argv.algorithm,
bits: argv.bits,
privateKey: argv.privateKey,
emptyRepo: argv.emptyRepo,
Expand Down
10 changes: 5 additions & 5 deletions packages/ipfs/src/core/components/init.js
Expand Up @@ -174,7 +174,7 @@ module.exports = ({
return apiManager.api
}

async function initNewRepo (repo, { privateKey, emptyRepo, bits, profiles, config, pass, print }) {
async function initNewRepo (repo, { privateKey, emptyRepo, algorithm, bits, profiles, config, pass, print }) {
emptyRepo = emptyRepo || false
bits = bits == null ? 2048 : Number(bits)

Expand All @@ -188,7 +188,7 @@ async function initNewRepo (repo, { privateKey, emptyRepo, bits, profiles, confi
throw new Error('repo already exists')
}

const peerId = await createPeerId({ privateKey, bits, print })
const peerId = await createPeerId({ privateKey, algorithm, bits, print })

log('identity generated')

Expand Down Expand Up @@ -257,16 +257,16 @@ async function initExistingRepo (repo, { config: newConfig, profiles, pass }) {
return { peerId, keychain: libp2p.keychain }
}

function createPeerId ({ privateKey, bits, print }) {
function createPeerId ({ privateKey, algorithm = 'rsa', bits, print }) {
if (privateKey) {
log('using user-supplied private-key')
return typeof privateKey === 'object'
? privateKey
: PeerId.createFromPrivKey(Buffer.from(privateKey, 'base64'))
} else {
// Generate peer identity keypair + transform to desired format + add to config.
print('generating %s-bit RSA keypair...', bits)
return PeerId.create({ bits })
print('generating %s-bit (rsa only) %s keypair...', bits, algorithm)
return PeerId.create({ keyType: algorithm, bits })
}
}

Expand Down
8 changes: 4 additions & 4 deletions packages/ipfs/src/core/components/name/publish.js
Expand Up @@ -21,16 +21,16 @@ const { resolvePath } = require('./utils')
* @param {IPFS} self
* @returns {Object}
*/
module.exports = ({ ipns, dag, peerId, isOnline, keychain, options: constructorOptions }) => {
module.exports = ({ ipns, dag, peerId, isOnline, keychain }) => {
const lookupKey = async keyName => {
if (keyName === 'self') {
return peerId.privKey
}

try {
const pass = constructorOptions.pass
const pem = await keychain.exportKey(keyName, pass)
const privateKey = await crypto.keys.import(pem, pass)
// We're exporting and immediately importing the key, so we can just use a throw away password
const pem = await keychain.exportKey(keyName, 'temp')
const privateKey = await crypto.keys.import(pem, 'temp')
return privateKey
} catch (err) {
log.error(err)
Expand Down
44 changes: 0 additions & 44 deletions packages/ipfs/src/http/api/resources/key.js
Expand Up @@ -181,50 +181,6 @@ exports.gen = {
}
}

exports.export = {
options: {
validate: {
options: {
allowUnknown: true,
stripUnknown: true
},
query: Joi.object().keys({
name: Joi.string().required(),
password: Joi.string().required(),
timeout: Joi.timeout()
})
.rename('arg', 'name', {
override: true,
ignoreUndefined: true
})
}
},
handler: async (request, h) => {
const {
app: {
signal
},
server: {
app: {
ipfs
}
},
query: {
name,
password,
timeout
}
} = request

const pem = await ipfs.key.export(name, password, {
signal,
timeout
})

return h.response(pem).type('application/x-pem-file')
}
}

exports.import = {
options: {
validate: {
Expand Down
2 changes: 1 addition & 1 deletion packages/ipfs/src/http/api/resources/name.js
Expand Up @@ -80,7 +80,7 @@ exports.publish = {
name: Joi.string().required(),
resolve: Joi.boolean().default(true),
lifetime: Joi.string().default('24h'),
ttl: Joi.string(),
ttl: Joi.string().allow(''),
key: Joi.string().default('self'),
allowOffline: Joi.boolean(),
timeout: Joi.timeout()
Expand Down
5 changes: 0 additions & 5 deletions packages/ipfs/src/http/api/routes/key.js
Expand Up @@ -23,11 +23,6 @@ module.exports = [
path: '/api/v0/key/rename',
...resources.key.rename
},
{
method: 'POST',
path: '/api/v0/key/export',
...resources.key.export
},
{
method: 'POST',
path: '/api/v0/key/import',
Expand Down

0 comments on commit 7e0e85c

Please sign in to comment.