From a4410da48ce5695a0dfc502de5d381212fedad68 Mon Sep 17 00:00:00 2001 From: hyeonLewis Date: Thu, 7 Mar 2024 12:43:14 +0900 Subject: [PATCH 1/3] Update readme --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index cdcb70a..1fba7e9 100644 --- a/README.md +++ b/README.md @@ -225,8 +225,6 @@ If user wallet is `ghost` wallet, user can't recover it, so delete it and create const iss: string[] = []; const sub: string[] = []; const salts: string[] = []; - const confUrls: string[] = []; - const jwkUrls: string[] = []; const jwks: RsaJsonWebKey[] = []; const proofAndPubSigs: any[] = []; @@ -239,8 +237,6 @@ If user wallet is `ghost` wallet, user can't recover it, so delete it and create sub.push(subTemp); salts.push(await calcSalt(subTemp)); const provider = getProviderNameFromIss(issTemp); - confUrls.push(OIDCProviders.find(p => p.name === provider.toLowerCase())?.confUrl as string); - jwkUrls.push(OIDCProviders.find(p => p.name === provider.toLowerCase())?.jwkUrl as string); jwks.push((await getJWKs(provider, header.kid)) as RsaJsonWebKey); // Prepare zk proof From 7beabea54ae816f612f06ba83fe58aa486c58fe8 Mon Sep 17 00:00:00 2001 From: hyeonLewis Date: Thu, 7 Mar 2024 12:43:24 +0900 Subject: [PATCH 2/3] Fix pubsigs process --- src/circuit-helpers.ts | 113 +++++++++++++++++++++++++++-------------- src/zkauth-circuit.ts | 37 +++++++------- 2 files changed, 92 insertions(+), 58 deletions(-) diff --git a/src/circuit-helpers.ts b/src/circuit-helpers.ts index 2dbe11c..c6a1ff2 100644 --- a/src/circuit-helpers.ts +++ b/src/circuit-helpers.ts @@ -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) => { @@ -10,6 +49,23 @@ 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)]); @@ -17,56 +73,37 @@ function stretch(buf: Buffer, len: number): Buffer { 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); diff --git a/src/zkauth-circuit.ts b/src/zkauth-circuit.ts index ec38b5d..47e401e 100644 --- a/src/zkauth-circuit.ts +++ b/src/zkauth-circuit.ts @@ -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 @@ -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[] { @@ -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, @@ -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); }, }; From 3f4ad1ea78ecd7c8ef3fab61674c92aea5d7ee43 Mon Sep 17 00:00:00 2001 From: hyeonLewis Date: Fri, 8 Mar 2024 10:46:09 +0900 Subject: [PATCH 3/3] Use toUints for generateModPubSig --- src/authBuilder.ts | 4 ++-- src/circuit-helpers.ts | 20 -------------------- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/src/authBuilder.ts b/src/authBuilder.ts index a06abf1..e7b3c21 100644 --- a/src/authBuilder.ts +++ b/src/authBuilder.ts @@ -9,7 +9,7 @@ import { IJwtProvider } from "./jwtProvider"; import { typeDataRecovery } from "./samples"; import { sampleProofA, sampleProofB, sampleProofC } from "./samples/constants"; -import { ZkauthJwtV02, string2Uints } from "."; +import { ZkauthJwtV02, toUints } from "."; export interface typeDataArgs { verifyingContract: string; @@ -280,7 +280,7 @@ export function calcGuardianId(subHash: string, guardian: string) { } export const generateModPubSig = (modBytes: string | Buffer) => { - return string2Uints(modBytes, ZkauthJwtV02.maxPubLen); + return toUints(Buffer.from(modBytes), ZkauthJwtV02.maxPubLen); }; export const generateJwtPubSig = (jwt: string) => { diff --git a/src/circuit-helpers.ts b/src/circuit-helpers.ts index c6a1ff2..c481a89 100644 --- a/src/circuit-helpers.ts +++ b/src/circuit-helpers.ts @@ -41,14 +41,6 @@ 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) => { - result.push(BigInt("0x" + Buffer.from(slice).toString("hex")).toString()); - }); - return result; -} - export function toUints(buffer: Buffer, maxLen: number): string[] { const stretched = stretch(buffer, maxLen); const result: string[] = []; @@ -98,15 +90,3 @@ 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 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); - - return bufferToUints(paddedStr, numBytes); -}