Skip to content

Commit

Permalink
fix!: remove private key field from peer id
Browse files Browse the repository at this point in the history
The only time you'll ever see a private key on a peer id is for the
id of the currently running node.

At runtime a service can obtain the unmarhsalled private key by adding
a `privateKey: PrivateKey` field to it's components map so there's no
need to have the field on every `PeerId`.

BREAKING CHANGE: the `.privateKey` field of the `PeerId` interface has been removed
  • Loading branch information
achingbrain committed Aug 13, 2024
1 parent 0edbfe7 commit d8777a8
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 31 deletions.
58 changes: 46 additions & 12 deletions packages/interface/src/peer-id/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,73 @@ import type { MultihashDigest } from 'multiformats/hashes/interface'

export type PeerIdType = KeyType | string

export interface RSAPeerId extends PeerId {
export interface RSAPeerId {
readonly type: 'RSA'
readonly publicKey?: Uint8Array
readonly multihash: MultihashDigest

toString(): string
toCID(): CID
toBytes(): Uint8Array
equals(other?: PeerId | Uint8Array | string): boolean
}

export interface Ed25519PeerId extends PeerId {
export interface Ed25519PeerId {
readonly type: 'Ed25519'
readonly publicKey: Uint8Array
readonly multihash: MultihashDigest

toString(): string
toCID(): CID
toBytes(): Uint8Array
equals(other?: PeerId | Uint8Array | string): boolean
}

export interface Secp256k1PeerId extends PeerId {
export interface Secp256k1PeerId {
readonly type: 'secp256k1'
readonly publicKey: Uint8Array
}
readonly multihash: MultihashDigest

export interface URLPeerId extends PeerId {
readonly type: 'url'
toString(): string
toCID(): CID
toBytes(): Uint8Array
equals(other?: PeerId | Uint8Array | string): boolean
}

export interface PeerId {
type: PeerIdType
multihash: MultihashDigest
privateKey?: Uint8Array
publicKey?: Uint8Array
export interface URLPeerId {
readonly type: 'url'
readonly multihash: MultihashDigest
readonly publicKey: Uint8Array | undefined

toString(): string
toCID(): CID
toBytes(): Uint8Array
equals(other?: PeerId | Uint8Array | string): boolean
}

export type PeerId = RSAPeerId | Ed25519PeerId | Secp256k1PeerId | URLPeerId

export interface LocalRSAPeerId extends RSAPeerId {
readonly publicKey: Uint8Array
readonly privateKey: Uint8Array
}

export interface LocalEd25519PeerId extends Ed25519PeerId {
readonly privateKey: Uint8Array
}

export interface LocalSecp256k1PeerId extends Secp256k1PeerId {
readonly privateKey: Uint8Array
}

export type LocalPeerId = LocalRSAPeerId | LocalEd25519PeerId | LocalSecp256k1PeerId

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

export function isPeerId (other: any): other is PeerId {
export function isPeerId (other?: any): other is PeerId {
return other != null && Boolean(other[peerIdSymbol])
}

export function isLocalPeerId (other?: any): other is LocalPeerId {
return Boolean(other?.privateKey) && isPeerId(other)
}
19 changes: 11 additions & 8 deletions packages/peer-id-factory/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@
*/

import { generateKeyPair, marshalPrivateKey, unmarshalPrivateKey, marshalPublicKey, unmarshalPublicKey } from '@libp2p/crypto/keys'
import { isLocalPeerId, isPeerId } from '@libp2p/interface'
import { peerIdFromKeys, peerIdFromBytes } from '@libp2p/peer-id'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
import { PeerIdProto } from './proto.js'
import type { PublicKey, PrivateKey, RSAPeerId, Ed25519PeerId, Secp256k1PeerId, KeyType } from '@libp2p/interface'
import type { PublicKey, PrivateKey, RSAPeerId, Ed25519PeerId, Secp256k1PeerId, KeyType, LocalEd25519PeerId, LocalSecp256k1PeerId, LocalRSAPeerId, LocalPeerId, PeerId } from '@libp2p/interface'

export const createEd25519PeerId = async (): Promise<Ed25519PeerId> => {
export const createEd25519PeerId = async (): Promise<LocalEd25519PeerId> => {
const key = await generateKeyPair('Ed25519')
const id = await createFromPrivKey(key)

Expand All @@ -38,7 +39,7 @@ export const createEd25519PeerId = async (): Promise<Ed25519PeerId> => {
throw new Error(`Generated unexpected PeerId type "${id.type}"`)
}

export const createSecp256k1PeerId = async (): Promise<Secp256k1PeerId> => {
export const createSecp256k1PeerId = async (): Promise<LocalSecp256k1PeerId> => {
const key = await generateKeyPair('secp256k1')
const id = await createFromPrivKey(key)

Expand All @@ -49,7 +50,7 @@ export const createSecp256k1PeerId = async (): Promise<Secp256k1PeerId> => {
throw new Error(`Generated unexpected PeerId type "${id.type}"`)
}

export const createRSAPeerId = async (opts?: { bits: number }): Promise<RSAPeerId> => {
export const createRSAPeerId = async (opts?: { bits: number }): Promise<LocalRSAPeerId> => {
const key = await generateKeyPair('RSA', opts?.bits ?? 2048)
const id = await createFromPrivKey(key)

Expand All @@ -64,15 +65,15 @@ export async function createFromPubKey <T extends KeyType > (publicKey: PublicKe
return peerIdFromKeys(marshalPublicKey(publicKey))
}

export async function createFromPrivKey <T extends KeyType > (privateKey: PrivateKey<T>): Promise<Ed25519PeerId | Secp256k1PeerId | RSAPeerId> {
export async function createFromPrivKey <T extends KeyType > (privateKey: PrivateKey<T>): Promise<LocalEd25519PeerId | LocalSecp256k1PeerId | LocalRSAPeerId> {
return peerIdFromKeys(marshalPublicKey(privateKey.public), marshalPrivateKey(privateKey))
}

export function exportToProtobuf (peerId: RSAPeerId | Ed25519PeerId | Secp256k1PeerId, excludePrivateKey?: boolean): Uint8Array {
export function exportToProtobuf (peerId: PeerId | LocalPeerId, excludePrivateKey?: boolean): Uint8Array {
return PeerIdProto.encode({
id: peerId.multihash.bytes,
pubKey: peerId.publicKey,
privKey: excludePrivateKey === true || peerId.privateKey == null ? undefined : peerId.privateKey
pubKey: isPeerId(peerId) && peerId.type !== 'url' ? peerId.publicKey : undefined,
privKey: excludePrivateKey === true || isLocalPeerId(peerId) === false ? undefined : peerId.privateKey
})
}

Expand All @@ -90,6 +91,8 @@ export async function createFromProtobuf (buf: Uint8Array): Promise<Ed25519PeerI
)
}

export async function createFromJSON (obj: { id: string, pubKey?: string }): Promise<PeerId>
export async function createFromJSON (obj: { id: string, privKey: string, pubKey?: string }): Promise<LocalPeerId>
export async function createFromJSON (obj: { id: string, privKey?: string, pubKey?: string }): Promise<Ed25519PeerId | Secp256k1PeerId | RSAPeerId> {
return createFromParts(
uint8ArrayFromString(obj.id, 'base58btc'),
Expand Down
4 changes: 1 addition & 3 deletions packages/peer-id-factory/test/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ describe('PeerId', () => {
const id = await PeerIdFactory.createEd25519PeerId()
const other = await PeerIdFactory.createFromJSON({
id: id.toString(),
privKey: id.privateKey != null ? uint8ArrayToString(id.privateKey, 'base64pad') : undefined,
privKey: uint8ArrayToString(id.privateKey, 'base64pad'),
pubKey: uint8ArrayToString(id.publicKey, 'base64pad')
})
expect(id.toString()).to.equal(other.toString())
Expand All @@ -260,11 +260,9 @@ describe('PeerId', () => {
const key = await keys.generateKeyPair('RSA', 1024)
const digest = await key.public.hash()
const id = peerIdFromBytes(digest)
expect(id.privateKey).to.not.exist()
expect(id.publicKey).to.not.exist()
const other = await PeerIdFactory.createFromJSON({
id: id.toString(),
privKey: id.privateKey != null ? uint8ArrayToString(id.privateKey, 'base64pad') : undefined,
pubKey: id.publicKey != null ? uint8ArrayToString(id.publicKey, 'base64pad') : undefined
})
expect(id.toString()).to.equal(other.toString())
Expand Down
17 changes: 9 additions & 8 deletions packages/peer-id/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
* ```
*/

import { CodeError } from '@libp2p/interface'
import { type Ed25519PeerId, type PeerIdType, type RSAPeerId, type URLPeerId, type Secp256k1PeerId, peerIdSymbol, type PeerId } from '@libp2p/interface'
import { CodeError, peerIdSymbol } from '@libp2p/interface'
import { base58btc } from 'multiformats/bases/base58'
import { bases } from 'multiformats/basics'
import { CID } from 'multiformats/cid'
Expand All @@ -25,6 +24,7 @@ import { sha256 } from 'multiformats/hashes/sha2'
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
import type { LocalEd25519PeerId, LocalRSAPeerId, LocalSecp256k1PeerId, Ed25519PeerId, PeerIdType, RSAPeerId, URLPeerId, Secp256k1PeerId, PeerId } from '@libp2p/interface'
import type { MultibaseDecoder } from 'multiformats/bases/interface'
import type { MultihashDigest } from 'multiformats/hashes/interface'

Expand Down Expand Up @@ -189,8 +189,7 @@ const TRANSPORT_IPFS_GATEWAY_HTTP_CODE = 0x0920
class URLPeerIdImpl implements URLPeerId {
readonly type = 'url'
readonly multihash: MultihashDigest
readonly privateKey?: Uint8Array
readonly publicKey?: Uint8Array
readonly publicKey: Uint8Array | undefined
readonly url: string

constructor (url: URL) {
Expand Down Expand Up @@ -229,7 +228,7 @@ class URLPeerIdImpl implements URLPeerId {
}
}

export function createPeerId (init: PeerIdInit): Ed25519PeerId | Secp256k1PeerId | RSAPeerId {
export function createPeerId (init: PeerIdInit): PeerId {
if (init.type === 'RSA') {
return new RSAPeerIdImpl(init)
}
Expand All @@ -245,7 +244,7 @@ export function createPeerId (init: PeerIdInit): Ed25519PeerId | Secp256k1PeerId
throw new CodeError('Type must be "RSA", "Ed25519" or "secp256k1"', 'ERR_INVALID_PARAMETERS')
}

export function peerIdFromPeerId (other: any): Ed25519PeerId | Secp256k1PeerId | RSAPeerId {
export function peerIdFromPeerId (other: any): PeerId {
if (other.type === 'RSA') {
return new RSAPeerIdImpl(other)
}
Expand All @@ -261,7 +260,7 @@ export function peerIdFromPeerId (other: any): Ed25519PeerId | Secp256k1PeerId |
throw new CodeError('Not a PeerId', 'ERR_INVALID_PARAMETERS')
}

export function peerIdFromString (str: string, decoder?: MultibaseDecoder<any>): Ed25519PeerId | Secp256k1PeerId | RSAPeerId | URLPeerId {
export function peerIdFromString (str: string, decoder?: MultibaseDecoder<any>): PeerId {
decoder = decoder ?? baseDecoder

if (str.charAt(0) === '1' || str.charAt(0) === 'Q') {
Expand All @@ -281,7 +280,7 @@ export function peerIdFromString (str: string, decoder?: MultibaseDecoder<any>):
return peerIdFromBytes(baseDecoder.decode(str))
}

export function peerIdFromBytes (buf: Uint8Array): Ed25519PeerId | Secp256k1PeerId | RSAPeerId | URLPeerId {
export function peerIdFromBytes (buf: Uint8Array): PeerId {
try {
const multihash = Digest.decode(buf)

Expand Down Expand Up @@ -333,6 +332,8 @@ export function peerIdFromCID (cid: CID): Ed25519PeerId | Secp256k1PeerId | RSAP
* @param publicKey - A marshalled public key
* @param privateKey - A marshalled private key
*/
export async function peerIdFromKeys (publicKey: Uint8Array): Promise<Ed25519PeerId | Secp256k1PeerId | RSAPeerId>
export async function peerIdFromKeys (publicKey: Uint8Array, privateKey: Uint8Array): Promise<LocalEd25519PeerId | LocalSecp256k1PeerId | LocalRSAPeerId>
export async function peerIdFromKeys (publicKey: Uint8Array, privateKey?: Uint8Array): Promise<Ed25519PeerId | Secp256k1PeerId | RSAPeerId> {
if (publicKey.length === MARSHALLED_ED225519_PUBLIC_KEY_LENGTH) {
return new Ed25519PeerIdImpl({ multihash: Digest.create(identity.code, publicKey), privateKey })
Expand Down

0 comments on commit d8777a8

Please sign in to comment.