Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lock header max length #99

Merged
merged 7 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions circuits/http/interpreter.circom
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ template HeaderFieldNameValueMatch(dataLen, nameLen, valueLen) {
signal input headerValue[valueLen];
signal input index;

// signal output value[valueLen];

// is name matches
signal headerNameMatch <== SubstringMatchWithIndex(dataLen, nameLen)(data, headerName, index);

Expand All @@ -65,6 +63,31 @@ template HeaderFieldNameValueMatch(dataLen, nameLen, valueLen) {
signal output out <== headerNameMatchAndNextByteColon * headerValueMatch;
}

// https://www.rfc-editor.org/rfc/rfc9112.html#name-field-syntax
template HeaderFieldNameValueMatchPadded(dataLen, maxNameLen, maxValueLen) {
signal input data[dataLen];
signal input headerName[maxNameLen];
signal input nameLen;
signal input headerValue[maxValueLen];
signal input valueLen;
signal input index;

// is name matchesnameLen
signal headerNameMatch <== SubstringMatchWithIndexPadded(dataLen, maxNameLen)(data, headerName, nameLen, index);

// next byte to name should be COLON
signal endOfHeaderName <== IndexSelector(dataLen)(data, index + nameLen);
signal isNextByteColon <== IsEqual()([endOfHeaderName, 58]);

signal headerNameMatchAndNextByteColon <== headerNameMatch * isNextByteColon;

// field-name: SP field-value
signal headerValueMatch <== SubstringMatchWithIndexPadded(dataLen, maxValueLen)(data, headerValue, valueLen, index + nameLen + 2);

// header name matches + header value matches
signal output out <== headerNameMatchAndNextByteColon * headerValueMatch;
}

// https://www.rfc-editor.org/rfc/rfc9112.html#name-field-syntax
template HeaderFieldNameMatch(dataLen, nameLen) {
signal input data[dataLen];
Expand Down
67 changes: 42 additions & 25 deletions circuits/http/nivc/lock_header.circom
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ include "../interpreter.circom";
include "../../utils/array.circom";

// TODO: should use a MAX_HEADER_NAME_LENGTH and a MAX_HEADER_VALUE_LENGTH
template LockHeader(DATA_BYTES, MAX_STACK_HEIGHT, HEADER_NAME_LENGTH, HEADER_VALUE_LENGTH) {
template LockHeader(DATA_BYTES, MAX_STACK_HEIGHT, MAX_HEADER_NAME_LENGTH, MAX_HEADER_VALUE_LENGTH) {
// ------------------------------------------------------------------------------------------------------------------ //
// ~~ Set sizes at compile time ~~
// Total number of variables in the parser for each byte of data
Expand All @@ -23,32 +23,34 @@ template LockHeader(DATA_BYTES, MAX_STACK_HEIGHT, HEADER_NAME_LENGTH, HEADER_VAL
// ------------------------------------------------------------------------------------------------------------------ //
// ~ Unravel from previous NIVC step ~
// Read in from previous NIVC step (HttpParseAndLockStartLine or HTTPLockHeader)
signal input step_in[TOTAL_BYTES_ACROSS_NIVC];
signal input step_in[TOTAL_BYTES_ACROSS_NIVC];
signal output step_out[TOTAL_BYTES_ACROSS_NIVC];

signal data[DATA_BYTES];
for (var i = 0 ; i < DATA_BYTES ; i++) {
data[i] <== step_in[i];
}
signal httpParserState[DATA_BYTES * 5];
for (var i = 0 ; i < DATA_BYTES * 5 ; i++) {
httpParserState[i] <== step_in[DATA_BYTES + i];
}

// TODO: Better naming for these variables
signal input header[HEADER_NAME_LENGTH];
signal input value[HEADER_VALUE_LENGTH];

component headerNameLocation = FirstStringMatch(DATA_BYTES, HEADER_NAME_LENGTH);
headerNameLocation.data <== data;
headerNameLocation.key <== header;
signal input header[MAX_HEADER_NAME_LENGTH];
signal input headerNameLength;
signal input value[MAX_HEADER_VALUE_LENGTH];
signal input headerValueLength;

component headerFieldNameValueMatch;
headerFieldNameValueMatch = HeaderFieldNameValueMatch(DATA_BYTES, HEADER_NAME_LENGTH, HEADER_VALUE_LENGTH);
headerFieldNameValueMatch.data <== data;
headerFieldNameValueMatch.headerName <== header;
headerFieldNameValueMatch.headerValue <== value;
headerFieldNameValueMatch.index <== headerNameLocation.position;
// find header location
signal headerNameLocation <== FirstStringMatch(DATA_BYTES, MAX_HEADER_NAME_LENGTH)(data, header);

// TODO: Make this assert we are parsing header!!!
// This is the assertion that we have locked down the correct header
headerFieldNameValueMatch.out === 1;
signal headerFieldNameValueMatch <== HeaderFieldNameValueMatchPadded(DATA_BYTES, MAX_HEADER_NAME_LENGTH, MAX_HEADER_VALUE_LENGTH)(data, header, headerNameLength, value, headerValueLength, headerNameLocation);
headerFieldNameValueMatch === 1;

// parser state should be parsing header
signal isParsingHeader <== IndexSelector(DATA_BYTES * 5)(httpParserState, headerNameLocation * 5 + 1);
isParsingHeader === 1;

// ------------------------------------------------------------------------------------------------------------------ //
// ~ Write out to next NIVC step
Expand All @@ -63,27 +65,42 @@ template LockHeader(DATA_BYTES, MAX_STACK_HEIGHT, HEADER_NAME_LENGTH, HEADER_VAL
step_out[DATA_BYTES + i * 5 + 3] <== step_in[DATA_BYTES + i * 5 + 3];
step_out[DATA_BYTES + i * 5 + 4] <== step_in[DATA_BYTES + i * 5 + 4];
}
// Pad remaining with zeros
// Pad remaining with zeros
for (var i = TOTAL_BYTES_HTTP_STATE ; i < TOTAL_BYTES_ACROSS_NIVC ; i++ ) {
step_out[i] <== 0;
}
}

// TODO: Handrolled template that I haven't tested YOLO.
template FirstStringMatch(dataLen, keyLen) {
template FirstStringMatch(dataLen, maxKeyLen) {
signal input data[dataLen];
signal input key[keyLen];
signal input key[maxKeyLen];
signal output position;

signal paddedData[dataLen + maxKeyLen];
for (var i = 0 ; i < dataLen ; i++) {
paddedData[i] <== data[i];
}
for (var i = 0 ; i < maxKeyLen ; i++) {
paddedData[dataLen + i] <== 0;
}

var matched = 0;
var counter = 0;
component stringMatch[dataLen - keyLen];
component hasMatched[dataLen - keyLen];
for (var idx = 0 ; idx < dataLen - keyLen ; idx++) {
stringMatch[idx] = IsEqualArray(keyLen);
component stringMatch[dataLen];
component hasMatched[dataLen];
signal isKeyOutOfBounds[maxKeyLen];
signal isFirstMatchAndInsideBound[dataLen * maxKeyLen];
for (var i = 0 ; i < maxKeyLen ; i++) {
isKeyOutOfBounds[i] <== IsZero()(key[i]);
}

for (var idx = 0 ; idx < dataLen ; idx++) {
stringMatch[idx] = IsEqualArray(maxKeyLen);
stringMatch[idx].in[0] <== key;
for (var key_idx = 0 ; key_idx < keyLen ; key_idx++) {
stringMatch[idx].in[1][key_idx] <== data[idx + key_idx] * (1 - matched);
for (var key_idx = 0 ; key_idx < maxKeyLen ; key_idx++) {
isFirstMatchAndInsideBound[idx * maxKeyLen + key_idx] <== (1 - matched) * (1 - isKeyOutOfBounds[key_idx]);
stringMatch[idx].in[1][key_idx] <== paddedData[idx + key_idx] * isFirstMatchAndInsideBound[idx * maxKeyLen + key_idx];
}
hasMatched[idx] = IsEqual();
hasMatched[idx].in <== [stringMatch[idx].out, 1];
Expand Down
2 changes: 2 additions & 0 deletions circuits/json/interpreter.circom
Original file line number Diff line number Diff line change
Expand Up @@ -415,8 +415,10 @@ template MatchPaddedKey(n) {
isQuote[i] <== IsEqual()([in[1][i], 34]);
endOfKeyAccum[i+1] <== endOfKeyAccum[i] + isEndOfKey[i] * isQuote[i];

// TODO: might not be right to check for zero, instead check for -1?
isPaddedElement[i] = IsZero();
isPaddedElement[i].in <== in[0][i];

equalComponent[i] = IsEqual();
equalComponent[i].in[0] <== in[0][i];
equalComponent[i].in[1] <== in[1][i] * (1-isPaddedElement[i].out);
Expand Down
Loading