Skip to content

Commit

Permalink
feat: constrain usage of Buffer in enr package (#286)
Browse files Browse the repository at this point in the history
  • Loading branch information
wemeetagain authored Jan 25, 2024
1 parent 50cee57 commit 5351f87
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 40 deletions.
4 changes: 2 additions & 2 deletions packages/enr/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@
"@libp2p/interface": "^1.1.1",
"@libp2p/peer-id": "^4.0.4",
"@multiformats/multiaddr": "^12.1.10",
"base64url": "^3.0.1",
"bigint-buffer": "^1.1.5",
"ethereum-cryptography": "^2.1.3",
"rlp": "^2.2.6",
"uint8-varint": "^2.0.2"
"uint8-varint": "^2.0.2",
"uint8arrays": "^5.0.1"
}
}
8 changes: 0 additions & 8 deletions packages/enr/src/create.ts

This file was deleted.

4 changes: 2 additions & 2 deletions packages/enr/src/defaultCrypto.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { keccak256 } from "ethereum-cryptography/keccak";
import { secp256k1 } from "ethereum-cryptography/secp256k1";

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

export function hash(input: Uint8Array): Uint8Array {
Expand All @@ -25,5 +25,5 @@ function uncompressPublicKey(pubKey: Uint8Array): Uint8Array {
}

export function nodeId(pubKey: Uint8Array): NodeId {
return createNodeId(Buffer.from(hash(uncompressPublicKey(pubKey).slice(1))));
return createNodeId(hash(uncompressPublicKey(pubKey).slice(1)));
}
27 changes: 14 additions & 13 deletions packages/enr/src/enr.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { Multiaddr, multiaddr, protocols } from "@multiformats/multiaddr";
import base64url from "base64url";
import { toBigIntBE } from "bigint-buffer";
import * as RLP from "rlp";
import { KeyType, PeerId } from "@libp2p/interface";
import { convertToString, convertToBytes } from "@multiformats/multiaddr/convert";
Expand All @@ -9,8 +7,9 @@ import { encode as varintEncode } from "uint8-varint";
import { ERR_INVALID_ID, MAX_RECORD_SIZE } from "./constants.js";
import { ENRKey, ENRValue, SequenceNumber, NodeId } from "./types.js";
import { createPeerIdFromPublicKey, createPrivateKeyFromPeerId } from "./peerId.js";
import { toNewUint8Array } from "./util.js";
import { fromBase64url, toBase64url, toBigInt, toNewUint8Array } from "./util.js";
import { getV4Crypto } from "./crypto.js";
import { compare, fromString, toString } from "uint8arrays";

/** ENR identity scheme */
export enum IDScheme {
Expand All @@ -34,7 +33,7 @@ export type SignableENRData = {
export function id(kvs: ReadonlyMap<ENRKey, ENRValue>): IDScheme {
const idBuf = kvs.get("id");
if (!idBuf) throw new Error("id not found");
const id = Buffer.from(idBuf).toString("utf8") as IDScheme;
const id = toString(idBuf, "utf8") as IDScheme;
if (IDScheme[id] == null) {
throw new Error("Unknown enr id scheme: " + id);
}
Expand Down Expand Up @@ -143,7 +142,7 @@ export function decodeFromValues(decoded: Uint8Array[]): ENRData {
}
return {
kvs,
seq: toBigIntBE(Buffer.from(seq)),
seq: toBigInt(seq),
signature,
};
}
Expand All @@ -154,7 +153,7 @@ export function txtToBuf(encoded: string): Uint8Array {
if (!encoded.startsWith("enr:")) {
throw new Error("string encoded ENR must start with 'enr:'");
}
return base64url.toBuffer(encoded.slice(4));
return fromBase64url(encoded.slice(4));
}
export function decodeTxt(encoded: string): ENRData {
return decode(txtToBuf(encoded));
Expand All @@ -176,17 +175,19 @@ export function getIPValue(kvs: ReadonlyMap<ENRKey, ENRValue>, key: string, mult
export function getProtocolValue(kvs: ReadonlyMap<ENRKey, ENRValue>, key: string): number | undefined {
const raw = kvs.get(key);
if (raw) {
const view = new DataView(raw.buffer, raw.byteOffset, raw.byteLength);
return view.getUint16(0);
if (raw.length < 2) {
throw new Error("Encoded protocol length should be 2");
}
return (raw[0] << 8) + raw[1];
} else {
return undefined;
}
}

export function portToBuf(port: number): Uint8Array {
const buf = new Uint8Array(2);
const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
view.setUint16(0, port);
buf[0] = port >> 8;
buf[1] = port;
return buf;
}

Expand Down Expand Up @@ -296,7 +297,7 @@ export abstract class BaseENR {
abstract encodeToValues(): (ENRKey | ENRValue | number)[];
abstract encode(): Uint8Array;
encodeTxt(): string {
return "enr:" + base64url.encode(Buffer.from(this.encode()));
return "enr:" + toBase64url(this.encode());
}
}
/**
Expand Down Expand Up @@ -397,7 +398,7 @@ export class SignableENR extends BaseENR {
this._signature = signature;

if (this.id === IDScheme.v4) {
if (Buffer.compare(getV4Crypto().publicKey(this.privateKey), this.publicKey) !== 0) {
if (compare(getV4Crypto().publicKey(this.privateKey), this.publicKey) !== 0) {
throw new Error("Provided keypair doesn't match kv pubkey");
}
}
Expand All @@ -411,7 +412,7 @@ export class SignableENR extends BaseENR {
return new SignableENR(
{
...kvs,
id: Buffer.from("v4"),
id: fromString("v4"),
secp256k1: getV4Crypto().publicKey(privateKey),
},
BigInt(1),
Expand Down
2 changes: 1 addition & 1 deletion packages/enr/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ export * from "./crypto.js";
export * as defaultCrypto from "./defaultCrypto.js";
export * from "./enr.js";
export * from "./types.js";
export * from "./create.js";
export * from "./peerId.js";
export { createNodeId } from "./util.js";
42 changes: 42 additions & 0 deletions packages/enr/src/util.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,47 @@
import { toBigIntBE } from "bigint-buffer";
import { fromString, toString } from "uint8arrays";
import { NodeId } from "./types.js";

// multiaddr 8.0.0 expects an Uint8Array with internal buffer starting at 0 offset
export function toNewUint8Array(buf: Uint8Array): Uint8Array {
const arrayBuffer = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
return new Uint8Array(arrayBuffer);
}

export function toBase64url(buf: Uint8Array): string {
if (globalThis.Buffer != null) {
return globalThis.Buffer.from(buf).toString("base64url");
}
return toString(buf, "base64url");
}

export function fromBase64url(str: string): Uint8Array {
if (globalThis.Buffer != null) {
return globalThis.Buffer.from(str, "base64url");
}
return fromString(str, "base64url");
}

export function toBigInt(buf: Uint8Array): bigint {
if (globalThis.Buffer != null) {
return toBigIntBE(globalThis.Buffer.from(buf));
}

if (buf.length === 0) {
return BigInt(0);
}

return BigInt(`0x${toString(buf, "hex")}`);
}

export function createNodeId(buf: Uint8Array): NodeId {
if (buf.length !== 32) {
throw new Error("NodeId must be 32 bytes in length");
}

if (globalThis.Buffer != null) {
return globalThis.Buffer.from(buf).toString("hex");
}

return toString(buf, "hex");
}
21 changes: 7 additions & 14 deletions packages/enr/test/unit/enr.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,7 @@ describe("ENR", function () {

it("should throw decoding error - no id", () => {
try {
const txt = Buffer.from(
"656e723a2d435972595a62404b574342526c4179357a7a61445a584a42476b636e68344d486342465a6e75584e467264764a6a5830346a527a6a7a",
"hex"
).toString();
const txt = "enr:-CYrYZbKWCBRlAy5zzaDZXJBGkcnh4MHcBFZnuXNFrdvJjX04jRzjw";
ENR.decodeTxt(txt);
expect.fail("Expect error here");
} catch (err: any) {
Expand All @@ -237,23 +234,19 @@ describe("ENR", function () {

describe("ENR fuzzing testcases", () => {
it("should throw error in invalid signature", () => {
const buf = Buffer.from(
"656e723a2d4b7634514147774f54385374716d7749354c486149796d494f346f6f464b664e6b456a576130663150384f73456c67426832496a622d4772445f2d623957346b6350466377796e354845516d526371584e716470566f3168656f42683246306447356c64484f494141414141414141414143455a58526f4d704141414141414141414141505f5f5f5f5f5f5f5f5f5f676d6c6b676e5930676d6c7768424c663232534a6332566a634449314e6d73786f514a78436e4536765f7832656b67595f756f45317274777a76477934306d7139654436365866485042576749494e315a48437f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f434436410d0a",
"hex"
).toString();
const txt =
"enr:-Kv4QAGwOT8StqmwI5LHaIymIO4ooFKfNkEjWa0f1P8OsElgBh2Ijb-GrD_-b9W4kcPFcwyn5HEQmRcqXNqdpVo1heoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpAAAAAAAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQJxCnE6v_x2ekgY_uoE1rtwzvGy40mq9eD66XfHPBWgIIN1ZHCCD6A";
try {
ENR.decodeTxt(buf);
ENR.decodeTxt(txt);
} catch (e: any) {
expect(e.message).to.equal("Decoded ENR invalid signature: must be a byte array");
}
});
it("should throw error in invalid sequence number", () => {
const buf = Buffer.from(
"656e723a2d495334514b6b33ff583945717841337838334162436979416e537550444d764b353264433530486d31584744643574457951684d3356634a4c2d5062446b44673541507a5f706f76763022d48dcf992d5379716b306e616e636f4e572d656e7263713042676d6c6b676e5930676d6c77684838414141474a6332566a634449314e6d73786f514d31453579557370397638516a397476335a575843766146427672504e647a384b5049314e68576651577a494e315a4843434239410a",
"hex"
).toString();
const txt =
"enr:-IS4QKk3X9EqxA3x83AbCiyAnSuPDMvK52dC50Hm1XGDd5tEyQhM3VcJL-PbDkDg5APz_povv0-Syqk0nancoNW-enrcq0BgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQM1E5yUsp9v8Qj9tv3ZWXCvaFBvrPNdz8KPI1NhWfQWzIN1ZHCA";
try {
ENR.decodeTxt(buf);
ENR.decodeTxt(txt);
} catch (e: any) {
expect(e.message).to.equal("Decoded ENR invalid sequence number: must be a byte array");
}
Expand Down

0 comments on commit 5351f87

Please sign in to comment.