Skip to content

Commit

Permalink
feat: use ethereum-cryptography for enr crypto (#285)
Browse files Browse the repository at this point in the history
  • Loading branch information
wemeetagain committed Jan 25, 2024
1 parent 594166c commit 50cee57
Show file tree
Hide file tree
Showing 11 changed files with 114 additions and 34 deletions.
10 changes: 10 additions & 0 deletions packages/discv5/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ const libp2p = new Libp2p({

```

## Additional features

By default, importing this library will, as a side-effect, change the enr crypto implementation to use `bcrypto`.
If you'd like to remain using `@chainsafe/enr`'s default crypto you can add this after importing `@chainsafe/discv5`:
```ts
import {setV4Crypto, defaultCrypto} from "@chainsafe/enr";

setV4Crypto(defaultCrypto)
```

## License

Apache-2.0
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import keccak from "bcrypto/lib/keccak.js";
import secp256k1 from "bcrypto/lib/secp256k1.js";

import { NodeId } from "./types.js";
import { createNodeId } from "./create.js";
import { createNodeId, NodeId } from "@chainsafe/enr";

export function hash(input: Uint8Array): Buffer {
return keccak.digest(Buffer.from(input));
Expand Down
6 changes: 6 additions & 0 deletions packages/discv5/src/enr/setV4Crypto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// This module has the side effect of setting the ENR crypto implementations to use bcrypto

import { setV4Crypto } from "@chainsafe/enr";
import * as bcryptoV4Crypto from "./bcryptoV4Crypto.js";

setV4Crypto(bcryptoV4Crypto);
3 changes: 3 additions & 0 deletions packages/discv5/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ export * from "./service/index.js";
export * from "./session/index.js";
export * from "./transport/index.js";
export * from "./util/index.js";

// side effect: set the enr crypto implementation
import "./enr/setV4Crypto.js";
2 changes: 1 addition & 1 deletion packages/enr/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@
"@libp2p/peer-id": "^4.0.4",
"@multiformats/multiaddr": "^12.1.10",
"base64url": "^3.0.1",
"bcrypto": "^5.4.0",
"bigint-buffer": "^1.1.5",
"ethereum-cryptography": "^2.1.3",
"rlp": "^2.2.6",
"uint8-varint": "^2.0.2"
}
Expand Down
22 changes: 22 additions & 0 deletions packages/enr/src/crypto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { NodeId } from "./types.js";
import * as defaultCrypto from "./defaultCrypto.js";

/**
* In order to support different environments (eg: browser vs high performance), a pluggable crypto interface is provided
*/
export type V4Crypto = {
publicKey(privKey: Uint8Array): Uint8Array;
sign(privKey: Uint8Array, msg: Uint8Array): Uint8Array;
verify(pubKey: Uint8Array, msg: Uint8Array, sig: Uint8Array): boolean;
nodeId(pubKey: Uint8Array): NodeId;
};

let v4: V4Crypto = defaultCrypto;

export function setV4Crypto(crypto: V4Crypto): void {
v4 = crypto;
}

export function getV4Crypto(): V4Crypto {
return v4;
}
29 changes: 29 additions & 0 deletions packages/enr/src/defaultCrypto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { keccak256 } from "ethereum-cryptography/keccak";
import { secp256k1 } from "ethereum-cryptography/secp256k1";

import { createNodeId } from "./create.js";
import { NodeId } from "./types.js";

export function hash(input: Uint8Array): Uint8Array {
return keccak256(input);
}

export function publicKey(privKey: Uint8Array): Uint8Array {
return secp256k1.getPublicKey(privKey, true);
}

export function sign(privKey: Uint8Array, msg: Uint8Array): Uint8Array {
return secp256k1.sign(hash(msg), privKey).toCompactRawBytes();
}

export function verify(pubKey: Uint8Array, msg: Uint8Array, sig: Uint8Array): boolean {
return secp256k1.verify(sig, hash(msg), pubKey);
}

function uncompressPublicKey(pubKey: Uint8Array): Uint8Array {
return secp256k1.ProjectivePoint.fromHex(pubKey).toRawBytes(false);
}

export function nodeId(pubKey: Uint8Array): NodeId {
return createNodeId(Buffer.from(hash(uncompressPublicKey(pubKey).slice(1))));
}
31 changes: 6 additions & 25 deletions packages/enr/src/enr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,16 @@ import { convertToString, convertToBytes } from "@multiformats/multiaddr/convert
import { encode as varintEncode } from "uint8-varint";

import { ERR_INVALID_ID, MAX_RECORD_SIZE } from "./constants.js";
import * as bcryptoV4Crypto from "./v4.js";
import { ENRKey, ENRValue, SequenceNumber, NodeId } from "./types.js";
import { createPeerIdFromPublicKey, createPrivateKeyFromPeerId } from "./peerId.js";
import { toNewUint8Array } from "./util.js";
import { getV4Crypto } from "./crypto.js";

/** ENR identity scheme */
export enum IDScheme {
v4 = "v4",
}

// In order to support different environments (eg: browser vs high performance), a pluggable crypto interface is provided

export type V4Crypto = {
publicKey(privKey: Uint8Array): Uint8Array;
sign(privKey: Uint8Array, msg: Uint8Array): Uint8Array;
verify(pubKey: Uint8Array, msg: Uint8Array, sig: Uint8Array): boolean;
nodeId(pubKey: Uint8Array): NodeId;
};

let v4: V4Crypto = bcryptoV4Crypto;

export function setV4Crypto(crypto: V4Crypto): void {
v4 = crypto;
}

export function getV4Crypto(): V4Crypto {
return v4;
}

/** Raw data included in an ENR */
export type ENRData = {
kvs: ReadonlyMap<ENRKey, ENRValue>;
Expand Down Expand Up @@ -63,7 +44,7 @@ export function id(kvs: ReadonlyMap<ENRKey, ENRValue>): IDScheme {
export function nodeId(id: IDScheme, publicKey: Uint8Array): NodeId {
switch (id) {
case IDScheme.v4:
return v4.nodeId(publicKey);
return getV4Crypto().nodeId(publicKey);
default:
throw new Error(ERR_INVALID_ID);
}
Expand Down Expand Up @@ -93,15 +74,15 @@ export function keyType(id: IDScheme): KeyType {
export function verify(id: IDScheme, data: Uint8Array, publicKey: Uint8Array, signature: Uint8Array): boolean {
switch (id) {
case IDScheme.v4:
return v4.verify(publicKey, data, signature);
return getV4Crypto().verify(publicKey, data, signature);
default:
throw new Error(ERR_INVALID_ID);
}
}
export function sign(id: IDScheme, data: Uint8Array, privateKey: Uint8Array): Uint8Array {
switch (id) {
case IDScheme.v4:
return v4.sign(privateKey, data);
return getV4Crypto().sign(privateKey, data);
default:
throw new Error(ERR_INVALID_ID);
}
Expand Down Expand Up @@ -416,7 +397,7 @@ export class SignableENR extends BaseENR {
this._signature = signature;

if (this.id === IDScheme.v4) {
if (Buffer.compare(v4.publicKey(this.privateKey), this.publicKey) !== 0) {
if (Buffer.compare(getV4Crypto().publicKey(this.privateKey), this.publicKey) !== 0) {
throw new Error("Provided keypair doesn't match kv pubkey");
}
}
Expand All @@ -431,7 +412,7 @@ export class SignableENR extends BaseENR {
{
...kvs,
id: Buffer.from("v4"),
secp256k1: v4.publicKey(privateKey),
secp256k1: getV4Crypto().publicKey(privateKey),
},
BigInt(1),
privateKey
Expand Down
2 changes: 2 additions & 0 deletions packages/enr/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export * from "./constants.js";
export * from "./crypto.js";
export * as defaultCrypto from "./defaultCrypto.js";
export * from "./enr.js";
export * from "./types.js";
export * from "./create.js";
Expand Down
4 changes: 0 additions & 4 deletions packages/enr/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@
"outDir": "./lib",
"strict": true,
"strictNullChecks": true,
"typeRoots": [
"../../node_modules/@types",
"../../types"
],
"esModuleInterop": true,
"sourceMap": true,
}
Expand Down
36 changes: 34 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -592,14 +592,14 @@
uint8-varint "^2.0.1"
uint8arrays "^5.0.0"

"@noble/curves@^1.1.0":
"@noble/curves@1.3.0", "@noble/curves@^1.1.0", "@noble/curves@~1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.3.0.tgz#01be46da4fd195822dab821e72f71bf4aeec635e"
integrity sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==
dependencies:
"@noble/hashes" "1.3.3"

"@noble/hashes@1.3.3", "@noble/hashes@^1.3.1":
"@noble/hashes@1.3.3", "@noble/hashes@^1.3.1", "@noble/hashes@~1.3.2":
version "1.3.3"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699"
integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==
Expand Down Expand Up @@ -991,6 +991,28 @@
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==

"@scure/base@~1.1.4":
version "1.1.5"
resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.5.tgz#1d85d17269fe97694b9c592552dd9e5e33552157"
integrity sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==

"@scure/bip32@1.3.3":
version "1.3.3"
resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.3.tgz#a9624991dc8767087c57999a5d79488f48eae6c8"
integrity sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==
dependencies:
"@noble/curves" "~1.3.0"
"@noble/hashes" "~1.3.2"
"@scure/base" "~1.1.4"

"@scure/bip39@1.2.2":
version "1.2.2"
resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.2.tgz#f3426813f4ced11a47489cbcf7294aa963966527"
integrity sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==
dependencies:
"@noble/hashes" "~1.3.2"
"@scure/base" "~1.1.4"

"@sigstore/bundle@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@sigstore/bundle/-/bundle-1.1.0.tgz#17f8d813b09348b16eeed66a8cf1c3d6bd3d04f1"
Expand Down Expand Up @@ -2750,6 +2772,16 @@ esutils@^2.0.2:
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==

ethereum-cryptography@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz#1352270ed3b339fe25af5ceeadcf1b9c8e30768a"
integrity sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA==
dependencies:
"@noble/curves" "1.3.0"
"@noble/hashes" "1.3.3"
"@scure/bip32" "1.3.3"
"@scure/bip39" "1.2.2"

event-target-shim@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
Expand Down

0 comments on commit 50cee57

Please sign in to comment.