Skip to content
This repository has been archived by the owner on Aug 16, 2024. It is now read-only.

Commit

Permalink
Fix pubsigs process
Browse files Browse the repository at this point in the history
  • Loading branch information
hyeonLewis committed Mar 7, 2024
1 parent a4410da commit 7beabea
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 58 deletions.
113 changes: 75 additions & 38 deletions src/circuit-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,46 @@
import { Buffer } from "buffer";

import base64url from "base64url";
import _ from "lodash";

const numBits = 248;
const numBytes = numBits / 8;

// fromXXX: convert from XXX type to buffer
// toXXX: convert from buffer to XXX type

export function fromASCII(str: string): Buffer {
return Buffer.from(str, "ascii");
}

export function fromBase64(str: string): Buffer {
return base64url.toBuffer(str);
}

export function fromHex(str: string): Buffer {
return Buffer.from(str, "hex");
}

export function fromUints(arr: string[]): Buffer {
return Buffer.concat(arr.map((x) => Buffer.from(BigInt(x).toString(16), "hex")));
}

export function fromQwords(arr: string[]): Buffer {
return Buffer.from(_.reverse(_.map(arr, (x: any) => BigInt(x).toString(16).padStart(16, "0"))).join(""), "hex");
}

export function toASCII(buffer: Buffer): string {
return buffer.toString("ascii");
}

export function toBase64(buffer: Buffer): string {
return base64url(buffer);
}

export function toHex(buffer: Buffer): string {
return buffer.toString("hex");
}

function bufferToUints(buffer: Buffer, chunkLen: number): string[] {
const result: string[] = [];
_.map(_.chunk(buffer, chunkLen), (slice: Buffer) => {
Expand All @@ -10,63 +49,61 @@ function bufferToUints(buffer: Buffer, chunkLen: number): string[] {
return result;
}

export function toUints(buffer: Buffer, maxLen: number): string[] {
const stretched = stretch(buffer, maxLen);
const result: string[] = [];
_.map(_.chunk(stretched, numBytes), (slice: Buffer) => {
result.push(BigInt("0x" + Buffer.from(slice).toString("hex")).toString());
});
return result;
}

export function toQwords(buf: string | Buffer): string[] {
const bi = BigInt("0x" + Buffer.from(buf).toString("hex"));
return _.times(32, (i: any) => ((bi >> BigInt(i * 64)) & (2n ** 64n - 1n)).toString(10));
}

// Misc helpers

// stretch: pad the buffer to the given length
function stretch(buf: Buffer, len: number): Buffer {
if (buf.length < len) {
return Buffer.concat([buf, Buffer.alloc(len - buf.length, 0)]);
}
return buf;
}

export function string2Uints(str: string | Buffer, maxLen: number): string[] {
const numBits = 248;
const numBytes = numBits / 8;

let paddedStr = Buffer.from(str);

// Pad the string to the max length
paddedStr = stretch(paddedStr, maxLen);
// sha256Pad: add SHA256 padding
// [ string ][ 80 ][ 00..00 ][ len ]
export function sha256Pad(str: string | Buffer): Buffer {
let padded = Buffer.from(str);
const blockSize = 64; // Block size in bytes

return bufferToUints(paddedStr, numBytes);
}
padded = Buffer.concat([padded, Buffer.from([0x80])]); // Append a single '1' bit

export function uints2String(arr: string[]): string {
return arr.map((x) => Buffer.from(BigInt(x).toString(16), "hex").toString("ascii")).join("");
}
const zeroBits = Buffer.alloc(
(padded.length + 8) % blockSize == 0 ? 0 : blockSize - ((padded.length + 8) % blockSize)
);
padded = Buffer.concat([padded, zeroBits]); // Append the '0' bits

export function uints2Buffer(arr: string[]): Buffer {
return Buffer.concat(arr.map((x) => Buffer.from(BigInt(x).toString(16), "hex")));
}
const lengthBits = Buffer.alloc(8);
lengthBits.writeBigInt64BE(BigInt(str.length * 8), 0);
padded = Buffer.concat([padded, lengthBits]); // Append the length

export function string2Qwords(str: string): string[] {
const bi = str.startsWith("0x") ? BigInt(str) : BigInt("0x" + str);
return _.times(32, (i: any) => ((bi >> BigInt(i * 64)) & (2n ** 64n - 1n)).toString(10));
return padded;
}

export function qwords2String(arr: string[]): string {
return Buffer.from(
_.reverse(_.map(arr, (x: any) => BigInt(x).toString(16).padStart(16, "0"))).join(""),
"hex"
).toString("base64");
// sha256BlockLen: calculate the number of SHA256b blocks for given buffer
export function sha256BlockLen(buf: string | Buffer): number {
// 1 is for 0x80, 8 is for length bits (64 bits)
return Math.ceil((buf.length + 1 + 8) / 64);
}

export function string2UintsSha256Padded(str: string | Buffer, maxLen: number): string[] {
export function string2Uints(str: string | Buffer, maxLen: number): string[] {
const numBits = 248;
const numBytes = numBits / 8;

// Add the SHA256 padding
let paddedStr = Buffer.from(str);
const blockSize = 64; // Block size in bytes

paddedStr = Buffer.concat([paddedStr, Buffer.from([0x80])]); // Append a single '1' bit

const zeroBits = Buffer.alloc(
(paddedStr.length + 8) % blockSize == 0 ? 0 : blockSize - ((paddedStr.length + 8) % blockSize)
);
paddedStr = Buffer.concat([paddedStr, zeroBits]); // Append the '0' bits

const lengthBits = Buffer.alloc(8);
lengthBits.writeBigInt64BE(BigInt(str.length * 8), 0);
paddedStr = Buffer.concat([paddedStr, lengthBits]); // Append the length

// Pad the string to the max length
paddedStr = stretch(paddedStr, maxLen);
Expand Down
37 changes: 17 additions & 20 deletions src/zkauth-circuit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Buffer } from "buffer";

import base64url from "base64url";

import { string2Uints, string2UintsSha256Padded, uints2String, uints2Buffer } from "./circuit-helpers";
import * as helper from "./circuit-helpers";

export const ZkauthJwtV02 = {
maxSaltedSubLen: 341, // 31 * 11
Expand All @@ -25,14 +25,13 @@ export const ZkauthJwtV02 = {

const payOff = header.length + 1; // position in base64-encoded JWT
const payLen = payload.length;
const pay = base64url.decode(payload);
// toString('ascii') may result in some unicode characters to be misinterpreted
const pay = base64url.toBuffer(payload).toString("ascii");
const payObject = JSON.parse(base64url.decode(payload));
console.assert(signedJwt.substring(payOff, payOff + payLen) == payload, "payOff");

// [ string ][ 80 ][ 00..00 ][ len ]
const jwtUints = string2UintsSha256Padded(jwt, maxLen);
// 1 is for 0x80, 8 is for length bits (64 bits)
const jwtBlocks = Math.ceil((jwt.length + 1 + 8) / 64);
const jwtUints = helper.toUints(helper.sha256Pad(jwt), maxLen);
const jwtBlocks = helper.sha256BlockLen(jwt);

// Claims
function claimPos(jwt: string, name: string): number[] {
Expand Down Expand Up @@ -67,13 +66,12 @@ export const ZkauthJwtV02 = {
const sub = '"' + payObject["sub"] + '"';
// salt is hex string and sub is ASCII string
const saltedSub = Buffer.concat([Buffer.from(salt, "hex"), Buffer.from(sub, "ascii")]);
const saltedSubUints = string2UintsSha256Padded(saltedSub, maxSaltedSubLen);
// 1 is for 0x80, 8 is for length bits (64 bits)
const saltedSubBlocks = Math.ceil((saltedSub.length + 1 + 8) / 64);
const saltedSubUints = helper.toUints(helper.sha256Pad(saltedSub), maxSaltedSubLen);
const saltedSubBlocks = helper.sha256BlockLen(saltedSub);

// Signature
const sigUints = string2Uints(base64url.toBuffer(signature), maxSigLen);
const pubUints = string2Uints(base64url.toBuffer(pub), maxPubLen);
const sigUints = helper.toUints(helper.fromBase64(signature), maxSigLen);
const pubUints = helper.toUints(helper.fromBase64(pub), maxPubLen);

return {
jwtUints,
Expand All @@ -94,26 +92,25 @@ export const ZkauthJwtV02 = {
};
},
process_output: function (pubsig: string[]) {
const iss = uints2String(pubsig.slice(0, 9));
const iss = helper.toASCII(helper.fromUints(pubsig.slice(0, 9)));
const issLen = pubsig[9];
const aud = uints2String(pubsig.slice(10, 19));
const aud = helper.toASCII(helper.fromUints(pubsig.slice(10, 19)));
const audLen = pubsig[19];
const iat = uints2String(pubsig.slice(20, 29));
const iat = helper.toASCII(helper.fromUints(pubsig.slice(20, 29)));
const iatLen = pubsig[29];
const exp = uints2String(pubsig.slice(30, 39));
const exp = helper.toASCII(helper.fromUints(pubsig.slice(30, 39)));
const expLen = pubsig[39];
const nonce = uints2String(pubsig.slice(40, 49));
const nonce = helper.toASCII(helper.fromUints(pubsig.slice(40, 49)));
const nonceLen = pubsig[49];
const hSub =
BigInt(pubsig[50]).toString(16).padStart(32, "0") + BigInt(pubsig[51]).toString(16).padStart(32, "0");
const pub = base64url(uints2Buffer(pubsig.slice(52, 61)).subarray(0, 256));
const hSub = "0x" + helper.toHex(helper.fromUints(pubsig.slice(50, 52)).subarray(0, 32));
const pub = helper.toBase64(helper.fromUints(pubsig.slice(52, 61)).subarray(0, 256));

console.log("iss =", iss, ", issLen =", issLen);
console.log("aud =", aud, ", audLen =", audLen);
console.log("iat =", iat, ", iatLen =", iatLen);
console.log("exp =", exp, ", expLen =", expLen);
console.log("nonce =", nonce, ", nonceLen =", nonceLen);
console.log("hSub =", "0x" + hSub);
console.log("hSub =", hSub);
console.log("pub =", pub);
},
};

0 comments on commit 7beabea

Please sign in to comment.