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

feat: keccak256 #179

Merged
merged 3 commits into from
Sep 20, 2023
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
65 changes: 65 additions & 0 deletions src/math/src/keccak256.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use keccak::cairo_keccak;

#[generate_trait]
impl U64Impl of U64Trait {
/// Converts a little-endian byte slice to a 64-bit unsigned integer
///
/// # Arguments
///
/// * `self` - A `Span<u8>` slice of size n <=8.
0xLucqs marked this conversation as resolved.
Show resolved Hide resolved
///
/// # Returns
///
/// A tuple containing the converted 64-bit unsigned integer and the amount of bytes consumed
fn from_le_bytes(mut self: Span<u8>) -> (u64, u32) {
assert(self.len() < 9, 'bytes dont fit in u64');
// Pack full value
let mut value: u64 = 0;
let mut n_bytes: u32 = self.len();
loop {
let byte = match self.pop_back() {
Option::Some(byte) => *byte,
Option::None => {
break;
},
};
value = value * 0x100 + (byte.into());
};
(value, n_bytes)
}
}

/// Reverse the endianness of an u256
fn reverse_endianness(value: u256) -> u256 {
let new_low = integer::u128_byte_reverse(value.high);
let new_high = integer::u128_byte_reverse(value.low);
u256 { low: new_low, high: new_high }
}

/// Computes the Solidity-compatible Keccak hash of an array of bytes.
///
/// # Arguments
///
/// * `self` - A `Array<u8>` of bytes.
///
/// # Returns
///
/// A `u256` value representing the Keccak hash of the input bytes array.
fn keccak256(mut self: Span<u8>) -> u256 {
// Converts byte array to little endian 8 byte words array.
let mut words64: Array<u64> = Default::default();
let (last_word, last_word_bytes) = loop {
// Specifically handle last word
if self.len() < 8 {
let (value, n_bytes) = U64Trait::from_le_bytes(self);
break (value, n_bytes);
};
let mut current_word = self.slice(0, 8);
let (value, n_bytes) = U64Trait::from_le_bytes(current_word);
words64.append(value);
self = self.slice(8, self.len() - 8);
};
let mut hash = reverse_endianness(cairo_keccak(ref words64, last_word, last_word_bytes));
hash
}

1 change: 1 addition & 0 deletions src/math/src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ mod perfect_number;
mod sha256;
mod sha512;
mod zellers_congruence;
mod keccak256;

#[cfg(test)]
mod tests;
1 change: 1 addition & 0 deletions src/math/src/tests.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ mod perfect_number_test;
mod sha256_test;
mod sha512_test;
mod zellers_congruence_test;
mod test_keccak256;
75 changes: 75 additions & 0 deletions src/math/src/tests/test_keccak256.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use alexandria_math::keccak256::keccak256;
use debug::PrintTrait;

#[test]
#[available_gas(2000000)]
fn test_keccak256_empty_bytes() {
let input = array![];

let hash = keccak256(input.span());

assert(
hash == 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470,
'wrong hash value'
)
}

#[test]
#[available_gas(2000000)]
fn test_keccak256_partial_bytes() {
let input = array![0x00, 0x01, 0x02, 0x03, 0x04, 0x05];

let hash = keccak256(input.span());

assert(
hash == 0x51e8babe8b42352100dffa7f7b3843c95245d3d545c6cbf5052e80258ae80627,
'wrong hash value'
);
}

#[test]
#[available_gas(2000000)]
fn test_keccak256_full_u256() {
let input = array![
0x00,
0x01,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x08,
0x09,
0x10,
0x11,
0x12,
0x13,
0x14,
0x15,
0x16,
0x17,
0x18,
0x19,
0x20,
0x21,
0x22,
0x23,
0x24,
0x25,
0x26,
0x27,
0x28,
0x29,
0x30,
0x31,
0x32
];

let hash = keccak256(input.span());

assert(
hash == 0x98cfb1eca8a71b4a4b1c115f3d5a462296a66487d1d97fb4c47b979c64bde069,
'wrong hash value'
);
}