Skip to content

Commit

Permalink
fix: update precomputeText parameter type to Uint8Array
Browse files Browse the repository at this point in the history
  • Loading branch information
1 parent c433d79 commit 13ddc28
Showing 1 changed file with 29 additions and 91 deletions.
120 changes: 29 additions & 91 deletions packages/helpers/src/sha-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,104 +28,44 @@ export function padUint8ArrayWithZeros(array: Uint8Array, length: number): Uint8

export function generatePartialSHA({
body,
bodyLength,
selectorString, // String to split the body
maxRemainingBodyLength, // Maximum allowed length of the body after the selector
precomputeText,
shaCutoffIndex,
maxRemainingBodyLength,
}: {
body: Uint8Array;
bodyLength: number;
selectorString?: string;
precomputeText: Uint8Array;
shaCutoffIndex: number;
maxRemainingBodyLength: number;
}) {
let selectorIndex = 0;

if (selectorString) {
// First remove soft line breaks and get position mapping
const cleanContent = new Uint8Array(body);
const positionMap = new Map<number, number>();
let cleanPos = 0;
let i = 0;

// Build clean content and position map
while (i < body.length) {
if (i < body.length - 1 && body[i] === 61) { // '=' character
// Check for multi-byte UTF-8 sequence in QP format
const qpMatch = body.slice(i, i + 9).toString().match(/^=([0-9A-F]{2})=([0-9A-F]{2})=([0-9A-F]{2})/);
if (qpMatch) {
// Handle 3-byte UTF-8 sequence
const byte1 = parseInt(qpMatch[1], 16);
const byte2 = parseInt(qpMatch[2], 16);
const byte3 = parseInt(qpMatch[3], 16);
cleanContent[cleanPos] = byte1;
cleanContent[cleanPos + 1] = byte2;
cleanContent[cleanPos + 2] = byte3;
positionMap.set(cleanPos, i);
positionMap.set(cleanPos + 1, i + 3);
positionMap.set(cleanPos + 2, i + 6);
cleanPos += 3;
i += 9;
continue;
}

// Check for line continuation
let j = i + 1;
while (j < body.length && (body[j] === 13 || body[j] === 10 || body[j] === 32 || body[j] === 9)) {
j++;
}
if (j > i + 1) {
i = j;
continue;
}
}
positionMap.set(cleanPos, i);
cleanContent[cleanPos] = body[i];
cleanPos++;
i++;
}

// Create a view of only the valid content
const validContent = cleanContent.slice(0, cleanPos);

// Find selector in decoded content
const cleanString = new TextDecoder().decode(validContent);
selectorIndex = cleanString.indexOf(selectorString);

if (selectorIndex === -1) {
throw new Error(`SHA precompute selector "${selectorString}" not found in the body`);
}

// Map back to original position
const originalIndex = positionMap.get(selectorIndex);
if (originalIndex === undefined) {
throw new Error(`Failed to map selector position to original body`);
}

selectorIndex = originalIndex;
}): {
bodyRemaining: Uint8Array;
precomputedSha: Uint8Array;
} {
if (shaCutoffIndex < 0) {
throw new Error('Negative sha cutoff index');
}

const shaCutoffIndex = Math.floor(selectorIndex / 64) * 64;
const precomputeText = body.slice(0, shaCutoffIndex);
let bodyRemaining = body.slice(shaCutoffIndex);

const bodyRemainingLength = bodyLength - precomputeText.length;

if (bodyRemainingLength > maxRemainingBodyLength) {
throw new Error(
`Remaining body ${bodyRemainingLength} after the selector is longer than max (${maxRemainingBodyLength})`,
);
if (shaCutoffIndex > body.length) {
throw new Error('Sha cutoff index greater than body length');
}

if (bodyRemaining.length % 64 !== 0) {
if (maxRemainingBodyLength % 64 !== 0) {
throw new Error('Remaining body was not padded correctly with int64s');
}

bodyRemaining = padUint8ArrayWithZeros(bodyRemaining, maxRemainingBodyLength);
const precomputedSha = partialSha(precomputeText, shaCutoffIndex);
const buffer = new ArrayBuffer(maxRemainingBodyLength);
const bodyRemaining = new Uint8Array(buffer);

const slicedBody = new Uint8Array(body.buffer, shaCutoffIndex);
bodyRemaining.set(slicedBody);

if (bodyRemaining.length < maxRemainingBodyLength) {
return {
bodyRemaining: padUint8ArrayWithZeros(bodyRemaining, maxRemainingBodyLength),
precomputedSha: partialSha(precomputeText, shaCutoffIndex)
};
}

return {
precomputedSha,
bodyRemaining,
bodyRemainingLength,
precomputedSha: partialSha(precomputeText, shaCutoffIndex)
};
}

Expand All @@ -138,13 +78,11 @@ export function partialSha(msg: Uint8Array, msgLen: number): Uint8Array {
return shaGadget.update(msg, msgLen).cacheState();
}

// Puts an end selector, a bunch of 0s, then the length, then fill the rest with 0s.
export function sha256Pad(message: Uint8Array, maxShaBytes: number): [Uint8Array, number] {
const msgLen = message.length * 8; // bytes to bits
const msgLen = message.length * 8;
const msgLenBytes = int64toBytes(msgLen);

let res = mergeUInt8Arrays(message, int8toBytes(2 ** 7)); // Add the 1 on the end, length 505
// while ((prehash_prepad_m.length * 8 + length_in_bytes.length * 8) % 512 !== 0) {
let res = mergeUInt8Arrays(message, int8toBytes(2 ** 7));
while ((res.length * 8 + msgLenBytes.length * 8) % 512 !== 0) {
res = mergeUInt8Arrays(res, int8toBytes(0));
}
Expand Down

0 comments on commit 13ddc28

Please sign in to comment.