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

Commit 33fa734

Browse files
lidelachingbrain
andauthored
feat: ed25519 keys by default (#3693)
Switch from RSA to ed25519 to match what we already do in go-ipfs. Closes #3591 Co-authored-by: achingbrain <[email protected]>
1 parent 2886134 commit 33fa734

File tree

15 files changed

+174
-48
lines changed

15 files changed

+174
-48
lines changed

packages/interface-ipfs-core/src/key/gen.js

+31-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

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

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

1920
describe('.key.gen', () => {
2021
const keyTypes = [
21-
{ type: 'rsa', size: 2048 }
22+
{
23+
opts: { type: 'rsa', size: 2048 },
24+
expectedType: supportedKeys.rsa.RsaPrivateKey
25+
},
26+
{
27+
opts: { type: 'ed25519' },
28+
expectedType: supportedKeys.ed25519.Ed25519PrivateKey
29+
},
30+
{
31+
opts: { },
32+
expectedType: supportedKeys.ed25519.Ed25519PrivateKey
33+
}
2234
]
2335

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

3345
keyTypes.forEach((kt) => {
34-
it(`should generate a new ${kt.type} key`, async function () {
46+
it(`should generate a new ${kt.opts.type || 'default'} key`, async function () {
3547
// @ts-ignore this is mocha
3648
this.timeout(20 * 1000)
3749
const name = nanoid()
38-
const key = await ipfs.key.gen(name, kt)
50+
const key = await ipfs.key.gen(name, kt.opts)
3951
expect(key).to.exist()
4052
expect(key).to.have.property('name', name)
4153
expect(key).to.have.property('id')
54+
55+
try {
56+
const password = nanoid() + '-' + nanoid()
57+
const exported = await ipfs.key.export(name, password)
58+
const imported = await importKey(exported, password)
59+
60+
expect(imported).to.be.an.instanceOf(kt.expectedType)
61+
} catch (err) {
62+
if (err.code === 'ERR_NOT_IMPLEMENTED') {
63+
// key export is not exposed over the HTTP API
64+
// @ts-ignore this is mocha
65+
this.skip('Cannot verify key type')
66+
}
67+
68+
throw err
69+
}
4270
})
4371
})
4472
})

packages/ipfs-cli/src/commands/init.js

+13-5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ const fs = require('fs')
44
const debug = require('debug')('ipfs:cli:init')
55
const { ipfsPathHelp } = require('../utils')
66

7+
/** @type {Record<string, import('libp2p-crypto').KeyType>} */
8+
const keyTypes = {
9+
ed25519: 'Ed25519',
10+
rsa: 'RSA',
11+
secp256k1: 'secp256k1'
12+
}
13+
714
module.exports = {
815
command: 'init [default-config] [options]',
916
describe: 'Initialize a local IPFS node\n\n' +
@@ -23,15 +30,16 @@ module.exports = {
2330
})
2431
.option('algorithm', {
2532
type: 'string',
33+
choices: Object.keys(keyTypes),
2634
alias: 'a',
27-
default: 'RSA',
28-
describe: 'Cryptographic algorithm to use for key generation. Supports [RSA, Ed25519, secp256k1]'
35+
default: 'ed25519',
36+
describe: 'Cryptographic algorithm to use for key generation'
2937
})
3038
.option('bits', {
3139
type: 'number',
3240
alias: 'b',
3341
default: '2048',
34-
describe: 'Number of bits to use in the generated RSA private key (defaults to 2048)',
42+
describe: 'Number of bits to use if the generated private key is RSA (defaults to 2048)',
3543
coerce: Number
3644
})
3745
.option('empty-repo', {
@@ -58,7 +66,7 @@ module.exports = {
5866
* @param {object} argv
5967
* @param {import('../types').Context} argv.ctx
6068
* @param {string} argv.defaultConfig
61-
* @param {'RSA' | 'Ed25519' | 'secp256k1'} argv.algorithm
69+
* @param {'rsa' | 'ed25519' | 'secp256k1'} argv.algorithm
6270
* @param {number} argv.bits
6371
* @param {boolean} argv.emptyRepo
6472
* @param {string} argv.privateKey
@@ -88,7 +96,7 @@ module.exports = {
8896
await IPFS.create({
8997
repo: repoPath,
9098
init: {
91-
algorithm: argv.algorithm,
99+
algorithm: keyTypes[argv.algorithm],
92100
bits: argv.bits,
93101
privateKey: argv.privateKey,
94102
emptyRepo: argv.emptyRepo,

packages/ipfs-cli/src/commands/key/gen.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ module.exports = {
1313
builder: {
1414
type: {
1515
alias: 't',
16-
describe: 'type of the key to create [rsa, ed25519].',
17-
default: 'rsa'
16+
describe: 'type of the key to create',
17+
choices: ['rsa', 'ed25519'],
18+
default: 'ed25519'
1819
},
1920
size: {
2021
alias: 's',

packages/ipfs-cli/test/key.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ describe('key', () => {
2525
const name = 'key-name'
2626
const id = 'key-id'
2727
const defaultOptions = {
28-
type: 'rsa',
28+
type: 'ed25519',
2929
size: 2048,
3030
timeout: undefined
3131
}
@@ -43,28 +43,28 @@ describe('key', () => {
4343
it('gen with args', async () => {
4444
ipfs.key.gen.withArgs(name, {
4545
...defaultOptions,
46-
type: 'rsb',
46+
type: 'rsa',
4747
size: 7
4848
}).resolves({
4949
id,
5050
name
5151
})
5252

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

5757
it('gen with short args', async () => {
5858
ipfs.key.gen.withArgs(name, {
5959
...defaultOptions,
60-
type: 'rsc',
60+
type: 'rsa',
6161
size: 5
6262
}).resolves({
6363
id,
6464
name
6565
})
6666

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

packages/ipfs-core/src/components/key/gen.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

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

5-
const DEFAULT_KEY_TYPE = 'rsa'
5+
const DEFAULT_KEY_TYPE = 'ed25519'
66
const DEFAULT_KEY_SIZE = 2048
77

88
/**

packages/ipfs-core/src/components/libp2p.js

+7
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,15 @@ const { Multiaddr } = require('multiaddr')
1313
const pkgversion = require('../../package.json').version
1414

1515
/**
16+
* @typedef {object} DekOptions
17+
* @property {string} hash
18+
* @property {string} salt
19+
* @property {number} iterationCount
20+
* @property {number} keyLength
21+
*
1622
* @typedef {Object} KeychainConfig
1723
* @property {string} [pass]
24+
* @property {DekOptions} [dek]
1825
*
1926
* @typedef {import('ipfs-repo').IPFSRepo} Repo
2027
* @typedef {import('peer-id')} PeerId

packages/ipfs-core/src/components/resolve.js

+20-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

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

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

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

3435
// nothing to resolve return the input
3536
if (rest.length === 0) {
36-
return `/${schema}/${cid.toString(base && base.encoder)}`
37+
const str = base ? base.encoder.encode(bytes) : hash
38+
39+
return `/${schema}/${str}`
3740
}
3841

42+
const cid = CID.decode(bytes)
43+
3944
path = rest.join('/')
4045

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

5560
return withTimeoutOption(resolve)
5661
}
62+
63+
/**
64+
* Parse the input as a PeerID or a CID or throw an error
65+
*
66+
* @param {string} str
67+
*/
68+
function parseBytes (str) {
69+
try {
70+
return PeerID.parse(str).toBytes()
71+
} catch {
72+
return CID.parse(str).bytes
73+
}
74+
}

packages/ipfs-core/src/components/storage.js

+18-7
Original file line numberDiff line numberDiff line change
@@ -130,23 +130,34 @@ const initRepo = async (print, repo, options) => {
130130

131131
log('repo opened')
132132

133+
/** @type {import('./libp2p').KeychainConfig} */
134+
const keychainConfig = {
135+
pass: options.pass
136+
}
137+
138+
try {
139+
keychainConfig.dek = await repo.config.get('Keychain.DEK')
140+
} catch (err) {
141+
if (err.code !== 'ERR_NOT_FOUND') {
142+
throw err
143+
}
144+
}
145+
133146
// Create libp2p for Keychain creation
134147
const libp2p = await createLibP2P({
135148
options: undefined,
136149
multiaddrs: undefined,
137150
peerId,
138151
repo,
139152
config,
140-
keychainConfig: {
141-
pass: options.pass
142-
}
153+
keychainConfig
143154
})
144155

145156
if (libp2p.keychain && libp2p.keychain.opts) {
146157
await libp2p.loadKeychain()
147158

148159
await repo.config.set('Keychain', {
149-
dek: libp2p.keychain.opts.dek
160+
DEK: libp2p.keychain.opts.dek
150161
})
151162
}
152163

@@ -172,13 +183,13 @@ const decodePeerId = (peerId) => {
172183
*
173184
* @param {Print} print
174185
* @param {Object} options
175-
* @param {KeyType} [options.algorithm='RSA']
186+
* @param {KeyType} [options.algorithm='Ed25519']
176187
* @param {number} [options.bits=2048]
177188
* @returns {Promise<PeerId>}
178189
*/
179-
const initPeerId = (print, { algorithm = 'RSA', bits = 2048 }) => {
190+
const initPeerId = (print, { algorithm = 'Ed25519', bits = 2048 }) => {
180191
// Generate peer identity keypair + transform to desired format + add to config.
181-
print('generating %s-bit (rsa only) %s keypair...', bits, algorithm)
192+
print('generating %s keypair...', algorithm)
182193
return PeerId.create({ keyType: algorithm, bits })
183194
}
184195

packages/ipfs-core/src/types.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export interface Options {
8484

8585
/**
8686
* Occasionally a repo migration is necessary - pass true here to to this automatically at startup
87-
* when a new version of IPFS is being run for the first time and a migration is necssary, otherwise
87+
* when a new version of IPFS is being run for the first time and a migration is necessary, otherwise
8888
* the node will refuse to start
8989
*/
9090
repoAutoMigrate?: boolean

packages/ipfs-core/test/create-node.spec.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,10 @@ describe('create node', function () {
9898
it('should throw on boot error', () => {
9999
return expect(IPFS.create({
100100
repo: tempRepo,
101-
init: { bits: 256 }, // Too few bits will cause error on boot
101+
init: {
102+
algorithm: 'RSA',
103+
bits: 256
104+
}, // Too few bits will cause error on boot
102105
config: { Addresses: { Swarm: [] } }
103106
})).to.eventually.be.rejected()
104107
})
@@ -109,6 +112,7 @@ describe('create node', function () {
109112
const node = await IPFS.create({
110113
repo: tempRepo,
111114
init: {
115+
algorithm: 'RSA',
112116
bits: 1024
113117
},
114118
config: {

0 commit comments

Comments
 (0)