Check UInt8Array lengths (#126)

This commit is contained in:
Justin Traglia 2023-02-14 15:11:10 -06:00 committed by GitHub
parent 128dd3eb1f
commit 8aa78231f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 87 additions and 17 deletions

View File

@ -353,8 +353,11 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports["verifyAggregateKzgProof"] = Napi::Function::New(env, VerifyAggregateKzgProof); exports["verifyAggregateKzgProof"] = Napi::Function::New(env, VerifyAggregateKzgProof);
// Constants // Constants
exports["FIELD_ELEMENTS_PER_BLOB"] = Napi::Number::New(env, FIELD_ELEMENTS_PER_BLOB); exports["BYTES_PER_BLOB"] = Napi::Number::New(env, BYTES_PER_BLOB);
exports["BYTES_PER_COMMITMENT"] = Napi::Number::New(env, BYTES_PER_COMMITMENT);
exports["BYTES_PER_FIELD_ELEMENT"] = Napi::Number::New(env, BYTES_PER_FIELD_ELEMENT); exports["BYTES_PER_FIELD_ELEMENT"] = Napi::Number::New(env, BYTES_PER_FIELD_ELEMENT);
exports["BYTES_PER_PROOF"] = Napi::Number::New(env, BYTES_PER_PROOF);
exports["FIELD_ELEMENTS_PER_BLOB"] = Napi::Number::New(env, FIELD_ELEMENTS_PER_BLOB);
return exports; return exports;
} }

View File

@ -15,8 +15,11 @@ type SetupHandle = Object;
// The C++ native addon interface // The C++ native addon interface
type KZG = { type KZG = {
FIELD_ELEMENTS_PER_BLOB: number; BYTES_PER_BLOB: number;
BYTES_PER_COMMITMENT: number;
BYTES_PER_FIELD_ELEMENT: number; BYTES_PER_FIELD_ELEMENT: number;
BYTES_PER_PROOF: number;
FIELD_ELEMENTS_PER_BLOB: number;
loadTrustedSetup: (filePath: string) => SetupHandle; loadTrustedSetup: (filePath: string) => SetupHandle;
@ -58,8 +61,11 @@ type TrustedSetupJSON = {
roots_of_unity: string[]; roots_of_unity: string[];
}; };
export const FIELD_ELEMENTS_PER_BLOB = kzg.FIELD_ELEMENTS_PER_BLOB; export const BYTES_PER_BLOB = kzg.BYTES_PER_BLOB;
export const BYTES_PER_COMMITMENT = kzg.BYTES_PER_COMMITMENT;
export const BYTES_PER_FIELD_ELEMENT = kzg.BYTES_PER_FIELD_ELEMENT; export const BYTES_PER_FIELD_ELEMENT = kzg.BYTES_PER_FIELD_ELEMENT;
export const BYTES_PER_PROOF = kzg.BYTES_PER_PROOF;
export const FIELD_ELEMENTS_PER_BLOB = kzg.FIELD_ELEMENTS_PER_BLOB;
// Stored as internal state // Stored as internal state
let setupHandle: SetupHandle | undefined; let setupHandle: SetupHandle | undefined;
@ -71,6 +77,50 @@ function requireSetupHandle(): SetupHandle {
return setupHandle; return setupHandle;
} }
function checkBlob(blob: Blob) {
if (blob.length != BYTES_PER_BLOB) {
throw new Error(
`Expected blob to be UInt8Array of ${BYTES_PER_BLOB} bytes.`,
);
}
}
function checkBlobs(blobs: Blob[]) {
for (let blob of blobs) {
checkBlob(blob);
}
}
function checkCommitment(commitment: KZGCommitment) {
if (commitment.length != BYTES_PER_COMMITMENT) {
throw new Error(
`Expected commitment to be UInt8Array of ${BYTES_PER_COMMITMENT} bytes.`,
);
}
}
function checkCommitments(commitments: KZGCommitment[]) {
for (let commitment of commitments) {
checkCommitment(commitment);
}
}
function checkProof(proof: KZGProof) {
if (proof.length != BYTES_PER_PROOF) {
throw new Error(
`Expected proof to be UInt8Array of ${BYTES_PER_PROOF} bytes.`,
);
}
}
function checkFieldElement(field: Bytes32) {
if (field.length != BYTES_PER_FIELD_ELEMENT) {
throw new Error(
`Expected field element to be UInt8Array of ${BYTES_PER_FIELD_ELEMENT} bytes.`,
);
}
}
export async function transformTrustedSetupJSON( export async function transformTrustedSetupJSON(
filePath: string, filePath: string,
): Promise<string> { ): Promise<string> {
@ -113,14 +163,18 @@ export function freeTrustedSetup(): void {
} }
export function blobToKzgCommitment(blob: Blob): KZGCommitment { export function blobToKzgCommitment(blob: Blob): KZGCommitment {
checkBlob(blob);
return kzg.blobToKzgCommitment(blob, requireSetupHandle()); return kzg.blobToKzgCommitment(blob, requireSetupHandle());
} }
export function computeKzgProof(blob: Blob, zBytes: Bytes32): KZGProof { export function computeKzgProof(blob: Blob, zBytes: Bytes32): KZGProof {
checkBlob(blob);
checkFieldElement(zBytes);
return kzg.computeKzgProof(blob, zBytes, requireSetupHandle()); return kzg.computeKzgProof(blob, zBytes, requireSetupHandle());
} }
export function computeAggregateKzgProof(blobs: Blob[]): KZGProof { export function computeAggregateKzgProof(blobs: Blob[]): KZGProof {
checkBlobs(blobs);
return kzg.computeAggregateKzgProof(blobs, requireSetupHandle()); return kzg.computeAggregateKzgProof(blobs, requireSetupHandle());
} }
@ -130,6 +184,10 @@ export function verifyKzgProof(
yBytes: Bytes32, yBytes: Bytes32,
proofBytes: Bytes48, proofBytes: Bytes48,
): boolean { ): boolean {
checkCommitment(commitmentBytes);
checkFieldElement(zBytes);
checkFieldElement(yBytes);
checkProof(proofBytes);
return kzg.verifyKzgProof( return kzg.verifyKzgProof(
commitmentBytes, commitmentBytes,
zBytes, zBytes,
@ -144,6 +202,9 @@ export function verifyAggregateKzgProof(
commitmentsBytes: Bytes48[], commitmentsBytes: Bytes48[],
proofBytes: Bytes48, proofBytes: Bytes48,
): boolean { ): boolean {
checkBlobs(blobs);
checkCommitments(commitmentsBytes);
checkProof(proofBytes);
return kzg.verifyAggregateKzgProof( return kzg.verifyAggregateKzgProof(
blobs, blobs,
commitmentsBytes, commitmentsBytes,

View File

@ -9,8 +9,10 @@ import {
computeKzgProof, computeKzgProof,
computeAggregateKzgProof, computeAggregateKzgProof,
verifyAggregateKzgProof, verifyAggregateKzgProof,
BYTES_PER_BLOB,
BYTES_PER_COMMITMENT,
BYTES_PER_PROOF,
BYTES_PER_FIELD_ELEMENT, BYTES_PER_FIELD_ELEMENT,
FIELD_ELEMENTS_PER_BLOB,
transformTrustedSetupJSON, transformTrustedSetupJSON,
} from "./kzg"; } from "./kzg";
@ -20,13 +22,11 @@ const SETUP_FILE_PATH = existsSync(setupFileName)
? setupFileName ? setupFileName
: `../../src/${setupFileName}`; : `../../src/${setupFileName}`;
const BLOB_BYTE_COUNT = FIELD_ELEMENTS_PER_BLOB * BYTES_PER_FIELD_ELEMENT;
const MAX_TOP_BYTE = 114; const MAX_TOP_BYTE = 114;
const generateRandomBlob = () => { const generateRandomBlob = () => {
return new Uint8Array( return new Uint8Array(
randomBytes(BLOB_BYTE_COUNT).map((x, i) => { randomBytes(BYTES_PER_BLOB).map((x, i) => {
// Set the top byte to be low enough that the field element doesn't overflow the BLS modulus // Set the top byte to be low enough that the field element doesn't overflow the BLS modulus
if (x > MAX_TOP_BYTE && i % BYTES_PER_FIELD_ELEMENT == 31) { if (x > MAX_TOP_BYTE && i % BYTES_PER_FIELD_ELEMENT == 31) {
return Math.floor(Math.random() * MAX_TOP_BYTE); return Math.floor(Math.random() * MAX_TOP_BYTE);
@ -48,7 +48,7 @@ describe("C-KZG", () => {
it("computes a proof from blob", () => { it("computes a proof from blob", () => {
let blob = generateRandomBlob(); let blob = generateRandomBlob();
const zBytes = new Uint8Array(32).fill(0); const zBytes = new Uint8Array(BYTES_PER_FIELD_ELEMENT).fill(0);
computeKzgProof(blob, zBytes); computeKzgProof(blob, zBytes);
// No check, just make sure it doesn't crash. // No check, just make sure it doesn't crash.
}); });
@ -78,22 +78,22 @@ describe("C-KZG", () => {
}); });
it("verifies a valid KZG proof", () => { it("verifies a valid KZG proof", () => {
const commitment = new Uint8Array(48).fill(0); const commitment = new Uint8Array(BYTES_PER_COMMITMENT).fill(0);
commitment[0] = 0xc0; commitment[0] = 0xc0;
const z = new Uint8Array(32).fill(0); const z = new Uint8Array(BYTES_PER_FIELD_ELEMENT).fill(0);
const y = new Uint8Array(32).fill(0); const y = new Uint8Array(BYTES_PER_FIELD_ELEMENT).fill(0);
const proof = new Uint8Array(48).fill(0); const proof = new Uint8Array(BYTES_PER_PROOF).fill(0);
proof[0] = 0xc0; proof[0] = 0xc0;
expect(verifyKzgProof(commitment, z, y, proof)).toBe(true); expect(verifyKzgProof(commitment, z, y, proof)).toBe(true);
}); });
it("verifies an invalid valid KZG proof", () => { it("verifies an invalid valid KZG proof", () => {
const commitment = new Uint8Array(48).fill(0); const commitment = new Uint8Array(BYTES_PER_COMMITMENT).fill(0);
commitment[0] = 0xc0; commitment[0] = 0xc0;
const z = new Uint8Array(32).fill(1); const z = new Uint8Array(BYTES_PER_FIELD_ELEMENT).fill(1);
const y = new Uint8Array(32).fill(1); const y = new Uint8Array(BYTES_PER_FIELD_ELEMENT).fill(1);
const proof = new Uint8Array(48).fill(0); const proof = new Uint8Array(BYTES_PER_PROOF).fill(0);
proof[0] = 0xc0; proof[0] = 0xc0;
expect(verifyKzgProof(commitment, z, y, proof)).toBe(false); expect(verifyKzgProof(commitment, z, y, proof)).toBe(false);
@ -131,8 +131,14 @@ describe("C-KZG", () => {
it("throws as expected when given an argument of invalid type", () => { it("throws as expected when given an argument of invalid type", () => {
// @ts-expect-error // @ts-expect-error
expect(() => blobToKzgCommitment("wrong type")).toThrowError( expect(() => blobToKzgCommitment("wrong type")).toThrowError(
"Invalid argument type: blob. Expected UInt8Array", "Expected blob to be UInt8Array of 131072 bytes",
); );
}); });
it("throws as expected when given an argument of invalid length", () => {
expect(() =>
blobToKzgCommitment(randomBytes(BYTES_PER_BLOB - 1)),
).toThrowError("Expected blob to be UInt8Array of 131072 bytes");
});
}); });
}); });