Skip to content

Commit

Permalink
wtf
Browse files Browse the repository at this point in the history
  • Loading branch information
clabby committed Jan 5, 2024
1 parent 34e499d commit 3e294db
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 84 deletions.
2 changes: 1 addition & 1 deletion contracts/StatefulSponge.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
pragma solidity 0.8.15;

import { Lib_Keccak256 } from "contracts/lib/LK.sol";

Expand Down
218 changes: 166 additions & 52 deletions contracts/lib/LK.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@ pragma solidity >0.5.0;
// https://github.com/firefly/wallet/blob/master/source/libs/ethers/src/keccak256.c

library Lib_Keccak256 {
/// @notice The round constants for the keccak256 hash function. Packed in memory for efficient reading during the
/// permutation.
bytes private constant ROUND_CONSTANTS = abi.encode(
0x00000000000000010000000000008082800000000000808a8000000080008000, // r1,r2,r3,r4
0x000000000000808b000000008000000180000000800080818000000000008009, // r5,r6,r7,r8
0x000000000000008a00000000000000880000000080008009000000008000000a, // r9,r10,r11,r12
0x000000008000808b800000000000008b80000000000080898000000000008003, // r13,r14,r15,r16
0x80000000000080028000000000000080000000000000800a800000008000000a, // r17,r18,r19,r20
0x8000000080008081800000000000808000000000800000018000000080008008 // r21,r22,r23,r24
);

uint64 private constant U64_MASK = 0xFFFFFFFFFFFFFFFF;

struct CTX {
uint64[25] A;
}
Expand Down Expand Up @@ -34,52 +47,70 @@ library Lib_Keccak256 {
uint64 D3 = (C4 << 1) ^ (C4 >> 63) ^ C2;
uint64 D4 = (C0 << 1) ^ (C0 >> 63) ^ C3;
c.A[0] ^= D0;
uint64 A1 = ((c.A[1] ^ D1) << 1) ^ ((c.A[1] ^ D1) >> (64 - 1)); //
uint64 A1 = ((c.A[1] ^ D1) << 1) ^ ((c.A[1] ^ D1) >> (64 - 1));
c.A[1] = ((c.A[6] ^ D1) << 44) ^ ((c.A[6] ^ D1) >> (64 - 44));
// 6, 1, 44, D1
c.A[6] = ((c.A[9] ^ D4) << 20) ^ ((c.A[9] ^ D4) >> (64 - 20)); // 9, 6, 20, D4
c.A[9] = ((c.A[22] ^ D2) << 61) ^ ((c.A[22] ^ D2) >> (64 - 61)); // 22, 9, 61, D2
c.A[22] = ((c.A[14] ^ D4) << 39) ^ ((c.A[14] ^ D4) >> (64 - 39)); // 14, 22, 39, D4
c.A[14] = ((c.A[20] ^ D0) << 18) ^ ((c.A[20] ^ D0) >> (64 - 18)); // 20, 14, 18, D0
c.A[20] = ((c.A[2] ^ D2) << 62) ^ ((c.A[2] ^ D2) >> (64 - 62)); // 2, 20, 62, D2
c.A[2] = ((c.A[12] ^ D2) << 43) ^ ((c.A[12] ^ D2) >> (64 - 43)); // 12, 2, 43, D2
c.A[12] = ((c.A[13] ^ D3) << 25) ^ ((c.A[13] ^ D3) >> (64 - 25)); // 13, 12, 25, D3
c.A[13] = ((c.A[19] ^ D4) << 8) ^ ((c.A[19] ^ D4) >> (64 - 8)); // 19, 13, 8, D4
c.A[19] = ((c.A[23] ^ D3) << 56) ^ ((c.A[23] ^ D3) >> (64 - 56)); // 23, 19, 56, D3
c.A[23] = ((c.A[15] ^ D0) << 41) ^ ((c.A[15] ^ D0) >> (64 - 41)); // 15, 23, 41, D0
c.A[15] = ((c.A[4] ^ D4) << 27) ^ ((c.A[4] ^ D4) >> (64 - 27)); // 4, 15, 27, D4
c.A[4] = ((c.A[24] ^ D4) << 14) ^ ((c.A[24] ^ D4) >> (64 - 14)); // 24, 4, 14, D4
c.A[24] = ((c.A[21] ^ D1) << 2) ^ ((c.A[21] ^ D1) >> (64 - 2)); // 21, 24, 2, D1
c.A[21] = ((c.A[8] ^ D3) << 55) ^ ((c.A[8] ^ D3) >> (64 - 55)); // 8, 21, 55, D3
c.A[8] = ((c.A[16] ^ D1) << 45) ^ ((c.A[16] ^ D1) >> (64 - 45)); // 16, 8, 45, D1
c.A[16] = ((c.A[5] ^ D0) << 36) ^ ((c.A[5] ^ D0) >> (64 - 36)); // 5, 16, 36, D0
c.A[5] = ((c.A[3] ^ D3) << 28) ^ ((c.A[3] ^ D3) >> (64 - 28)); // 3, 5, 28, D3
c.A[3] = ((c.A[18] ^ D3) << 21) ^ ((c.A[18] ^ D3) >> (64 - 21)); // 18, 3, 21, D3
c.A[18] = ((c.A[17] ^ D2) << 15) ^ ((c.A[17] ^ D2) >> (64 - 15)); // 17, 18, 15, D2
c.A[17] = ((c.A[11] ^ D1) << 10) ^ ((c.A[11] ^ D1) >> (64 - 10)); // 11, 17, 10, D1
c.A[11] = ((c.A[7] ^ D2) << 6) ^ ((c.A[7] ^ D2) >> (64 - 6)); // 7, 11, 6, D2
c.A[7] = ((c.A[10] ^ D0) << 3) ^ ((c.A[10] ^ D0) >> (64 - 3)); // 10, 7, 3, D0
c.A[6] = ((c.A[9] ^ D4) << 20) ^ ((c.A[9] ^ D4) >> (64 - 20));
c.A[9] = ((c.A[22] ^ D2) << 61) ^ ((c.A[22] ^ D2) >> (64 - 61));
c.A[22] = ((c.A[14] ^ D4) << 39) ^ ((c.A[14] ^ D4) >> (64 - 39));
c.A[14] = ((c.A[20] ^ D0) << 18) ^ ((c.A[20] ^ D0) >> (64 - 18));
c.A[20] = ((c.A[2] ^ D2) << 62) ^ ((c.A[2] ^ D2) >> (64 - 62));
c.A[2] = ((c.A[12] ^ D2) << 43) ^ ((c.A[12] ^ D2) >> (64 - 43));
c.A[12] = ((c.A[13] ^ D3) << 25) ^ ((c.A[13] ^ D3) >> (64 - 25));
c.A[13] = ((c.A[19] ^ D4) << 8) ^ ((c.A[19] ^ D4) >> (64 - 8));
c.A[19] = ((c.A[23] ^ D3) << 56) ^ ((c.A[23] ^ D3) >> (64 - 56));
c.A[23] = ((c.A[15] ^ D0) << 41) ^ ((c.A[15] ^ D0) >> (64 - 41));
c.A[15] = ((c.A[4] ^ D4) << 27) ^ ((c.A[4] ^ D4) >> (64 - 27));
c.A[4] = ((c.A[24] ^ D4) << 14) ^ ((c.A[24] ^ D4) >> (64 - 14));
c.A[24] = ((c.A[21] ^ D1) << 2) ^ ((c.A[21] ^ D1) >> (64 - 2));
c.A[21] = ((c.A[8] ^ D3) << 55) ^ ((c.A[8] ^ D3) >> (64 - 55));
c.A[8] = ((c.A[16] ^ D1) << 45) ^ ((c.A[16] ^ D1) >> (64 - 45));
c.A[16] = ((c.A[5] ^ D0) << 36) ^ ((c.A[5] ^ D0) >> (64 - 36));
c.A[5] = ((c.A[3] ^ D3) << 28) ^ ((c.A[3] ^ D3) >> (64 - 28));
c.A[3] = ((c.A[18] ^ D3) << 21) ^ ((c.A[18] ^ D3) >> (64 - 21));
c.A[18] = ((c.A[17] ^ D2) << 15) ^ ((c.A[17] ^ D2) >> (64 - 15));
c.A[17] = ((c.A[11] ^ D1) << 10) ^ ((c.A[11] ^ D1) >> (64 - 10));
c.A[11] = ((c.A[7] ^ D2) << 6) ^ ((c.A[7] ^ D2) >> (64 - 6));
c.A[7] = ((c.A[10] ^ D0) << 3) ^ ((c.A[10] ^ D0) >> (64 - 3));
c.A[10] = A1;
}

function keccak_chi(CTX memory c) internal pure {
uint256 i;
uint64 A0;
uint64 A1;
uint64 A2;
uint64 A3;
uint64 A4;
for (i = 0; i < 25; i += 5) {
A0 = c.A[0 + i];
A1 = c.A[1 + i];
A2 = c.A[2 + i];
A3 = c.A[3 + i];
A4 = c.A[4 + i];
c.A[0 + i] ^= ~A1 & A2;
c.A[1 + i] ^= ~A2 & A3;
c.A[2 + i] ^= ~A3 & A4;
c.A[3 + i] ^= ~A4 & A0;
c.A[4 + i] ^= ~A0 & A1;
assembly {
// fetch a state element from the passed `StateMatrix` struct memory ptr.
function stateElem(ptr, idx) -> elem {
elem := mload(add(ptr, shl(0x05, idx)))
}

// set a state element in the passed `StateMatrix` struct memory ptr.
function setStateElem(ptr, idx, data) {
mstore(add(ptr, shl(0x05, idx)), and(data, U64_MASK))
}

// Inner `chi` function, unrolled in `chi` for performance.
function innerChi(ptr, start) {
let A0 := stateElem(ptr, start)
let A1 := stateElem(ptr, add(start, 1))
let A2 := stateElem(ptr, add(start, 2))
let A3 := stateElem(ptr, add(start, 3))
let A4 := stateElem(ptr, add(start, 4))

setStateElem(ptr, start, xor(A0, and(not(A1), A2)))
setStateElem(ptr, add(start, 1), xor(A1, and(not(A2), A3)))
setStateElem(ptr, add(start, 2), xor(A2, and(not(A3), A4)))
setStateElem(ptr, add(start, 3), xor(A3, and(not(A4), A0)))
setStateElem(ptr, add(start, 4), xor(A4, and(not(A0), A1)))
}

// Performs the `chi` step of the Keccak-f[1600] permutation on the passed `StateMatrix` struct memory ptr
function chi(ptr) {
innerChi(ptr, 0)
innerChi(ptr, 5)
innerChi(ptr, 10)
innerChi(ptr, 15)
innerChi(ptr, 20)
}

chi(add(c, 0x20))
}
}

Expand All @@ -92,15 +123,66 @@ library Lib_Keccak256 {
}

function sha3_xor_input(CTX memory c, bytes memory dat) internal pure {
for (uint256 i = 0; i < 17; i++) {
uint256 bo = i * 8;
c.A[i] ^= uint64(uint8(dat[bo + 7])) << 56 | uint64(uint8(dat[bo + 6])) << 48
| uint64(uint8(dat[bo + 5])) << 40 | uint64(uint8(dat[bo + 4])) << 32 | uint64(uint8(dat[bo + 3])) << 24
| uint64(uint8(dat[bo + 2])) << 16 | uint64(uint8(dat[bo + 1])) << 8 | uint64(uint8(dat[bo + 0])) << 0;
assembly {
// The input must be 1088 bits long.
if iszero(eq(mload(dat), 136)) { revert(0, 0) }

let dataPtr := add(dat, 0x20)
let statePtr := add(c, 0x20)

// set a state element in the passed `StateMatrix` struct memory ptr.
function setStateElem(ptr, idx, data) {
mstore(add(ptr, shl(0x05, idx)), and(data, U64_MASK))
}

// fetch a state element from the passed `StateMatrix` struct memory ptr.
function stateElem(ptr, idx) -> elem {
elem := mload(add(ptr, shl(0x05, idx)))
}

// Inner sha3 absorb XOR function
function absorbInner(stateMatrixPtr, inputPtr, idx) {
let bo := shl(3, idx)
let boWord := mload(add(inputPtr, bo))

let res :=
or(
or(
or(shl(56, byte(7, boWord)), shl(48, byte(6, boWord))),
or(shl(40, byte(5, boWord)), shl(32, byte(4, boWord)))
),
or(
or(shl(24, byte(3, boWord)), shl(16, byte(2, boWord))),
or(shl(8, byte(1, boWord)), byte(0, boWord))
)
)
setStateElem(stateMatrixPtr, idx, xor(stateElem(stateMatrixPtr, idx), res))
}

// Unroll the input XOR loop.
absorbInner(statePtr, dataPtr, 0)
absorbInner(statePtr, dataPtr, 1)
absorbInner(statePtr, dataPtr, 2)
absorbInner(statePtr, dataPtr, 3)
absorbInner(statePtr, dataPtr, 4)
absorbInner(statePtr, dataPtr, 5)
absorbInner(statePtr, dataPtr, 6)
absorbInner(statePtr, dataPtr, 7)
absorbInner(statePtr, dataPtr, 8)
absorbInner(statePtr, dataPtr, 9)
absorbInner(statePtr, dataPtr, 10)
absorbInner(statePtr, dataPtr, 11)
absorbInner(statePtr, dataPtr, 12)
absorbInner(statePtr, dataPtr, 13)
absorbInner(statePtr, dataPtr, 14)
absorbInner(statePtr, dataPtr, 15)
absorbInner(statePtr, dataPtr, 16)
}
}

function sha3_permutation(CTX memory c) internal pure {
bytes memory rounds = ROUND_CONSTANTS;

uint256 round;
for (round = 0; round < 24; round++) {
keccak_theta_rho_pi(c);
Expand All @@ -116,7 +198,21 @@ library Lib_Keccak256 {
// }
// }
// keccak_iota
c.A[0] ^= get_round_constant(round);
assembly {
// set a state element in the passed `StateMatrix` struct memory ptr.
function setStateElem(ptr, idx, data) {
mstore(add(ptr, shl(0x05, idx)), and(data, U64_MASK))
}

// fetch a state element from the passed `StateMatrix` struct memory ptr.
function stateElem(ptr, idx) -> elem {
elem := mload(add(ptr, shl(0x05, idx)))
}

let ptr := add(c, 0x20)
let roundConst := shr(192, mload(add(add(rounds, 0x20), shl(0x03, round))))
setStateElem(ptr, 0, xor(stateElem(ptr, 0), roundConst))
}
}
}

Expand All @@ -127,10 +223,28 @@ library Lib_Keccak256 {
return (val << 32) | (val >> 32);
}

function get_hash(CTX memory c) internal pure returns (bytes32) {
return bytes32(
(uint256(flip(c.A[0])) << 192) | (uint256(flip(c.A[1])) << 128) | (uint256(flip(c.A[2])) << 64)
| (uint256(flip(c.A[3])) << 0)
);
function get_hash(CTX memory c) internal pure returns (bytes32 hash_) {
assembly {
// convert a big endian 64-bit value to a little endian 64-bit value.
function toLE(beVal) -> leVal {
beVal :=
or(and(and(shl(8, beVal), U64_MASK), 0xFF00FF00FF00FF00), and(shr(8, beVal), 0x00FF00FF00FF00FF))
beVal :=
or(and(and(shl(16, beVal), U64_MASK), 0xFFFF0000FFFF0000), and(shr(16, beVal), 0x0000FFFF0000FFFF))
leVal := or(and(shl(32, beVal), U64_MASK), shr(32, beVal))
}

// fetch a state element from the passed `StateMatrix` struct memory ptr.
function stateElem(ptr, idx) -> elem {
elem := mload(add(ptr, shl(0x05, idx)))
}

let stateMatrixPtr := add(c, 0x20)
hash_ :=
or(
or(shl(192, toLE(stateElem(stateMatrixPtr, 0))), shl(128, toLE(stateElem(stateMatrixPtr, 1)))),
or(shl(64, toLE(stateElem(stateMatrixPtr, 2))), toLE(stateElem(stateMatrixPtr, 3)))
)
}
}
}
65 changes: 35 additions & 30 deletions contracts/lib/LibKeccak.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
pragma solidity 0.8.15;

/// @title LibKeccak
/// @notice An EVM implementation of the Keccak-f[1600] permutation.
Expand Down Expand Up @@ -58,10 +58,10 @@ library LibKeccak {
}

// Performs an indivudual rho + pi computation, to be used in the full `thetaRhoPi` chain.
function rhoPi(ptr, src, dest, fact, dt) {
let xs1 := xor(stateElem(ptr, src), dt)
function rhoPi(ptr, destIdx, srcIdx, fact, dt) {
let xs1 := xor(stateElem(ptr, srcIdx), dt)
let res := xor(shl64(fact, xs1), shr(sub(64, fact), xs1))
setStateElem(ptr, dest, res)
setStateElem(ptr, destIdx, res)
}

// Performs the `theta`, `rho`, and `pi` steps of the Keccak-f[1600] permutation on
Expand All @@ -84,29 +84,29 @@ library LibKeccak {
let A1 := xor(shl64(1, xs1), shr(63, xs1))

setStateElem(ptr, 0, xor(stateElem(ptr, 0), D0))
rhoPi(ptr, 6, 1, 44, D1) // 6, 1, 44, D1
rhoPi(ptr, 9, 6, 20, D4) // 9, 6, 20, D4
rhoPi(ptr, 22, 9, 61, D2) // 22, 9, 61, D2
rhoPi(ptr, 14, 22, 39, D4) // 14, 22, 39, D4
rhoPi(ptr, 20, 14, 18, D0) // 20, 14, 18, D0
rhoPi(ptr, 2, 20, 62, D2) // 2, 20, 62, D2
rhoPi(ptr, 12, 2, 43, D2) // 12, 2, 43, D2
rhoPi(ptr, 13, 12, 25, D3) // 13, 12, 25, D3
rhoPi(ptr, 19, 13, 8, D4) // 19, 13, 8, D4
rhoPi(ptr, 23, 19, 56, D3) // 23, 19, 56, D3
rhoPi(ptr, 15, 23, 41, D0) // 15, 23, 41, D0
rhoPi(ptr, 4, 15, 27, D4) // 4, 15, 27, D4
rhoPi(ptr, 24, 4, 14, D4) // 24, 4, 14, D4
rhoPi(ptr, 21, 24, 2, D1) // 21, 24, 2, D1
rhoPi(ptr, 8, 21, 55, D3) // 8, 21, 55, D3
rhoPi(ptr, 16, 8, 45, D1) // 16, 8, 45, D1
rhoPi(ptr, 5, 16, 36, D0) // 5, 16, 36, D0
rhoPi(ptr, 3, 5, 28, D3) // 3, 5, 28, D3
rhoPi(ptr, 18, 3, 21, D3) // 18, 3, 21, D3
rhoPi(ptr, 17, 18, 15, D2) // 17, 18, 15, D2
rhoPi(ptr, 11, 17, 10, D1) // 11, 17, 10, D1
rhoPi(ptr, 7, 11, 6, D2) // 7, 11, 6, D2
rhoPi(ptr, 10, 7, 3, D0) // 10, 7, 3, D0
rhoPi(ptr, 1, 6, 44, D1)
rhoPi(ptr, 6, 9, 20, D4)
rhoPi(ptr, 9, 22, 61, D2)
rhoPi(ptr, 22, 14, 39, D4)
rhoPi(ptr, 14, 20, 18, D0)
rhoPi(ptr, 20, 2, 62, D2)
rhoPi(ptr, 2, 12, 43, D2)
rhoPi(ptr, 12, 13, 25, D3)
rhoPi(ptr, 13, 19, 8, D4)
rhoPi(ptr, 19, 23, 56, D3)
rhoPi(ptr, 23, 15, 41, D0)
rhoPi(ptr, 15, 4, 27, D4)
rhoPi(ptr, 4, 24, 14, D4)
rhoPi(ptr, 24, 21, 2, D1)
rhoPi(ptr, 21, 8, 55, D3)
rhoPi(ptr, 8, 16, 45, D1)
rhoPi(ptr, 16, 5, 36, D0)
rhoPi(ptr, 5, 3, 28, D3)
rhoPi(ptr, 3, 18, 21, D3)
rhoPi(ptr, 18, 17, 15, D2)
rhoPi(ptr, 17, 11, 10, D1)
rhoPi(ptr, 11, 7, 6, D2)
rhoPi(ptr, 7, 10, 3, D0)
setStateElem(ptr, 10, A1)
}

Expand Down Expand Up @@ -234,11 +234,16 @@ library LibKeccak {
/// @notice Squeezes the final keccak256 digest from the passed `StateMatrix`.
function squeeze(StateMatrix memory _stateMatrix) internal pure returns (bytes32 hash_) {
assembly {
// 64 bit logical shift
function shl64(a, b) -> val {
val := and(shl(a, b), U64_MASK)
}

// convert a big endian 64-bit value to a little endian 64-bit value.
function toLE(beVal) -> leVal {
beVal := or(and(shl(8, beVal), 0xFF00FF00FF00FF00), and(shr(8, beVal), 0x00FF00FF00FF00FF))
beVal := or(and(shl(16, beVal), 0xFFFF0000FFFF0000), and(shr(16, beVal), 0x0000FFFF0000FFFF))
leVal := or(shl(32, beVal), shr(32, beVal))
beVal := or(and(shl64(8, beVal), 0xFF00FF00FF00FF00), and(shr(8, beVal), 0x00FF00FF00FF00FF))
beVal := or(and(shl64(16, beVal), 0xFFFF0000FFFF0000), and(shr(16, beVal), 0x0000FFFF0000FFFF))
leVal := or(shl64(32, beVal), shr(32, beVal))
}

// fetch a state element from the passed `StateMatrix` struct memory ptr.
Expand Down
2 changes: 1 addition & 1 deletion test/LibKeccak.t.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
pragma solidity 0.8.15;

import { Test, console2 as console } from "forge-std/Test.sol";

Expand Down

0 comments on commit 3e294db

Please sign in to comment.