Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix!: remove private key field from peer id #2660

Merged
merged 20 commits into from
Sep 4, 2024

Conversation

achingbrain
Copy link
Member

@achingbrain achingbrain commented Aug 13, 2024

The only time you should ever see a private key on a PeerId is for the id of the currently running node. An exception to this is the keychain which used PeerIds to export private keys, but a nicer API there is to just deal with PrivateKey instances directly.

At runtime a service can obtain the unmarshaled 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.

The publicKey field of a PeerId was a Uint8Array which (if present) held the public key bytes in a protobuf wrapper along with a field saying what type of key it was. This was because we wanted to avoid pulling @libp2p/crypto into front ends, since it had a dependency on node-forge which was a large blob of untreeshakable CSJ code. This dependency has been removed so @libp2p/crypto is now comparatively lightweight so we can use the PublicKey type instead of Uint8Array, which also saves CPU time since we don't need to unmarshal the key before we can use it.

Fixes #2659

BREAKING CHANGE:

  • The .privateKey field of the PeerId interface has been removed
  • The .publicKey field of the PeerId interface is now a PublicKey instead of a Uint8Array
  • createLibp2p now accepts a privateKey instead of a peerId
  • The keychain operates on PrivateKey instances instead of PeerIds with .privateKey fields
  • @libp2p/peer-id-factory has been removed, use generateKeyPair and peerIdFromPrivateKey instead

Change checklist

  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation if necessary (this includes comments as well)
  • I have added tests that prove my fix is effective or that my feature works

@achingbrain achingbrain mentioned this pull request Aug 13, 2024
27 tasks
@achingbrain
Copy link
Member Author

achingbrain commented Aug 13, 2024

Opened as draft so we can agree early on the nomenclature.

  • Things like peerIdFromString return a PeerId which is a Ed public key or RSA key hash or what have you
  • Functions like createEd25519PeerId return a LocalPeerId which is a PeerId with the .privateKey field and are mostly used in tests This is no longer true

I suppose we could change the creation of Peer Ids so you create a keypair, then turn the public key into a PeerId?

Comment on lines -26 to -30
export interface PeerId {
type: PeerIdType
multihash: MultihashDigest
privateKey?: Uint8Array
publicKey?: Uint8Array
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we take this opportunity to do something that requires a little less maintenance for these very common interfaces?

export interface LocalPeerIdBase {
  readonly publicKey: Uint8Array
  readonly privateKey: Uint8Array
}

export interface PeerIdBase <pType extends PeerIdType, key extends Uint8Array = Uint8Array> {
  readonly type: pType
  readonly publicKey: key
  readonly multihash: MultihashDigest

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

export type RSAPeerId = PeerIdBase<'RSA'>
export type LocalRSAPeerId = RSAPeerId & LocalPeerIdBase
export type PeerId = RSAPeerId | Ed25519PeerId | Secp256k1PeerId | URLPeerId

basic demo on typescript playground

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idk that we're planning on adding many more peerId types or anything, but it results in less code that seems easier to reason about for me at least. and will be less lines in the future if we need to extend PeerIdBase

Copy link
Member Author

@achingbrain achingbrain Aug 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I somewhat did this on purpose. As suggested the generated API docs will have you clicking through from PeerId -> Ed25519PeerId -> PeerIdBase to see the docs for the fields on the type.

No-one will ever deal with a PeerIdBase in day to day code so I just copied the fields/methods to make the docs simpler.

They are interfaces so are removed at build time so there's no bundle size impact.

@SgtPooki
Copy link
Member

SgtPooki commented Aug 13, 2024

I suppose we could change the creation of Peer Ids so you create a keypair, then turn the public key into a PeerId?

I think that's what you and @wemeetagain agreed to on the maintainers' call.

Is there prior art in go-libp2p or rust-libp2p on how they generate peer IDs? Do we have a need to accept the full keypair, so we're validating the person providing that public key so we get out of any impersonation attempts, or noise, with duplicate peerIds on the network?

@achingbrain achingbrain changed the base branch from main to release-v2.0 August 15, 2024 08:26
@achingbrain achingbrain force-pushed the fix/remove-private-key-from-peer-id branch 2 times, most recently from 939c741 to de57508 Compare August 15, 2024 17:24
@achingbrain achingbrain added the version-2.0 PRs that will be released in libp2p v2 label Aug 15, 2024
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
@achingbrain achingbrain force-pushed the fix/remove-private-key-from-peer-id branch from de57508 to a3226cd Compare August 15, 2024 18:01
@achingbrain achingbrain marked this pull request as ready for review August 30, 2024 18:40
@achingbrain achingbrain requested a review from a team as a code owner August 30, 2024 18:40
@achingbrain
Copy link
Member Author

Is there prior art in go-libp2p or rust-libp2p on how they generate peer IDs? Do we have a need to accept the full keypair, so we're validating the person providing that public key so we get out of any impersonation attempts, or noise, with duplicate peerIds on the network?

We don't need the keypair to create a PeerId, just the public key (for Ed and secp keys) or the hash of the public key (for RSA keys).

We validate the remote peer has the private key corresponding to the public key during the connection upgrade process.

Duplicate PeerId being used by different hosts would cause a problem but fixing that is outside the scope of this PR.

@@ -3,7 +3,7 @@ syntax = "proto3";
enum KeyType {
RSA = 0;
Ed25519 = 1;
Secp256k1 = 2;
secp256k1 = 2;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's referred to as secp256k1 in it's paper, plus where we have it as a string constant we have it as "secp256k1" so it's just bringing this in line with that.

On the wire 2 is still 2 so there's no difference.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should include it in the breaking changes list

Copy link
Member

@wemeetagain wemeetagain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@achingbrain achingbrain merged commit 2920136 into release-v2.0 Sep 4, 2024
21 checks passed
@achingbrain achingbrain deleted the fix/remove-private-key-from-peer-id branch September 4, 2024 12:55
achingbrain added a commit that referenced this pull request Sep 6, 2024
The only time you should ever see a private key on a `PeerId` is for the id of the currently running node.  An exception to this is the keychain which used `PeerId`s to export private keys, but a nicer API there is to just deal with `PrivateKey` instances directly.

At runtime a service can obtain the unmarshaled 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`.

The `publicKey` field of a `PeerId` was a `Uint8Array` which (if present) held the public key bytes in a protobuf wrapper along with a field saying what type of key it was.  This was because we wanted to avoid pulling `@libp2p/crypto` into front ends, since it had a dependency on `node-forge` which was a large blob of untreeshakable CSJ code.  This dependency has been removed so `@libp2p/crypto` is now comparatively lightweight so we can use the `PublicKey` type instead of `Uint8Array`, which also saves CPU time since we don't need to unmarshal the key before we can use it.

Fixes #2659

BREAKING CHANGE:
  - The `.privateKey` field of the `PeerId` interface has been removed
  - The `.publicKey` field of the `PeerId` interface is now a `PublicKey` instead of a `Uint8Array`
  - `createLibp2p` now accepts a `privateKey` instead of a `peerId`
  - The keychain operates on `PrivateKey` instances instead of `PeerId`s with `.privateKey` fields
  - `@libp2p/peer-id-factory` has been removed, use `generateKeyPair` and `peerIdFromPrivateKey` instead
achingbrain added a commit that referenced this pull request Sep 6, 2024
The only time you should ever see a private key on a `PeerId` is for the id of the currently running node.  An exception to this is the keychain which used `PeerId`s to export private keys, but a nicer API there is to just deal with `PrivateKey` instances directly.

At runtime a service can obtain the unmarshaled 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`.

The `publicKey` field of a `PeerId` was a `Uint8Array` which (if present) held the public key bytes in a protobuf wrapper along with a field saying what type of key it was.  This was because we wanted to avoid pulling `@libp2p/crypto` into front ends, since it had a dependency on `node-forge` which was a large blob of untreeshakable CSJ code.  This dependency has been removed so `@libp2p/crypto` is now comparatively lightweight so we can use the `PublicKey` type instead of `Uint8Array`, which also saves CPU time since we don't need to unmarshal the key before we can use it.

Fixes #2659

BREAKING CHANGE:
  - The `.privateKey` field of the `PeerId` interface has been removed
  - The `.publicKey` field of the `PeerId` interface is now a `PublicKey` instead of a `Uint8Array`
  - `createLibp2p` now accepts a `privateKey` instead of a `peerId`
  - The keychain operates on `PrivateKey` instances instead of `PeerId`s with `.privateKey` fields
  - `@libp2p/peer-id-factory` has been removed, use `generateKeyPair` and `peerIdFromPrivateKey` instead
achingbrain added a commit that referenced this pull request Sep 6, 2024
The only time you should ever see a private key on a `PeerId` is for the id of the currently running node.  An exception to this is the keychain which used `PeerId`s to export private keys, but a nicer API there is to just deal with `PrivateKey` instances directly.

At runtime a service can obtain the unmarshaled 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`.

The `publicKey` field of a `PeerId` was a `Uint8Array` which (if present) held the public key bytes in a protobuf wrapper along with a field saying what type of key it was.  This was because we wanted to avoid pulling `@libp2p/crypto` into front ends, since it had a dependency on `node-forge` which was a large blob of untreeshakable CSJ code.  This dependency has been removed so `@libp2p/crypto` is now comparatively lightweight so we can use the `PublicKey` type instead of `Uint8Array`, which also saves CPU time since we don't need to unmarshal the key before we can use it.

Fixes #2659

BREAKING CHANGE:
  - The `.privateKey` field of the `PeerId` interface has been removed
  - The `.publicKey` field of the `PeerId` interface is now a `PublicKey` instead of a `Uint8Array`
  - `createLibp2p` now accepts a `privateKey` instead of a `peerId`
  - The keychain operates on `PrivateKey` instances instead of `PeerId`s with `.privateKey` fields
  - `@libp2p/peer-id-factory` has been removed, use `generateKeyPair` and `peerIdFromPrivateKey` instead
@achingbrain achingbrain mentioned this pull request Sep 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
version-2.0 PRs that will be released in libp2p v2
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Remove private key from peer id
3 participants