Skip to content

Commit 0d5d966

Browse files
feat: extensible peer ids (#2496)
Sometimes a peer will have a non-cryptographic identifier such as a URL. Update the PeerId type to be an interface so we can use arbitrary data to identify other peers, not just cryptographic keys. --------- Co-authored-by: Russell Dempsey <[email protected]>
1 parent fd1f834 commit 0d5d966

File tree

8 files changed

+49
-50
lines changed

8 files changed

+49
-50
lines changed

packages/crypto/src/keys/ed25519-class.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ import { isPromise } from '../util.js'
77
import * as crypto from './ed25519.js'
88
import { exporter } from './exporter.js'
99
import * as pbm from './keys.js'
10+
import type { PublicKey, PrivateKey } from '@libp2p/interface'
1011
import type { Multibase } from 'multiformats'
1112
import type { Uint8ArrayList } from 'uint8arraylist'
1213

13-
export class Ed25519PublicKey {
14+
export class Ed25519PublicKey implements PublicKey<'Ed25519'> {
1415
private readonly _key: Uint8Array
1516

1617
constructor (key: Uint8Array) {
@@ -47,7 +48,7 @@ export class Ed25519PublicKey {
4748
}
4849
}
4950

50-
export class Ed25519PrivateKey {
51+
export class Ed25519PrivateKey implements PrivateKey<'Ed25519'> {
5152
private readonly _key: Uint8Array
5253
private readonly _publicKey: Uint8Array
5354

packages/crypto/src/keys/index.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ import * as keysPBM from './keys.js'
1919
import * as RSA from './rsa-class.js'
2020
import { importFromPem } from './rsa-utils.js'
2121
import * as Secp256k1 from './secp256k1-class.js'
22-
import type { PrivateKey, PublicKey } from '@libp2p/interface'
22+
import type { PrivateKey, PublicKey, KeyType as KeyTypes } from '@libp2p/interface'
2323

2424
export { keyStretcher }
2525
export { generateEphemeralKeyPair }
2626
export { keysPBM }
2727

28-
export type KeyTypes = 'RSA' | 'Ed25519' | 'secp256k1'
28+
export type { KeyTypes }
2929

3030
export { RsaPrivateKey, RsaPublicKey, MAX_RSA_KEY_SIZE } from './rsa-class.js'
3131
export { Ed25519PrivateKey, Ed25519PublicKey } from './ed25519-class.js'
@@ -55,11 +55,8 @@ function typeToKey (type: string): typeof RSA | typeof Ed25519 | typeof Secp256k
5555

5656
/**
5757
* Generates a keypair of the given type and bitsize
58-
*
59-
* @param type
60-
* @param bits - Minimum of 1024
6158
*/
62-
export async function generateKeyPair (type: KeyTypes, bits?: number): Promise<PrivateKey> {
59+
export async function generateKeyPair <T extends KeyTypes> (type: T, bits?: number): Promise<PrivateKey<T>> {
6360
return typeToKey(type).generateKeyPair(bits ?? 2048)
6461
}
6562

@@ -68,7 +65,7 @@ export async function generateKeyPair (type: KeyTypes, bits?: number): Promise<P
6865
*
6966
* Seed is a 32 byte uint8array
7067
*/
71-
export async function generateKeyPairFromSeed (type: KeyTypes, seed: Uint8Array, bits?: number): Promise<PrivateKey> {
68+
export async function generateKeyPairFromSeed <T extends KeyTypes> (type: T, seed: Uint8Array, bits?: number): Promise<PrivateKey<T>> {
7269
if (type.toLowerCase() !== 'ed25519') {
7370
throw new CodeError('Seed key derivation is unimplemented for RSA or secp256k1', 'ERR_UNSUPPORTED_KEY_DERIVATION_TYPE')
7471
}
@@ -79,7 +76,7 @@ export async function generateKeyPairFromSeed (type: KeyTypes, seed: Uint8Array,
7976
/**
8077
* Converts a protobuf serialized public key into its representative object
8178
*/
82-
export function unmarshalPublicKey (buf: Uint8Array): PublicKey {
79+
export function unmarshalPublicKey <T extends KeyTypes> (buf: Uint8Array): PublicKey<T> {
8380
const decoded = keysPBM.PublicKey.decode(buf)
8481
const data = decoded.Data ?? new Uint8Array()
8582

@@ -107,7 +104,7 @@ export function marshalPublicKey (key: { bytes: Uint8Array }, type?: string): Ui
107104
/**
108105
* Converts a protobuf serialized private key into its representative object
109106
*/
110-
export async function unmarshalPrivateKey (buf: Uint8Array): Promise<PrivateKey> {
107+
export async function unmarshalPrivateKey <T extends KeyTypes> (buf: Uint8Array): Promise<PrivateKey<T>> {
111108
const decoded = keysPBM.PrivateKey.decode(buf)
112109
const data = decoded.Data ?? new Uint8Array()
113110

@@ -137,7 +134,7 @@ export function marshalPrivateKey (key: { bytes: Uint8Array }, type?: string): U
137134
*
138135
* Supported formats are 'pem' (RSA only) and 'libp2p-key'.
139136
*/
140-
export async function importKey (encryptedKey: string, password: string): Promise<PrivateKey> {
137+
export async function importKey <T extends KeyTypes> (encryptedKey: string, password: string): Promise<PrivateKey<T>> {
141138
try {
142139
const key = await importer(encryptedKey, password)
143140
return await unmarshalPrivateKey(key)

packages/crypto/src/keys/rsa-class.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ import { isPromise } from '../util.js'
66
import { exporter } from './exporter.js'
77
import * as pbm from './keys.js'
88
import * as crypto from './rsa.js'
9+
import type { PublicKey, PrivateKey } from '@libp2p/interface'
910
import type { Multibase } from 'multiformats'
1011
import type { Uint8ArrayList } from 'uint8arraylist'
1112

1213
export const MAX_RSA_KEY_SIZE = 8192
1314

14-
export class RsaPublicKey {
15+
export class RsaPublicKey implements PublicKey<'RSA'> {
1516
private readonly _key: JsonWebKey
1617

1718
constructor (key: JsonWebKey) {
@@ -48,7 +49,7 @@ export class RsaPublicKey {
4849
}
4950
}
5051

51-
export class RsaPrivateKey {
52+
export class RsaPrivateKey implements PrivateKey<'RSA'> {
5253
private readonly _key: JsonWebKey
5354
private readonly _publicKey: JsonWebKey
5455

packages/crypto/src/keys/secp256k1-class.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ import { isPromise } from '../util.js'
66
import { exporter } from './exporter.js'
77
import * as keysProtobuf from './keys.js'
88
import * as crypto from './secp256k1.js'
9+
import type { PublicKey, PrivateKey } from '@libp2p/interface'
910
import type { Multibase } from 'multiformats'
1011
import type { Uint8ArrayList } from 'uint8arraylist'
1112

12-
export class Secp256k1PublicKey {
13+
export class Secp256k1PublicKey implements PublicKey<'secp256k1'> {
1314
private readonly _key: Uint8Array
1415

1516
constructor (key: Uint8Array) {
@@ -50,7 +51,7 @@ export class Secp256k1PublicKey {
5051
}
5152
}
5253

53-
export class Secp256k1PrivateKey {
54+
export class Secp256k1PrivateKey implements PrivateKey<'secp256k1'> {
5455
private readonly _key: Uint8Array
5556
private readonly _publicKey: Uint8Array
5657

packages/interface/src/keys/index.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
import type { Uint8ArrayList } from 'uint8arraylist'
22

3-
export interface PublicKey {
3+
export interface PublicKey<Type extends KeyType = 'Ed25519'> {
44
readonly bytes: Uint8Array
55
verify(data: Uint8Array | Uint8ArrayList, sig: Uint8Array): boolean | Promise<boolean>
66
marshal(): Uint8Array
7-
equals(key: PublicKey): boolean
7+
equals(key: PublicKey<Type>): boolean
88
hash(): Uint8Array | Promise<Uint8Array>
99
}
1010

1111
/**
1212
* Generic private key interface
1313
*/
14-
export interface PrivateKey {
15-
readonly public: PublicKey
14+
export interface PrivateKey<Type extends KeyType = 'Ed25519'> {
15+
readonly public: PublicKey<Type>
1616
readonly bytes: Uint8Array
1717
sign(data: Uint8Array | Uint8ArrayList): Uint8Array | Promise<Uint8Array>
1818
marshal(): Uint8Array
19-
equals(key: PrivateKey): boolean
19+
equals(key: PrivateKey<Type>): boolean
2020
hash(): Uint8Array | Promise<Uint8Array>
2121
/**
2222
* Gets the ID of the key.

packages/interface/src/peer-id/index.ts

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,35 @@
1+
import type { KeyType } from '../keys/index.js'
12
import type { CID } from 'multiformats/cid'
23
import type { MultihashDigest } from 'multiformats/hashes/interface'
34

4-
export type PeerIdType = 'RSA' | 'Ed25519' | 'secp256k1'
5+
export type PeerIdType = KeyType | string
56

6-
interface BasePeerId {
7-
readonly type: PeerIdType
8-
readonly multihash: MultihashDigest
9-
readonly privateKey?: Uint8Array
10-
readonly publicKey?: Uint8Array
11-
12-
toString(): string
13-
toCID(): CID
14-
toBytes(): Uint8Array
15-
equals(other?: PeerId | Uint8Array | string): boolean
16-
}
17-
18-
export interface RSAPeerId extends BasePeerId {
7+
export interface RSAPeerId extends PeerId {
198
readonly type: 'RSA'
209
readonly publicKey?: Uint8Array
2110
}
2211

23-
export interface Ed25519PeerId extends BasePeerId {
12+
export interface Ed25519PeerId extends PeerId {
2413
readonly type: 'Ed25519'
2514
readonly publicKey: Uint8Array
2615
}
2716

28-
export interface Secp256k1PeerId extends BasePeerId {
17+
export interface Secp256k1PeerId extends PeerId {
2918
readonly type: 'secp256k1'
3019
readonly publicKey: Uint8Array
3120
}
3221

33-
export type PeerId = RSAPeerId | Ed25519PeerId | Secp256k1PeerId
22+
export interface PeerId {
23+
type: PeerIdType
24+
multihash: MultihashDigest
25+
privateKey?: Uint8Array
26+
publicKey?: Uint8Array
27+
28+
toString(): string
29+
toCID(): CID
30+
toBytes(): Uint8Array
31+
equals(other?: PeerId | Uint8Array | string): boolean
32+
}
3433

3534
export const peerIdSymbol = Symbol.for('@libp2p/peer-id')
3635

packages/peer-id-factory/src/index.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { generateKeyPair, marshalPrivateKey, unmarshalPrivateKey, marshalPublicK
2525
import { peerIdFromKeys, peerIdFromBytes } from '@libp2p/peer-id'
2626
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
2727
import { PeerIdProto } from './proto.js'
28-
import type { PublicKey, PrivateKey, RSAPeerId, Ed25519PeerId, Secp256k1PeerId, PeerId } from '@libp2p/interface'
28+
import type { PublicKey, PrivateKey, RSAPeerId, Ed25519PeerId, Secp256k1PeerId, KeyType } from '@libp2p/interface'
2929

3030
export const createEd25519PeerId = async (): Promise<Ed25519PeerId> => {
3131
const key = await generateKeyPair('Ed25519')
@@ -60,11 +60,11 @@ export const createRSAPeerId = async (opts?: { bits: number }): Promise<RSAPeerI
6060
throw new Error(`Generated unexpected PeerId type "${id.type}"`)
6161
}
6262

63-
export async function createFromPubKey (publicKey: PublicKey): Promise<PeerId> {
63+
export async function createFromPubKey <T extends KeyType > (publicKey: PublicKey<T>): Promise<Ed25519PeerId | Secp256k1PeerId | RSAPeerId> {
6464
return peerIdFromKeys(marshalPublicKey(publicKey))
6565
}
6666

67-
export async function createFromPrivKey (privateKey: PrivateKey): Promise<PeerId> {
67+
export async function createFromPrivKey <T extends KeyType > (privateKey: PrivateKey<T>): Promise<Ed25519PeerId | Secp256k1PeerId | RSAPeerId> {
6868
return peerIdFromKeys(marshalPublicKey(privateKey.public), marshalPrivateKey(privateKey))
6969
}
7070

@@ -76,7 +76,7 @@ export function exportToProtobuf (peerId: RSAPeerId | Ed25519PeerId | Secp256k1P
7676
})
7777
}
7878

79-
export async function createFromProtobuf (buf: Uint8Array): Promise<PeerId> {
79+
export async function createFromProtobuf (buf: Uint8Array): Promise<Ed25519PeerId | Secp256k1PeerId | RSAPeerId> {
8080
const {
8181
id,
8282
privKey,
@@ -90,15 +90,15 @@ export async function createFromProtobuf (buf: Uint8Array): Promise<PeerId> {
9090
)
9191
}
9292

93-
export async function createFromJSON (obj: { id: string, privKey?: string, pubKey?: string }): Promise<PeerId> {
93+
export async function createFromJSON (obj: { id: string, privKey?: string, pubKey?: string }): Promise<Ed25519PeerId | Secp256k1PeerId | RSAPeerId> {
9494
return createFromParts(
9595
uint8ArrayFromString(obj.id, 'base58btc'),
9696
obj.privKey != null ? uint8ArrayFromString(obj.privKey, 'base64pad') : undefined,
9797
obj.pubKey != null ? uint8ArrayFromString(obj.pubKey, 'base64pad') : undefined
9898
)
9999
}
100100

101-
async function createFromParts (multihash: Uint8Array, privKey?: Uint8Array, pubKey?: Uint8Array): Promise<PeerId> {
101+
async function createFromParts (multihash: Uint8Array, privKey?: Uint8Array, pubKey?: Uint8Array): Promise<Ed25519PeerId | Secp256k1PeerId | RSAPeerId> {
102102
if (privKey != null) {
103103
const key = await unmarshalPrivateKey(privKey)
104104

packages/peer-id/src/index.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ class Secp256k1PeerIdImpl extends PeerIdImpl implements Secp256k1PeerId {
181181
}
182182
}
183183

184-
export function createPeerId (init: PeerIdInit): PeerId {
184+
export function createPeerId (init: PeerIdInit): Ed25519PeerId | Secp256k1PeerId | RSAPeerId {
185185
if (init.type === 'RSA') {
186186
return new RSAPeerIdImpl(init)
187187
}
@@ -197,7 +197,7 @@ export function createPeerId (init: PeerIdInit): PeerId {
197197
throw new CodeError('Type must be "RSA", "Ed25519" or "secp256k1"', 'ERR_INVALID_PARAMETERS')
198198
}
199199

200-
export function peerIdFromPeerId (other: any): PeerId {
200+
export function peerIdFromPeerId (other: any): Ed25519PeerId | Secp256k1PeerId | RSAPeerId {
201201
if (other.type === 'RSA') {
202202
return new RSAPeerIdImpl(other)
203203
}
@@ -213,7 +213,7 @@ export function peerIdFromPeerId (other: any): PeerId {
213213
throw new CodeError('Not a PeerId', 'ERR_INVALID_PARAMETERS')
214214
}
215215

216-
export function peerIdFromString (str: string, decoder?: MultibaseDecoder<any>): PeerId {
216+
export function peerIdFromString (str: string, decoder?: MultibaseDecoder<any>): Ed25519PeerId | Secp256k1PeerId | RSAPeerId {
217217
decoder = decoder ?? baseDecoder
218218

219219
if (str.charAt(0) === '1' || str.charAt(0) === 'Q') {
@@ -233,7 +233,7 @@ export function peerIdFromString (str: string, decoder?: MultibaseDecoder<any>):
233233
return peerIdFromBytes(baseDecoder.decode(str))
234234
}
235235

236-
export function peerIdFromBytes (buf: Uint8Array): PeerId {
236+
export function peerIdFromBytes (buf: Uint8Array): Ed25519PeerId | Secp256k1PeerId | RSAPeerId {
237237
try {
238238
const multihash = Digest.decode(buf)
239239

@@ -255,7 +255,7 @@ export function peerIdFromBytes (buf: Uint8Array): PeerId {
255255
throw new Error('Supplied PeerID CID is invalid')
256256
}
257257

258-
export function peerIdFromCID (cid: CID): PeerId {
258+
export function peerIdFromCID (cid: CID): Ed25519PeerId | Secp256k1PeerId | RSAPeerId {
259259
if (cid == null || cid.multihash == null || cid.version == null || (cid.version === 1 && cid.code !== LIBP2P_KEY_CODE)) {
260260
throw new Error('Supplied PeerID CID is invalid')
261261
}
@@ -279,7 +279,7 @@ export function peerIdFromCID (cid: CID): PeerId {
279279
* @param publicKey - A marshalled public key
280280
* @param privateKey - A marshalled private key
281281
*/
282-
export async function peerIdFromKeys (publicKey: Uint8Array, privateKey?: Uint8Array): Promise<PeerId> {
282+
export async function peerIdFromKeys (publicKey: Uint8Array, privateKey?: Uint8Array): Promise<Ed25519PeerId | Secp256k1PeerId | RSAPeerId> {
283283
if (publicKey.length === MARSHALLED_ED225519_PUBLIC_KEY_LENGTH) {
284284
return new Ed25519PeerIdImpl({ multihash: Digest.create(identity.code, publicKey), privateKey })
285285
}

0 commit comments

Comments
 (0)