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

feat: ed25519 keys by default #3693

Merged
merged 5 commits into from
Aug 11, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
34 changes: 31 additions & 3 deletions packages/interface-ipfs-core/src/key/gen.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

const { nanoid } = require('nanoid')
const { getDescribe, getIt, expect } = require('../utils/mocha')
const { keys: { supportedKeys, import: importKey } } = require('libp2p-crypto')

/**
* @typedef {import('ipfsd-ctl').Factory} Factory
Expand All @@ -18,7 +19,18 @@ module.exports = (factory, options) => {

describe('.key.gen', () => {
const keyTypes = [
{ type: 'rsa', size: 2048 }
{
opts: { type: 'rsa', size: 2048 },
expectedType: supportedKeys.rsa.RsaPrivateKey
},
{
opts: { type: 'ed25519' },
expectedType: supportedKeys.ed25519.Ed25519PrivateKey
},
{
opts: { },
expectedType: supportedKeys.ed25519.Ed25519PrivateKey
}
]

/** @type {import('ipfs-core-types').IPFS} */
Expand All @@ -31,14 +43,30 @@ module.exports = (factory, options) => {
after(() => factory.clean())

keyTypes.forEach((kt) => {
it(`should generate a new ${kt.type} key`, async function () {
it(`should generate a new ${kt.opts.type || 'default'} key`, async function () {
// @ts-ignore this is mocha
this.timeout(20 * 1000)
const name = nanoid()
const key = await ipfs.key.gen(name, kt)
const key = await ipfs.key.gen(name, kt.opts)
expect(key).to.exist()
expect(key).to.have.property('name', name)
expect(key).to.have.property('id')

try {
const password = nanoid() + '-' + nanoid()
const exported = await ipfs.key.export(name, password)
const imported = await importKey(exported, password)

expect(imported).to.be.an.instanceOf(kt.expectedType)
} catch (err) {
if (err.code === 'ERR_NOT_IMPLEMENTED') {
// key export is not exposed over the HTTP API
// @ts-ignore this is mocha
this.skip('Cannot verify key type')
}

throw err
}
})
})
})
Expand Down
18 changes: 13 additions & 5 deletions packages/ipfs-cli/src/commands/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ const fs = require('fs')
const debug = require('debug')('ipfs:cli:init')
const { ipfsPathHelp } = require('../utils')

/** @type {Record<string, import('libp2p-crypto').KeyType>} */
const keyTypes = {
ed25519: 'Ed25519',
rsa: 'RSA',
secp256k1: 'secp256k1'
}

module.exports = {
command: 'init [default-config] [options]',
describe: 'Initialize a local IPFS node\n\n' +
Expand All @@ -23,15 +30,16 @@ module.exports = {
})
.option('algorithm', {
type: 'string',
choices: Object.keys(keyTypes),
alias: 'a',
default: 'RSA',
describe: 'Cryptographic algorithm to use for key generation. Supports [RSA, Ed25519, secp256k1]'
default: 'ed25519',
describe: 'Cryptographic algorithm to use for key generation'
})
.option('bits', {
type: 'number',
alias: 'b',
default: '2048',
describe: 'Number of bits to use in the generated RSA private key (defaults to 2048)',
describe: 'Number of bits to use if the generated private key is RSA (defaults to 2048)',
coerce: Number
})
.option('empty-repo', {
Expand All @@ -58,7 +66,7 @@ module.exports = {
* @param {object} argv
* @param {import('../types').Context} argv.ctx
* @param {string} argv.defaultConfig
* @param {'RSA' | 'Ed25519' | 'secp256k1'} argv.algorithm
* @param {'rsa' | 'ed25519' | 'secp256k1'} argv.algorithm
* @param {number} argv.bits
* @param {boolean} argv.emptyRepo
* @param {string} argv.privateKey
Expand Down Expand Up @@ -88,7 +96,7 @@ module.exports = {
await IPFS.create({
repo: repoPath,
init: {
algorithm: argv.algorithm,
algorithm: keyTypes[argv.algorithm],
bits: argv.bits,
privateKey: argv.privateKey,
emptyRepo: argv.emptyRepo,
Expand Down
5 changes: 3 additions & 2 deletions packages/ipfs-cli/src/commands/key/gen.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ module.exports = {
builder: {
type: {
alias: 't',
describe: 'type of the key to create [rsa, ed25519].',
default: 'rsa'
describe: 'type of the key to create',
choices: ['rsa', 'ed25519'],
default: 'ed25519'
},
size: {
alias: 's',
Expand Down
10 changes: 5 additions & 5 deletions packages/ipfs-cli/test/key.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('key', () => {
const name = 'key-name'
const id = 'key-id'
const defaultOptions = {
type: 'rsa',
type: 'ed25519',
size: 2048,
timeout: undefined
}
Expand All @@ -43,28 +43,28 @@ describe('key', () => {
it('gen with args', async () => {
ipfs.key.gen.withArgs(name, {
...defaultOptions,
type: 'rsb',
type: 'rsa',
size: 7
}).resolves({
id,
name
})

const out = await cli(`key gen ${name} --type rsb --size 7`, { ipfs })
const out = await cli(`key gen ${name} --type rsa --size 7`, { ipfs })
expect(out).to.equal(`generated ${id} ${name}\n`)
})

it('gen with short args', async () => {
ipfs.key.gen.withArgs(name, {
...defaultOptions,
type: 'rsc',
type: 'rsa',
size: 5
}).resolves({
id,
name
})

const out = await cli(`key gen ${name} -t rsc -s 5`, { ipfs })
const out = await cli(`key gen ${name} -t rsa -s 5`, { ipfs })
expect(out).to.equal(`generated ${id} ${name}\n`)
})

Expand Down
2 changes: 1 addition & 1 deletion packages/ipfs-core/src/components/key/gen.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const withTimeoutOption = require('ipfs-core-utils/src/with-timeout-option')

const DEFAULT_KEY_TYPE = 'rsa'
const DEFAULT_KEY_TYPE = 'ed25519'
const DEFAULT_KEY_SIZE = 2048

/**
Expand Down
7 changes: 7 additions & 0 deletions packages/ipfs-core/src/components/libp2p.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,15 @@ const { Multiaddr } = require('multiaddr')
const pkgversion = require('../../package.json').version

/**
* @typedef {object} DekOptions
* @property {string} hash
* @property {string} salt
* @property {number} iterationCount
* @property {number} keyLength
*
* @typedef {Object} KeychainConfig
* @property {string} [pass]
* @property {DekOptions} [dek]
*
* @typedef {import('ipfs-repo').IPFSRepo} Repo
* @typedef {import('peer-id')} PeerId
Expand Down
22 changes: 20 additions & 2 deletions packages/ipfs-core/src/components/resolve.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

const isIpfs = require('is-ipfs')
const { CID } = require('multiformats/cid')
const PeerID = require('peer-id')
const withTimeoutOption = require('ipfs-core-utils/src/with-timeout-option')
const { resolve: res } = require('../utils')

Expand All @@ -28,14 +29,18 @@ module.exports = ({ repo, codecs, bases, name }) => {
}

const [, schema, hash, ...rest] = path.split('/') // ['', 'ipfs', 'hash', ...path]
const cid = CID.parse(hash)
const base = opts.cidBase ? await bases.getBase(opts.cidBase) : undefined
const bytes = parseBytes(hash)

// nothing to resolve return the input
if (rest.length === 0) {
return `/${schema}/${cid.toString(base && base.encoder)}`
const str = base ? base.encoder.encode(bytes) : hash

return `/${schema}/${str}`
}

const cid = CID.decode(bytes)

path = rest.join('/')

const results = res(cid, path, codecs, repo, opts)
Expand All @@ -54,3 +59,16 @@ module.exports = ({ repo, codecs, bases, name }) => {

return withTimeoutOption(resolve)
}

/**
* Parse the input as a PeerID or a CID or throw an error
*
* @param {string} str
*/
function parseBytes (str) {
try {
return PeerID.parse(str).toBytes()
} catch {
return CID.parse(str).bytes
}
}
25 changes: 18 additions & 7 deletions packages/ipfs-core/src/components/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,23 +130,34 @@ const initRepo = async (print, repo, options) => {

log('repo opened')

/** @type {import('./libp2p').KeychainConfig} */
const keychainConfig = {
pass: options.pass
}

try {
keychainConfig.dek = await repo.config.get('Keychain.DEK')
} catch (err) {
if (err.code !== 'ERR_NOT_FOUND') {
throw err
}
}

// Create libp2p for Keychain creation
const libp2p = await createLibP2P({
options: undefined,
multiaddrs: undefined,
peerId,
repo,
config,
keychainConfig: {
pass: options.pass
}
keychainConfig
})

if (libp2p.keychain && libp2p.keychain.opts) {
await libp2p.loadKeychain()

await repo.config.set('Keychain', {
dek: libp2p.keychain.opts.dek
DEK: libp2p.keychain.opts.dek
})
}

Expand All @@ -172,13 +183,13 @@ const decodePeerId = (peerId) => {
*
* @param {Print} print
* @param {Object} options
* @param {KeyType} [options.algorithm='RSA']
* @param {KeyType} [options.algorithm='Ed25519']
* @param {number} [options.bits=2048]
* @returns {Promise<PeerId>}
*/
const initPeerId = (print, { algorithm = 'RSA', bits = 2048 }) => {
const initPeerId = (print, { algorithm = 'Ed25519', bits = 2048 }) => {
// Generate peer identity keypair + transform to desired format + add to config.
print('generating %s-bit (rsa only) %s keypair...', bits, algorithm)
print('generating %s keypair...', algorithm)
return PeerId.create({ keyType: algorithm, bits })
}

Expand Down
2 changes: 1 addition & 1 deletion packages/ipfs-core/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export interface Options {

/**
* Occasionally a repo migration is necessary - pass true here to to this automatically at startup
* when a new version of IPFS is being run for the first time and a migration is necssary, otherwise
* when a new version of IPFS is being run for the first time and a migration is necessary, otherwise
* the node will refuse to start
*/
repoAutoMigrate?: boolean
Expand Down
6 changes: 5 additions & 1 deletion packages/ipfs-core/test/create-node.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ describe('create node', function () {
it('should throw on boot error', () => {
return expect(IPFS.create({
repo: tempRepo,
init: { bits: 256 }, // Too few bits will cause error on boot
init: {
algorithm: 'RSA',
bits: 256
}, // Too few bits will cause error on boot
config: { Addresses: { Swarm: [] } }
})).to.eventually.be.rejected()
})
Expand All @@ -109,6 +112,7 @@ describe('create node', function () {
const node = await IPFS.create({
repo: tempRepo,
init: {
algorithm: 'RSA',
bits: 1024
},
config: {
Expand Down
Loading