diff --git a/src/hint_processor/blake2s_hash.zig b/src/hint_processor/blake2s_hash.zig index a4520ed2..82d3387f 100644 --- a/src/hint_processor/blake2s_hash.zig +++ b/src/hint_processor/blake2s_hash.zig @@ -1,6 +1,7 @@ const std = @import("std"); const testing = std.testing; const Allocator = std.mem.Allocator; + pub const IV = [8]u32{ 0x6A09E667, 0xBB67AE85, @@ -11,6 +12,7 @@ pub const IV = [8]u32{ 0x1F83D9AB, 0x5BE0CD19, }; + const SIGMA = [_][16]usize{ [_]usize{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, [_]usize{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, @@ -23,21 +25,24 @@ const SIGMA = [_][16]usize{ [_]usize{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, [_]usize{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, }; -fn right_rot(value: u32, comptime n: u5) u32 { + +fn rightRot(value: u32, comptime n: u5) u32 { return (value >> n) | (value << (@as(u6, 32) - @as(u6, n))); } + fn mix(a: u32, b: u32, c: u32, d: u32, m0: u32, m1: u32) struct { u32, u32, u32, u32 } { var a1 = a +% b +% m0; - var d1 = right_rot(d ^ a1, 16); + var d1 = rightRot(d ^ a1, 16); var c1 = c +% d1; - var b1 = right_rot(b ^ c1, 12); + var b1 = rightRot(b ^ c1, 12); a1 = a1 +% b1 +% m1; - d1 = right_rot(d1 ^ a1, 8); + d1 = rightRot(d1 ^ a1, 8); c1 = c1 +% d1; - b1 = right_rot(b1 ^ c1, 7); + b1 = rightRot(b1 ^ c1, 7); return .{ a1, b1, c1, d1 }; } -fn blake_round(state: std.ArrayList(u32), message: [16]u32, sigma: [16]usize) void { + +fn blakeRound(state: std.ArrayList(u32), message: [16]u32, sigma: [16]usize) void { const state_items = state.items; state_items[0], state_items[4], state_items[8], state_items[12] = mix( state_items[0], @@ -106,7 +111,8 @@ fn blake_round(state: std.ArrayList(u32), message: [16]u32, sigma: [16]usize) vo message[sigma[15]], ); } -pub fn blake2s_compress( + +pub fn blake2sCompress( allocator: Allocator, h: [8]u32, message: [16]u32, @@ -117,16 +123,20 @@ pub fn blake2s_compress( ) !std.ArrayList(u32) { var state = std.ArrayList(u32).init(allocator); defer state.deinit(); + try state.appendSlice(&h); try state.appendSlice(&[8]u32{ IV[0], IV[1], IV[2], IV[3], IV[4] ^ t0, IV[5] ^ t1, IV[6] ^ f0, IV[7] ^ f1 }); for (SIGMA) |sigma| { - blake_round(state, message, sigma); + blakeRound(state, message, sigma); } var new_state = std.ArrayList(u32).init(allocator); + errdefer new_state.deinit(); + for (0..8) |i| { try new_state.append(h[i] ^ state.items[i] ^ state.items[8 + i]); } + return new_state; } @@ -136,7 +146,7 @@ test "blake2s compress: test a" { 1541459225, }; const message = [_]u32{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - const new_state = try blake2s_compress(testing.allocator, h, message, 2, 0, 4294967295, 0); + const new_state = try blake2sCompress(testing.allocator, h, message, 2, 0, 4294967295, 0); defer new_state.deinit(); try std.testing.expectEqualSlices(u32, &[8]u32{ 412110711, 3234706100, 3894970767, 982912411, 937789635, 742982576, 3942558313, 1407547065 }, new_state.items); } @@ -147,7 +157,7 @@ test "blake2s compress: test b" { 1541459225, }; const message = [_]u32{ 456710651, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - const new_state = try blake2s_compress(testing.allocator, h, message, 2, 0, 4294967295, 0); + const new_state = try blake2sCompress(testing.allocator, h, message, 2, 0, 4294967295, 0); defer new_state.deinit(); try std.testing.expectEqualSlices(u32, &[8]u32{ 1061041453, 3663967611, 2158760218, 836165556, 3696892209, 3887053585, 2675134684, 2201582556 }, new_state.items); } @@ -160,7 +170,7 @@ test "blake2s compress: test c" { const message = [_]u32{ 1819043144, 1870078063, 6581362, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; - const new_state = try blake2s_compress(testing.allocator, h, message, 9, 0, 4294967295, 0); + const new_state = try blake2sCompress(testing.allocator, h, message, 9, 0, 4294967295, 0); defer new_state.deinit(); try std.testing.expectEqualSlices(u32, &[8]u32{ 939893662, 3935214984, 1704819782, 3912812968, 4211807320, 3760278243, 674188535, 2642110762 }, new_state.items); } @@ -174,7 +184,7 @@ test "blake2s compress: test d" { 1819043144, 1870078063, 6581362, 274628678, 715791845, 175498643, 871587583, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; - const new_state = try blake2s_compress(testing.allocator, h, message, 28, 0, 4294967295, 0); + const new_state = try blake2sCompress(testing.allocator, h, message, 28, 0, 4294967295, 0); defer new_state.deinit(); try std.testing.expectEqualSlices(u32, &[8]u32{ 3980510537, 3982966407, 1593299263, 2666882356, 3288094120, 2682988286, 1666615862, 378086837 }, new_state.items); } @@ -188,7 +198,7 @@ test "blake2s compress: test e" { 1819043144, 1870078063, 6581362, 274628678, 715791845, 175498643, 871587583, 635963558, 557369694, 1576875962, 215769785, 0, 0, 0, 0, 0, }; - const new_state = try blake2s_compress(testing.allocator, h, message, 44, 0, 4294967295, 0); + const new_state = try blake2sCompress(testing.allocator, h, message, 44, 0, 4294967295, 0); defer new_state.deinit(); try std.testing.expectEqualSlices(u32, &[8]u32{ 3251785223, 1946079609, 2665255093, 3508191500, 3630835628, 3067307230, 3623370123, 656151356 }, new_state.items); } @@ -202,7 +212,7 @@ test "blake2s compress: test f" { 1819043144, 1870078063, 6581362, 274628678, 715791845, 175498643, 871587583, 635963558, 557369694, 1576875962, 215769785, 152379578, 585849303, 764739320, 437383930, 74833930, }; - const new_state = try blake2s_compress(testing.allocator, h, message, 64, 0, 4294967295, 0); + const new_state = try blake2sCompress(testing.allocator, h, message, 64, 0, 4294967295, 0); defer new_state.deinit(); try std.testing.expectEqualSlices(u32, &[8]u32{ 2593218707, 3238077801, 914875393, 3462286058, 4028447058, 3174734057, 2001070146, 3741410512 }, new_state.items); } @@ -216,7 +226,7 @@ test "blake2s compress: test g" { 11563522, 43535528, 653255322, 274628678, 73471943, 17549868, 87158958, 635963558, 343656565, 1576875962, 215769785, 152379578, 585849303, 76473202, 437253230, 74833930, }; - const new_state = try blake2s_compress(testing.allocator, h, message, 64, 0, 4294967295, 0); + const new_state = try blake2sCompress(testing.allocator, h, message, 64, 0, 4294967295, 0); defer new_state.deinit(); try std.testing.expectEqualSlices(u32, &[8]u32{ 3496615692, 3252241979, 3771521549, 2125493093, 3240605752, 2885407061, 3962009872, 3845288240 }, new_state.items); } diff --git a/src/hint_processor/blake2s_utils.zig b/src/hint_processor/blake2s_utils.zig index 7f74c424..a59b71b9 100644 --- a/src/hint_processor/blake2s_utils.zig +++ b/src/hint_processor/blake2s_utils.zig @@ -20,7 +20,8 @@ const CairoVMError = @import("../vm/error.zig").CairoVMError; const MemoryError = @import("../vm/error.zig").MemoryError; const blake2s_hash = @import("blake2s_hash.zig"); const builtin_hints = @import("builtin_hint_codes.zig"); -fn feltToU32(felt: Felt252) MathError!u32 { + +pub fn feltToU32(felt: Felt252) MathError!u32 { const u256_val = felt.toInteger(); if (u256_val > 0xFFFFFFFF) { return MathError.Felt252ToU32Conversion; @@ -35,14 +36,18 @@ fn getFixedSizeU32Array(comptime N: usize, h_range: std.ArrayList(Felt252)) ![N] } return result; } + fn getMaybeRelocArrayFromU32Array(allocator: Allocator, h_range: std.ArrayList(u32)) !std.ArrayList(MaybeRelocatable) { var result = std.ArrayList(MaybeRelocatable).init(allocator); + errdefer result.deinit(); + for (h_range.items) |h| { - const felt = Felt252.fromInt(u32, h); - try result.append(MaybeRelocatable.fromFelt(felt)); + try result.append(MaybeRelocatable.fromInt(u32, h)); } + return result; } + fn computeBlake2sFunc(allocator: Allocator, vm: *CairoVM, output_ptr: Relocatable) !void { const h_felt_range = try vm.getFeltRange(try output_ptr.subUint(26), 8); defer h_felt_range.deinit(); @@ -55,7 +60,7 @@ fn computeBlake2sFunc(allocator: Allocator, vm: *CairoVM, output_ptr: Relocatabl const t = try feltToU32(try vm.getFelt(try output_ptr.subUint(2))); const f = try feltToU32(try vm.getFelt(try output_ptr.subUint(1))); - const h_range = try blake2s_hash.blake2s_compress(allocator, h, message, t, 0, f, 0); + const h_range = try blake2s_hash.blake2sCompress(allocator, h, message, t, 0, f, 0); defer h_range.deinit(); var new_state = try getMaybeRelocArrayFromU32Array(allocator, h_range); defer new_state.deinit(); @@ -71,7 +76,7 @@ pub fn blake2sCompute(allocator: Allocator, vm: *CairoVM, ids_data: std.StringHa try computeBlake2sFunc(allocator, vm, output); } -// /* Implements Hint: +// Implements Hint: // # Add dummy pairs of input and output. // from starkware.cairo.common.cairo_blake2s.blake2s_utils import IV, blake2s_compress @@ -99,7 +104,7 @@ pub fn blake2sFinalize(allocator: Allocator, vm: *CairoVM, ids_data: std.StringH const message: [16]u32 = .{0} ** 16; var modified_iv = blake2s_hash.IV; modified_iv[0] = blake2s_hash.IV[0] ^ 0x01010020; - const output = try blake2s_hash.blake2s_compress(allocator, modified_iv, message, 0, 0, 0xffffffff, 0); + const output = try blake2s_hash.blake2sCompress(allocator, modified_iv, message, 0, 0, 0xffffffff, 0); defer output.deinit(); var full_padding = std.ArrayList(u32).init(allocator); defer full_padding.deinit(); @@ -113,7 +118,7 @@ pub fn blake2sFinalize(allocator: Allocator, vm: *CairoVM, ids_data: std.StringH defer data.deinit(); _ = try vm.loadData(blake2s_ptr_end, &data); } -// /* Implements Hint: +// Implements Hint: // # Add dummy pairs of input and output. // from starkware.cairo.common.cairo_blake2s.blake2s_utils import IV, blake2s_compress @@ -134,18 +139,19 @@ pub fn blake2sFinalize(allocator: Allocator, vm: *CairoVM, ids_data: std.StringH // ) // padding = (message + modified_iv + [0, 0xffffffff] + output) * (_n_packed_instances - 1) // segments.write_arg(ids.blake2s_ptr_end, padding) -// */ +// pub fn blake2sFinalizeV3(allocator: Allocator, vm: *CairoVM, ids_data: std.StringHashMap(HintReference), ap_tracking: ApTracking) !void { const N_PACKED_INSTANCES = 7; const blake2s_ptr_end = try hint_utils.getPtrFromVarName("blake2s_ptr_end", vm, ids_data, ap_tracking); const message: [16]u32 = .{0} ** 16; var modified_iv = blake2s_hash.IV; modified_iv[0] = blake2s_hash.IV[0] ^ 0x01010020; - const output = try blake2s_hash.blake2s_compress(allocator, modified_iv, message, 0, 0, 0xffffffff, 0); + const output = try blake2s_hash.blake2sCompress(allocator, modified_iv, message, 0, 0, 0xffffffff, 0); defer output.deinit(); var padding = std.ArrayList(u32).init(allocator); defer padding.deinit(); + try padding.appendSlice(&message); try padding.appendSlice(&modified_iv); try padding.appendSlice(&.{ 0, 0xffffffff }); @@ -153,11 +159,14 @@ pub fn blake2sFinalizeV3(allocator: Allocator, vm: *CairoVM, ids_data: std.Strin var full_padding = std.ArrayList(u32).init(allocator); defer full_padding.deinit(); + for (N_PACKED_INSTANCES - 1) |_| { - try full_padding.appendSlice(&padding); + try full_padding.appendSlice(padding.items); } + var data = try getMaybeRelocArrayFromU32Array(allocator, full_padding); defer data.deinit(); + _ = try vm.loadData(blake2s_ptr_end, &data); } // /* Implements Hint: @@ -197,12 +206,11 @@ pub fn blake2sAddUnit256(_: Allocator, vm: *CairoVM, ids_data: std.StringHashMap _ = try vm.loadData(data_ptr, &data); } -// /* Implements Hint: +// Implements Hint: // B = 32 // MASK = 2 ** 32 - 1 // segments.write_arg(ids.data, [(ids.high >> (B * (3 - i))) & MASK for i in range(4)]) // segments.write_arg(ids.data + 4, [(ids.low >> (B * (3 - i))) & MASK for i in range(4)]) -// */ pub fn blake2sAddUnit256BigEnd(_: Allocator, vm: *CairoVM, ids_data: std.StringHashMap(HintReference), ap_tracking: ApTracking) !void { const data_ptr = try hint_utils.getPtrFromVarName("data", vm, ids_data, ap_tracking); const low_addr = try hint_utils.getRelocatableFromVarName("low", vm, ids_data, ap_tracking); @@ -238,7 +246,7 @@ pub fn blake2sAddUnit256BigEnd(_: Allocator, vm: *CairoVM, ids_data: std.StringH _ = try vm.loadData(data_ptr, &bigend_data); } -// /* Implements Hint: +// Implements Hint: // %{ // from starkware.cairo.common.cairo_blake2s.blake2s_utils import IV, blake2s_compress @@ -258,21 +266,29 @@ pub fn blake2sAddUnit256BigEnd(_: Allocator, vm: *CairoVM, ids_data: std.StringH // %} // Note: This hint belongs to the blake2s lib in cario_examples -// */ - -pub fn exampleBlake2SCompress(allocator: Allocator, vm: *CairoVM, ids_data: std.StringHashMap(HintReference), ap_tracking: ApTracking) !void { +// +pub fn exampleBlake2SCompress( + allocator: Allocator, + vm: *CairoVM, + ids_data: std.StringHashMap(HintReference), + ap_tracking: ApTracking, +) !void { const blake2s_start = try hint_utils.getPtrFromVarName("blake2s_start", vm, ids_data, ap_tracking); const output = try hint_utils.getPtrFromVarName("output", vm, ids_data, ap_tracking); const n_bytes_felt = try hint_utils.getIntegerFromVarName("n_bytes", vm, ids_data, ap_tracking); const n_bytes = try feltToU32(n_bytes_felt); const message = try getFixedSizeU32Array(16, try vm.getFeltRange(blake2s_start, 16)); + var modified_iv = blake2s_hash.IV; modified_iv[0] = blake2s_hash.IV[0] ^ 0x01010020; - var new_state = try blake2s_hash.blake2s_compress(allocator, modified_iv, message, n_bytes, 0, 0xffffffff, 0); + + var new_state = try blake2s_hash.blake2sCompress(allocator, modified_iv, message, n_bytes, 0, 0xffffffff, 0); defer new_state.deinit(); + var new_state_relocatable = try getMaybeRelocArrayFromU32Array(allocator, new_state); - new_state_relocatable.deinit(); + defer new_state_relocatable.deinit(); + _ = try vm.segments.writeArg(std.ArrayList(MaybeRelocatable), output, &new_state_relocatable); } diff --git a/src/hint_processor/builtin_hint_codes.zig b/src/hint_processor/builtin_hint_codes.zig index 9fdf9ba0..dfafa1a3 100644 --- a/src/hint_processor/builtin_hint_codes.zig +++ b/src/hint_processor/builtin_hint_codes.zig @@ -743,6 +743,7 @@ pub const EXAMPLE_BLAKE2S_COMPRESS = pub const BLAKE2S_FINALIZE_V3 = \\# Add dummy pairs of input and output. \\from starkware.cairo.common.cairo_blake2s.blake2s_utils import IV, blake2s_compress + \\ \\_n_packed_instances = int(ids.N_PACKED_INSTANCES) \\assert 0 <= _n_packed_instances < 20 \\_blake2s_input_chunk_size_felts = int(ids.BLAKE2S_INPUT_CHUNK_SIZE_FELTS) @@ -1484,6 +1485,52 @@ pub const PACK_MODN_DIV_MODN = \\value = res = div_mod(x, s, N) ; +pub const SHA256_INPUT = "ids.full_word = int(ids.n_bytes >= 4)"; + +pub const SHA256_MAIN_CONSTANT_INPUT_LENGTH = + \\from starkware.cairo.common.cairo_sha256.sha256_utils import ( + \\ IV, compute_message_schedule, sha2_compress_function) + \\ + \\_sha256_input_chunk_size_felts = int(ids.SHA256_INPUT_CHUNK_SIZE_FELTS) + \\assert 0 <= _sha256_input_chunk_size_felts < 100 + \\ + \\w = compute_message_schedule(memory.get_range( + \\ ids.sha256_start, _sha256_input_chunk_size_felts)) + \\new_state = sha2_compress_function(IV, w) + \\segments.write_arg(ids.output, new_state) +; + +pub const SHA256_MAIN_ARBITRARY_INPUT_LENGTH = + \\from starkware.cairo.common.cairo_sha256.sha256_utils import ( + \\ compute_message_schedule, sha2_compress_function) + \\ + \\_sha256_input_chunk_size_felts = int(ids.SHA256_INPUT_CHUNK_SIZE_FELTS) + \\assert 0 <= _sha256_input_chunk_size_felts < 100 + \\_sha256_state_size_felts = int(ids.SHA256_STATE_SIZE_FELTS) + \\assert 0 <= _sha256_state_size_felts < 100 + \\w = compute_message_schedule(memory.get_range( + \\ ids.sha256_start, _sha256_input_chunk_size_felts)) + \\new_state = sha2_compress_function(memory.get_range(ids.state, _sha256_state_size_felts), w) + \\segments.write_arg(ids.output, new_state) +; + +pub const SHA256_FINALIZE = + \\# Add dummy pairs of input and output. + \\from starkware.cairo.common.cairo_sha256.sha256_utils import ( + \\ IV, compute_message_schedule, sha2_compress_function) + \\ + \\_block_size = int(ids.BLOCK_SIZE) + \\assert 0 <= _block_size < 20 + \\_sha256_input_chunk_size_felts = int(ids.SHA256_INPUT_CHUNK_SIZE_FELTS) + \\assert 0 <= _sha256_input_chunk_size_felts < 100 + \\ + \\message = [0] * _sha256_input_chunk_size_felts + \\w = compute_message_schedule(message) + \\output = sha2_compress_function(IV, w) + \\padding = (message + IV + output) * (_block_size - 1) + \\segments.write_arg(ids.sha256_ptr_end, padding) +; + pub const UINT384_GET_SQUARE_ROOT = \\from starkware.python.math_utils import is_quad_residue, sqrt \\ diff --git a/src/hint_processor/hint_processor_def.zig b/src/hint_processor/hint_processor_def.zig index de9f379c..5a0566c8 100644 --- a/src/hint_processor/hint_processor_def.zig +++ b/src/hint_processor/hint_processor_def.zig @@ -39,6 +39,7 @@ const segments = @import("segments.zig"); const bigint_utils = @import("../hint_processor/builtin_hint_processor/secp/bigint_utils.zig"); const bigint = @import("bigint.zig"); const uint384 = @import("uint384.zig"); +const sha256_utils = @import("sha256_utils.zig"); const inv_mod_p_uint512 = @import("vrf/inv_mod_p_uint512.zig"); const fq = @import("vrf/fq.zig"); const vrf_pack = @import("vrf/pack.zig"); @@ -395,6 +396,8 @@ pub const CairoVMHintProcessor = struct { try blake2s_utils.blake2sFinalize(allocator, vm, hint_data.ids_data, hint_data.ap_tracking); } else if (std.mem.eql(u8, hint_codes.BLAKE2S_FINALIZE_V2, hint_data.code)) { try blake2s_utils.blake2sFinalize(allocator, vm, hint_data.ids_data, hint_data.ap_tracking); + } else if (std.mem.eql(u8, hint_codes.BLAKE2S_FINALIZE_V3, hint_data.code)) { + try blake2s_utils.blake2sFinalizeV3(allocator, vm, hint_data.ids_data, hint_data.ap_tracking); } else if (std.mem.eql(u8, hint_codes.BLAKE2S_ADD_UINT256, hint_data.code)) { try blake2s_utils.blake2sAddUnit256(allocator, vm, hint_data.ids_data, hint_data.ap_tracking); } else if (std.mem.eql(u8, hint_codes.BLAKE2S_ADD_UINT256_BIGEND, hint_data.code)) { @@ -852,6 +855,13 @@ pub const CairoVMHintProcessor = struct { hint_data.ids_data, hint_data.ap_tracking, ); + } else if (std.mem.eql(u8, hint_codes.SHA256_INPUT, hint_data.code)) { + try sha256_utils.sha256Input( + allocator, + vm, + hint_data.ids_data, + hint_data.ap_tracking, + ); } else if (std.mem.eql(u8, hint_codes.UINT384_GET_SQUARE_ROOT, hint_data.code)) { try field_arithmetic.u384GetSquareRoot( allocator, @@ -859,6 +869,29 @@ pub const CairoVMHintProcessor = struct { hint_data.ids_data, hint_data.ap_tracking, ); + } else if (std.mem.eql(u8, hint_codes.SHA256_MAIN_CONSTANT_INPUT_LENGTH, hint_data.code)) { + try sha256_utils.sha256MainConstantInputLength( + allocator, + vm, + hint_data.ids_data, + hint_data.ap_tracking, + constants, + ); + } else if (std.mem.eql(u8, hint_codes.SHA256_MAIN_ARBITRARY_INPUT_LENGTH, hint_data.code)) { + try sha256_utils.sha256MainArbitraryInputLength( + allocator, + vm, + hint_data.ids_data, + hint_data.ap_tracking, + constants, + ); + } else if (std.mem.eql(u8, hint_codes.SHA256_FINALIZE, hint_data.code)) { + try sha256_utils.sha256Finalize( + allocator, + vm, + hint_data.ids_data, + hint_data.ap_tracking, + ); } else if (std.mem.eql(u8, hint_codes.UINT256_GET_SQUARE_ROOT, hint_data.code)) { try field_arithmetic.u256GetSquareRoot( allocator, diff --git a/src/hint_processor/sha256_utils.zig b/src/hint_processor/sha256_utils.zig new file mode 100644 index 00000000..95cb5b29 --- /dev/null +++ b/src/hint_processor/sha256_utils.zig @@ -0,0 +1,383 @@ +const std = @import("std"); + +const CoreVM = @import("../vm/core.zig"); +const field_helper = @import("../math/fields/helper.zig"); +const Felt252 = @import("../math/fields/starknet.zig").Felt252; +const STARKNET_PRIME = @import("../math/fields/fields.zig").STARKNET_PRIME; +const SIGNED_FELT_MAX = @import("../math/fields/fields.zig").SIGNED_FELT_MAX; +const MaybeRelocatable = @import("../vm/memory/relocatable.zig").MaybeRelocatable; +const Relocatable = @import("../vm/memory/relocatable.zig").Relocatable; +const CairoVM = CoreVM.CairoVM; +const hint_utils = @import("hint_utils.zig"); +const testing_utils = @import("testing_utils.zig"); +const HintProcessor = @import("hint_processor_def.zig").CairoVMHintProcessor; +const HintData = @import("hint_processor_def.zig").HintData; +const HintReference = @import("hint_processor_def.zig").HintReference; +const hint_codes = @import("builtin_hint_codes.zig"); +const Allocator = std.mem.Allocator; +const ApTracking = @import("../vm/types/programjson.zig").ApTracking; +const ExecutionScopes = @import("../vm/types/execution_scopes.zig").ExecutionScopes; + +const MathError = @import("../vm/error.zig").MathError; +const HintError = @import("../vm/error.zig").HintError; +const CairoVMError = @import("../vm/error.zig").CairoVMError; + +const sha256 = @import("../math/crypto/sha256.zig"); + +const feltToU32 = @import("blake2s_utils.zig").feltToU32; + +const SHA256_STATE_SIZE_FELTS: usize = 8; +const BLOCK_SIZE: usize = 7; +const IV: [SHA256_STATE_SIZE_FELTS]u32 = .{ + 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19, +}; + +pub fn sha256Input( + allocator: std.mem.Allocator, + vm: *CairoVM, + ids_data: std.StringHashMap(HintReference), + ap_tracking: ApTracking, +) !void { + const n_bytes = try hint_utils.getIntegerFromVarName("n_bytes", vm, ids_data, ap_tracking); + + try hint_utils.insertValueFromVarName( + allocator, + "full_word", + MaybeRelocatable.fromInt(u8, if (n_bytes.cmp(Felt252.fromInt(u32, 4)).compare(.gte)) + 1 + else + 0), + vm, + ids_data, + ap_tracking, + ); +} + +/// Inner implementation of [`sha256_main_constant_input_length`] and [`sha256_main_arbitrary_input_length`] +fn sha256Main( + allocator: std.mem.Allocator, + vm: *CairoVM, + ids_data: std.StringHashMap(HintReference), + ap_tracking: ApTracking, + constants: *std.StringHashMap(Felt252), + iv: *[8]u32, +) !void { + const input_ptr = try hint_utils.getPtrFromVarName("sha256_start", vm, ids_data, ap_tracking); + + // The original code gets it from `ids` in both cases, and this makes it easier + // to implement the arbitrary length one + const input_chunk_size_felts = + (try hint_utils.getConstantFromVarName("SHA256_INPUT_CHUNK_SIZE_FELTS", constants)).intoUsizeOrOptional() orelse 100; + + if (input_chunk_size_felts >= 100) + return HintError.AssertionFailed; + + var message = try std.ArrayList(u8).initCapacity(allocator, 4 * input_chunk_size_felts); + defer message.deinit(); + + var buf: [4]u8 = undefined; + + for (0..input_chunk_size_felts) |i| { + const input_element = try vm.getFelt(try input_ptr.addUint(i)); + + const input_elem_u32 = (try feltToU32(input_element)); + + std.mem.writeInt(u32, &buf, input_elem_u32, .big); + + try message.appendSlice(&buf); + } + + const blocks: []const [64]u8 = &.{ + message.items[0..64].*, + }; + + sha256.compress(iv, blocks); + + var output = try std.ArrayList(MaybeRelocatable).initCapacity(allocator, iv.len); + defer output.deinit(); + + for (iv.*) |new_state| { + try output.append(MaybeRelocatable.fromInt(u32, new_state)); + } + + const output_base = try hint_utils.getPtrFromVarName("output", vm, ids_data, ap_tracking); + + _ = try vm.segments.writeArg(std.ArrayList(MaybeRelocatable), output_base, &output); +} + +// Implements hint: +// from starkware.cairo.common.cairo_sha256.sha256_utils import ( +// IV, compute_message_schedule, sha2_compress_function) + +// _sha256_input_chunk_size_felts = int(ids.SHA256_INPUT_CHUNK_SIZE_FELTS) +// assert 0 <= _sha256_input_chunk_size_felts < 100 + +// w = compute_message_schedule(memory.get_range( +// ids.sha256_start, _sha256_input_chunk_size_felts)) +// new_state = sha2_compress_function(IV, w) +// segments.write_arg(ids.output, new_state) +pub fn sha256MainConstantInputLength( + allocator: std.mem.Allocator, + vm: *CairoVM, + ids_data: std.StringHashMap(HintReference), + ap_tracking: ApTracking, + constants: *std.StringHashMap(Felt252), +) !void { + var iv = IV; + try sha256Main(allocator, vm, ids_data, ap_tracking, constants, &iv); +} + +// Implements hint: +// from starkware.cairo.common.cairo_sha256.sha256_utils import ( +// compute_message_schedule, sha2_compress_function) + +// _sha256_input_chunk_size_felts = int(ids.SHA256_INPUT_CHUNK_SIZE_FELTS) +// assert 0 <= _sha256_input_chunk_size_felts < 100 +// _sha256_state_size_felts = int(ids.SHA256_STATE_SIZE_FELTS) +// assert 0 <= _sha256_state_size_felts < 100 +// w = compute_message_schedule(memory.get_range( +// ids.sha256_start, _sha256_input_chunk_size_felts)) +// new_state = sha2_compress_function(memory.get_range(ids.state, _sha256_state_size_felts), w) +// segments.write_arg(ids.output, new_state) +pub fn sha256MainArbitraryInputLength( + allocator: std.mem.Allocator, + vm: *CairoVM, + ids_data: std.StringHashMap(HintReference), + ap_tracking: ApTracking, + constants: *std.StringHashMap(Felt252), +) !void { + const iv_ptr = try hint_utils.getPtrFromVarName("state", vm, ids_data, ap_tracking); + + const state_size_felt = + try hint_utils.getConstantFromVarName("SHA256_STATE_SIZE_FELTS", constants); + + const state_size = blk: { + const size = state_size_felt.intoUsizeOrOptional() orelse return HintError.AssertionFailed; + if (size == SHA256_STATE_SIZE_FELTS) break :blk size; + if (size < 100) return HintError.InvalidValue; + return HintError.AssertionFailed; + }; + + var iv_felt = try vm + .getFeltRange(iv_ptr, state_size); + defer iv_felt.deinit(); + + var iv = try std.ArrayList(u32).initCapacity(allocator, iv_felt.items.len); + defer iv.deinit(); + + for (iv_felt.items) |x| try iv.append(try feltToU32(x)); + + try sha256Main(allocator, vm, ids_data, ap_tracking, constants, iv.items[0..8]); +} + +pub fn sha256Finalize( + allocator: std.mem.Allocator, + vm: *CairoVM, + ids_data: std.StringHashMap(HintReference), + ap_tracking: ApTracking, +) !void { + const message = [_]u8{0} ** 64; + + var iv = IV; + + var iv_static = try std.ArrayList(MaybeRelocatable).initCapacity(allocator, iv.len); + defer iv_static.deinit(); + + for (iv) |n| try iv_static.append(MaybeRelocatable.fromInt(u32, n)); + + const blocks: []const [64]u8 = &.{message[0..64].*}; + + sha256.compress(&iv, blocks); + + var output = try std.ArrayList(MaybeRelocatable).initCapacity(allocator, SHA256_STATE_SIZE_FELTS); + defer output.deinit(); + + for (iv) |new_state| { + try output.append(MaybeRelocatable.fromInt(u32, new_state)); + } + + const sha256_ptr_end = try hint_utils.getPtrFromVarName("sha256_ptr_end", vm, ids_data, ap_tracking); + + var padding = std.ArrayList(MaybeRelocatable).init(allocator); + defer padding.deinit(); + + var zero_vector_message = [_]MaybeRelocatable{MaybeRelocatable.fromInt(u8, 0)} ** 16; + + for (BLOCK_SIZE - 1) |_| { + try padding.appendSlice(&zero_vector_message); + try padding.appendSlice(iv_static.items); + try padding.appendSlice(output.items); + } + + _ = try vm.segments.writeArg(std.ArrayList(MaybeRelocatable), sha256_ptr_end, &padding); +} + +const SHA256_INPUT_CHUNK_SIZE_FELTS: usize = 16; + +test "Sha256Utils: sha256 input one" { + var vm = try testing_utils.initVMWithRangeCheck(std.testing.allocator); + defer vm.deinit(); + defer vm.segments.memory.deinitData(std.testing.allocator); + + try vm.segments.memory.setUpMemory(std.testing.allocator, .{ + .{ .{ 1, 1 }, .{7} }, + }); + + vm.run_context.fp.* = 2; + + var ids_data = try testing_utils.setupIdsForTestWithoutMemory(std.testing.allocator, &.{ + "full_word", "n_bytes", + }); + defer ids_data.deinit(); + + try sha256Input(std.testing.allocator, &vm, ids_data, .{}); + + try testing_utils.checkMemory(vm.segments.memory, .{ + .{ .{ 1, 0 }, .{1} }, + }); +} + +test "Sha256Utils: sha256 input zero" { + var vm = try testing_utils.initVMWithRangeCheck(std.testing.allocator); + defer vm.deinit(); + defer vm.segments.memory.deinitData(std.testing.allocator); + + try vm.segments.memory.setUpMemory(std.testing.allocator, .{ + .{ .{ 1, 1 }, .{3} }, + }); + + vm.run_context.fp.* = 2; + + var ids_data = try testing_utils.setupIdsForTestWithoutMemory(std.testing.allocator, &.{ + "full_word", "n_bytes", + }); + defer ids_data.deinit(); + + try sha256Input(std.testing.allocator, &vm, ids_data, .{}); + + try testing_utils.checkMemory(vm.segments.memory, .{ + .{ .{ 1, 0 }, .{0} }, + }); +} + +test "Sha256Utils: constant input length ok" { + var vm = try testing_utils.initVMWithRangeCheck(std.testing.allocator); + defer vm.deinit(); + defer vm.segments.memory.deinitData(std.testing.allocator); + + try vm.segments.memory.setUpMemory(std.testing.allocator, .{ + .{ .{ 1, 0 }, .{ 2, 0 } }, + .{ .{ 1, 1 }, .{ 3, 0 } }, + .{ .{ 2, 0 }, .{22} }, + .{ .{ 2, 1 }, .{22} }, + .{ .{ 2, 2 }, .{22} }, + .{ .{ 2, 3 }, .{22} }, + .{ .{ 2, 4 }, .{22} }, + .{ .{ 2, 5 }, .{22} }, + .{ .{ 2, 6 }, .{22} }, + .{ .{ 2, 7 }, .{22} }, + .{ .{ 2, 8 }, .{22} }, + .{ .{ 2, 9 }, .{22} }, + .{ .{ 2, 10 }, .{22} }, + .{ .{ 2, 11 }, .{22} }, + .{ .{ 2, 12 }, .{22} }, + .{ .{ 2, 13 }, .{22} }, + .{ .{ 2, 14 }, .{22} }, + .{ .{ 2, 15 }, .{22} }, + .{ .{ 3, 9 }, .{0} }, + }); + + vm.run_context.fp.* = 2; + + var ids_data = try testing_utils.setupIdsForTestWithoutMemory(std.testing.allocator, &.{ + "sha256_start", "output", + }); + defer ids_data.deinit(); + + var constants = std.StringHashMap(Felt252).init(std.testing.allocator); + defer constants.deinit(); + + var exec_scopes = try ExecutionScopes.init(std.testing.allocator); + defer exec_scopes.deinit(); + + try constants.put("SHA256_INPUT_CHUNK_SIZE_FELTS", Felt252.fromInt(usize, SHA256_INPUT_CHUNK_SIZE_FELTS)); + + try testing_utils.runHint(std.testing.allocator, &vm, ids_data, hint_codes.SHA256_MAIN_CONSTANT_INPUT_LENGTH, &constants, &exec_scopes); + + try testing_utils.checkMemory(vm.segments.memory, .{ + .{ .{ 3, 0 }, .{3704205499} }, + .{ .{ 3, 1 }, .{2308112482} }, + .{ .{ 3, 2 }, .{3022351583} }, + .{ .{ 3, 3 }, .{174314172} }, + .{ .{ 3, 4 }, .{1762869695} }, + .{ .{ 3, 5 }, .{1649521060} }, + .{ .{ 3, 6 }, .{2811202336} }, + .{ .{ 3, 7 }, .{4231099170} }, + }); +} + +test "Sha256Utils: arbitary input length ok" { + var vm = try testing_utils.initVMWithRangeCheck(std.testing.allocator); + defer vm.deinit(); + defer vm.segments.memory.deinitData(std.testing.allocator); + + try vm.segments.memory.setUpMemory(std.testing.allocator, .{ + .{ .{ 1, 0 }, .{ 2, 0 } }, + .{ .{ 1, 1 }, .{ 3, 0 } }, + .{ .{ 1, 2 }, .{ 4, 0 } }, + .{ .{ 2, 0 }, .{22} }, + .{ .{ 2, 1 }, .{22} }, + .{ .{ 2, 2 }, .{22} }, + .{ .{ 2, 3 }, .{22} }, + .{ .{ 2, 4 }, .{22} }, + .{ .{ 2, 5 }, .{22} }, + .{ .{ 2, 6 }, .{22} }, + .{ .{ 2, 7 }, .{22} }, + .{ .{ 2, 8 }, .{22} }, + .{ .{ 2, 9 }, .{22} }, + .{ .{ 2, 10 }, .{22} }, + .{ .{ 2, 11 }, .{22} }, + .{ .{ 2, 12 }, .{22} }, + .{ .{ 2, 13 }, .{22} }, + .{ .{ 2, 14 }, .{22} }, + .{ .{ 2, 15 }, .{22} }, + .{ .{ 3, 9 }, .{0} }, + .{ .{ 4, 0 }, .{0x6A09E667} }, + .{ .{ 4, 1 }, .{0xBB67AE85} }, + .{ .{ 4, 2 }, .{0x3C6EF372} }, + .{ .{ 4, 3 }, .{0xA54FF53A} }, + .{ .{ 4, 4 }, .{0x510E527F} }, + .{ .{ 4, 5 }, .{0x9B05688C} }, + .{ .{ 4, 6 }, .{0x1F83D9AB} }, + .{ .{ 4, 7 }, .{0x5BE0CD18} }, + }); + + vm.run_context.fp.* = 3; + + var ids_data = try testing_utils.setupIdsForTestWithoutMemory(std.testing.allocator, &.{ + "sha256_start", "output", "state", + }); + defer ids_data.deinit(); + + var constants = std.StringHashMap(Felt252).init(std.testing.allocator); + defer constants.deinit(); + + var exec_scopes = try ExecutionScopes.init(std.testing.allocator); + defer exec_scopes.deinit(); + + try constants.put("SHA256_INPUT_CHUNK_SIZE_FELTS", Felt252.fromInt(usize, SHA256_INPUT_CHUNK_SIZE_FELTS)); + try constants.put("SHA256_STATE_SIZE_FELTS", Felt252.fromInt(usize, SHA256_STATE_SIZE_FELTS)); + + try testing_utils.runHint(std.testing.allocator, &vm, ids_data, hint_codes.SHA256_MAIN_ARBITRARY_INPUT_LENGTH, &constants, &exec_scopes); + + try testing_utils.checkMemory(vm.segments.memory, .{ + .{ .{ 3, 0 }, .{1676947577} }, + .{ .{ 3, 1 }, .{1555161467} }, + .{ .{ 3, 2 }, .{2679819371} }, + .{ .{ 3, 3 }, .{2084775296} }, + .{ .{ 3, 4 }, .{3059346845} }, + .{ .{ 3, 5 }, .{785647811} }, + .{ .{ 3, 6 }, .{2729325562} }, + .{ .{ 3, 7 }, .{2503090120} }, + }); +} diff --git a/src/integration_tests.zig b/src/integration_tests.zig index 1b999894..06d550bc 100644 --- a/src/integration_tests.zig +++ b/src/integration_tests.zig @@ -61,8 +61,8 @@ pub fn main() !void { .{ .pathname = "cairo_programs/ed25519_ec.json", .layout = "all_cairo" }, .{ .pathname = "cairo_programs/ed25519_field.json", .layout = "all_cairo" }, .{ .pathname = "cairo_programs/efficient_secp256r1_ec.json", .layout = "all_cairo" }, - // TODO: sha256 - // .{ .pathname = "cairo_programs/example_blake2s.json", .layout = "all_cairo" }, + + .{ .pathname = "cairo_programs/example_blake2s.json", .layout = "all_cairo" }, .{ .pathname = "cairo_programs/example_program.json", .layout = "all_cairo" }, .{ .pathname = "cairo_programs/factorial.json", .layout = "plain" }, @@ -129,10 +129,8 @@ pub fn main() !void { .{ .pathname = "cairo_programs/operations_with_data_structures.json", .layout = "all_cairo" }, - // TODO: hint not implemented sha256 - // .{ .pathname = "cairo_programs/packed_sha256_test.json", .layout = "all_cairo" }, - // TODO: hint not implemented sha256 - // .{ .pathname = "cairo_programs/packed_sha256.json", .layout = "all_cairo" }, + .{ .pathname = "cairo_programs/packed_sha256_test.json", .layout = "all_cairo" }, + .{ .pathname = "cairo_programs/packed_sha256.json", .layout = "all_cairo" }, .{ .pathname = "cairo_programs/pedersen_test.json", .layout = "all_cairo" }, .{ .pathname = "cairo_programs/pointers.json", .layout = "all_cairo" }, .{ .pathname = "cairo_programs/poseidon_builtin.json", .layout = "all_cairo" }, @@ -167,9 +165,8 @@ pub fn main() !void { .{ .pathname = "cairo_programs/set_add.json", .layout = "all_cairo" }, .{ .pathname = "cairo_programs/set_integration_tests.json", .layout = "all_cairo" }, - // TODO: sha 256 hints not implemented - // .{ .pathname = "cairo_programs/sha256_test.json", .layout = "all_cairo" }, - // .{ .pathname = "cairo_programs/sha256.json", .layout = "all_cairo" }, + .{ .pathname = "cairo_programs/sha256_test.json", .layout = "all_cairo" }, + .{ .pathname = "cairo_programs/sha256.json", .layout = "all_cairo" }, .{ .pathname = "cairo_programs/signature.json", .layout = "all_cairo" }, .{ .pathname = "cairo_programs/signed_div_rem.json", .layout = "all_cairo" }, diff --git a/src/lib.zig b/src/lib.zig index c4fa0ccf..08593475 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -85,6 +85,7 @@ pub const hint_processor = struct { pub usingnamespace @import("hint_processor/blake2s_hash.zig"); pub usingnamespace @import("hint_processor/blake2s_utils.zig"); pub usingnamespace @import("hint_processor/uint384.zig"); + pub usingnamespace @import("hint_processor/sha256_utils.zig"); pub usingnamespace @import("hint_processor/vrf/fq.zig"); pub usingnamespace @import("hint_processor/vrf/pack.zig"); diff --git a/src/math/crypto/sha256.zig b/src/math/crypto/sha256.zig new file mode 100644 index 00000000..1701924e --- /dev/null +++ b/src/math/crypto/sha256.zig @@ -0,0 +1,250 @@ +const std = @import("std"); + +pub const BLOCK_LEN: usize = 16; + +fn shl(v: [4]u32, o: u32) [4]u32 { + return .{ v[0] >> @intCast(o), v[1] >> @intCast(o), v[2] >> @intCast(o), v[3] >> @intCast(o) }; +} + +fn shr(v: [4]u32, o: u32) [4]u32 { + return .{ v[0] << @intCast(o), v[1] << @intCast(o), v[2] << @intCast(o), v[3] << @intCast(o) }; +} + +fn orf(a: [4]u32, b: [4]u32) [4]u32 { + return .{ a[0] | b[0], a[1] | b[1], a[2] | b[2], a[3] | b[3] }; +} + +fn xor(a: [4]u32, b: [4]u32) [4]u32 { + return .{ a[0] ^ b[0], a[1] ^ b[1], a[2] ^ b[2], a[3] ^ b[3] }; +} + +fn add(a: [4]u32, b: [4]u32) [4]u32 { + return .{ + @addWithOverflow(a[0], b[0])[0], + @addWithOverflow(a[1], b[1])[0], + @addWithOverflow(a[2], b[2])[0], + @addWithOverflow(a[3], b[3])[0], + }; +} + +fn sha256load(v2: [4]u32, v3: [4]u32) [4]u32 { + return .{ v3[3], v2[0], v2[1], v2[2] }; +} + +fn sha256swap(v0: [4]u32) [4]u32 { + return .{ v0[2], v0[3], v0[0], v0[1] }; +} + +fn sigma0x4(x: [4]u32) [4]u32 { + const t1 = orf(shl(x, 7), shr(x, 25)); + const t2 = orf(shl(x, 18), shr(x, 14)); + const t3 = shl(x, 3); + return xor(xor(t1, t2), t3); +} + +fn sha256msg1(v0: [4]u32, v1: [4]u32) [4]u32 { + // sigma 0 on vectors + return add(v0, sigma0x4(sha256load(v0, v1))); +} + +fn rotr(x: u32, n: u32) u32 { + return (x >> @intCast(n % 32)) | (x << @intCast((32 - n) % 32)); +} + +fn sigma1(a: u32) u32 { + return rotr(a, 17) ^ rotr(a, 19) ^ (a >> 10); +} + +fn sha256msg2(v4: [4]u32, v3: [4]u32) [4]u32 { + const x3, const x2, const x1, const x0 = v4; + const w15, const w14, _, _ = v3; + + const w16 = @addWithOverflow(x0, sigma1(w14))[0]; + const w17 = @addWithOverflow(x1, sigma1(w15))[0]; + const w18 = @addWithOverflow(x2, sigma1(w16))[0]; + const w19 = @addWithOverflow(x3, sigma1(w17))[0]; + + return .{ w19, w18, w17, w16 }; +} + +fn bigSigma0(a: u32) u32 { + return rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22); +} +fn bigSigma1(a: u32) u32 { + return rotr(a, 6) ^ rotr(a, 11) ^ rotr(a, 25); +} + +fn bool3ary202(a: u32, b: u32, c: u32) u32 { + return c ^ (a & (b ^ c)); +} +fn bool3ary232(a: u32, b: u32, c: u32) u32 { + return (a & b) ^ (a & c) ^ (b & c); +} + +fn sha256DigestRoundX2(cdgh: [4]u32, abef: [4]u32, wk: [4]u32) [4]u32 { + _, _, const wk1, const wk0 = wk; + const a0, const b0, const e0, const f0 = abef; + const c0, const d0, const g0, const h0 = cdgh; + + // a round + var x0 = bigSigma1(e0); + x0 = @addWithOverflow(x0, bool3ary202(e0, f0, g0))[0]; + x0 = @addWithOverflow(x0, wk0)[0]; + x0 = @addWithOverflow(x0, h0)[0]; + + var y0 = bigSigma0(a0); + y0 = @addWithOverflow(y0, bool3ary232(a0, b0, c0))[0]; + + const a1, const b1, const c1, const d1, const e1, const f1, const g1, const h1 = .{ + @addWithOverflow(x0, y0)[0], + a0, + b0, + c0, + @addWithOverflow(x0, d0)[0], + e0, + f0, + g0, + }; + + // a round + var x1 = bigSigma1(e1); + x1 = @addWithOverflow(bool3ary202(e1, f1, g1), x1)[0]; + x1 = @addWithOverflow(wk1, x1)[0]; + x1 = @addWithOverflow(h1, x1)[0]; + + var y1 = bigSigma0(a1); + y1 = @addWithOverflow(y1, bool3ary232(a1, b1, c1))[0]; + + const a2, const b2, _, _, const e2, const f2, _, _ = .{ + @addWithOverflow(x1, y1)[0], + a1, + b1, + c1, + @addWithOverflow(x1, d1)[0], + e1, + f1, + g1, + }; + + return .{ a2, b2, e2, f2 }; +} + +/// Constants necessary for SHA-256 family of digests. +pub const K32: [64]u32 = .{ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, +}; + +/// Constants necessary for SHA-256 family of digests. +pub const K32X4: [16][4]u32 = .{ + .{ K32[3], K32[2], K32[1], K32[0] }, + .{ K32[7], K32[6], K32[5], K32[4] }, + .{ K32[11], K32[10], K32[9], K32[8] }, + .{ K32[15], K32[14], K32[13], K32[12] }, + .{ K32[19], K32[18], K32[17], K32[16] }, + .{ K32[23], K32[22], K32[21], K32[20] }, + .{ K32[27], K32[26], K32[25], K32[24] }, + .{ K32[31], K32[30], K32[29], K32[28] }, + .{ K32[35], K32[34], K32[33], K32[32] }, + .{ K32[39], K32[38], K32[37], K32[36] }, + .{ K32[43], K32[42], K32[41], K32[40] }, + .{ K32[47], K32[46], K32[45], K32[44] }, + .{ K32[51], K32[50], K32[49], K32[48] }, + .{ K32[55], K32[54], K32[53], K32[52] }, + .{ K32[59], K32[58], K32[57], K32[56] }, + .{ K32[63], K32[62], K32[61], K32[60] }, +}; + +fn schedule(v0: [4]u32, v1: [4]u32, v2: [4]u32, v3: [4]u32) [4]u32 { + const t1 = sha256msg1(v0, v1); + const t2 = sha256load(v2, v3); + const t3 = add(t1, t2); + return sha256msg2(t3, v3); +} + +fn rounds4(abef: *[4]u32, cdgh: *[4]u32, rest: [4]u32, i: u32) void { + const t1 = add(rest, K32X4[i]); + cdgh.* = sha256DigestRoundX2(cdgh.*, abef.*, t1); + const t2 = sha256swap(t1); + abef.* = sha256DigestRoundX2(abef.*, cdgh.*, t2); +} + +fn scheduleRounds4( + abef: *[4]u32, + cdgh: *[4]u32, + w0: [4]u32, + w1: [4]u32, + w2: [4]u32, + w3: [4]u32, + w4: *[4]u32, + i: u32, +) void { + w4.* = schedule(w0, w1, w2, w3); + rounds4(abef, cdgh, w4.*, i); +} + +/// Process a block with the SHA-256 algorithm. +fn sha256DigestBlockU32(state: *[8]u32, block: [16]u32) void { + var abef = [_]u32{ state[0], state[1], state[4], state[5] }; + var cdgh = [_]u32{ state[2], state[3], state[6], state[7] }; + + // Rounds 0..64 + var w0 = [_]u32{ block[3], block[2], block[1], block[0] }; + var w1 = [_]u32{ block[7], block[6], block[5], block[4] }; + var w2 = [_]u32{ block[11], block[10], block[9], block[8] }; + var w3 = [_]u32{ block[15], block[14], block[13], block[12] }; + var w4: [4]u32 = undefined; + + rounds4(&abef, &cdgh, w0, 0); + rounds4(&abef, &cdgh, w1, 1); + rounds4(&abef, &cdgh, w2, 2); + rounds4(&abef, &cdgh, w3, 3); + scheduleRounds4(&abef, &cdgh, w0, w1, w2, w3, &w4, 4); + scheduleRounds4(&abef, &cdgh, w1, w2, w3, w4, &w0, 5); + scheduleRounds4(&abef, &cdgh, w2, w3, w4, w0, &w1, 6); + scheduleRounds4(&abef, &cdgh, w3, w4, w0, w1, &w2, 7); + scheduleRounds4(&abef, &cdgh, w4, w0, w1, w2, &w3, 8); + scheduleRounds4(&abef, &cdgh, w0, w1, w2, w3, &w4, 9); + scheduleRounds4(&abef, &cdgh, w1, w2, w3, w4, &w0, 10); + scheduleRounds4(&abef, &cdgh, w2, w3, w4, w0, &w1, 11); + scheduleRounds4(&abef, &cdgh, w3, w4, w0, w1, &w2, 12); + scheduleRounds4(&abef, &cdgh, w4, w0, w1, w2, &w3, 13); + scheduleRounds4(&abef, &cdgh, w0, w1, w2, w3, &w4, 14); + scheduleRounds4(&abef, &cdgh, w1, w2, w3, w4, &w0, 15); + + const a, const b, const e, const f = abef; + const c, const d, const g, const h = cdgh; + + state[0] = @addWithOverflow(state[0], a)[0]; + state[1] = @addWithOverflow(state[1], b)[0]; + state[2] = @addWithOverflow(state[2], c)[0]; + state[3] = @addWithOverflow(state[3], d)[0]; + state[4] = @addWithOverflow(state[4], e)[0]; + state[5] = @addWithOverflow(state[5], f)[0]; + state[6] = @addWithOverflow(state[6], g)[0]; + state[7] = @addWithOverflow(state[7], h)[0]; +} + +pub fn compress(state: *[8]u32, blocks: []const [64]u8) void { + var block_u32 = [_]u32{0} ** BLOCK_LEN; + block_u32[0] = 0; + // since LLVM can't properly use aliasing yet it will make + // unnecessary state stores without this copy + var state_cpy = state.*; + + for (blocks) |block| { + for (0.., &block_u32) |i, *o| { + const chunk = block[i * 4 .. (i + 1) * 4]; + o.* = std.mem.readInt(u32, chunk[0..4], .big); + } + + sha256DigestBlockU32(&state_cpy, block_u32); + } + state.* = state_cpy; +} diff --git a/src/math/crypto/starknet_crypto b/src/math/crypto/starknet_crypto new file mode 100755 index 00000000..8b137891 --- /dev/null +++ b/src/math/crypto/starknet_crypto @@ -0,0 +1 @@ + diff --git a/src/vm/error.zig b/src/vm/error.zig index 8cad1852..9cadf939 100644 --- a/src/vm/error.zig +++ b/src/vm/error.zig @@ -266,6 +266,9 @@ pub const VerifyError = error{ }; pub const HintError = error{ + /// Invalid value + InvalidValue, + // verify_zero: Invalid input SecpVerifyZero,