igneum/tools/reference-apps/oracle/contracts/Blake2b.sol

85 lines
3.7 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
// Keyed BLAKE2b-256 through the EIP-152 compression precompile at address 0x09.
// The precompile input is 213 bytes: rounds (u32 big-endian), h (8 u64 little-endian),
// m (128 bytes), t (2 u64 little-endian), f (one byte). It returns the new h (64 bytes).
// Parameter block: h0 ^= 0x01010000 ^ (keylen << 8) ^ outlen, with outlen 32.
// The key padded to 128 bytes is the first block with t = 128 and f = 0.
// The message follows in 128-byte blocks; the last block carries f = 1 and t = 128 + total bytes.
library Blake2b {
uint64 private constant IV0 = 0x6a09e667f3bcc908;
uint64 private constant IV1 = 0xbb67ae8584caa73b;
uint64 private constant IV2 = 0x3c6ef372fe94f82b;
uint64 private constant IV3 = 0xa54ff53a5f1d36f1;
uint64 private constant IV4 = 0x510e527fade682d1;
uint64 private constant IV5 = 0x9b05688c2b3e6c1f;
uint64 private constant IV6 = 0x1f83d9abfb41bd6b;
uint64 private constant IV7 = 0x5be0cd19137e2179;
function swap64(uint64 v) internal pure returns (uint64 r) {
r = ((v & 0x00000000000000FF) << 56) | ((v & 0x000000000000FF00) << 40)
| ((v & 0x0000000000FF0000) << 24) | ((v & 0x00000000FF000000) << 8)
| ((v & 0x000000FF00000000) >> 8) | ((v & 0x0000FF0000000000) >> 24)
| ((v & 0x00FF000000000000) >> 40) | ((v & 0xFF00000000000000) >> 56);
}
// The 32-byte keyed digest of `data`. The key is at most 64 bytes and never empty here.
function hash256(bytes memory key, bytes memory data) internal view returns (bytes32 out) {
require(key.length > 0 && key.length <= 64, "blake2b: key length");
bytes memory buf = new bytes(213);
uint64[8] memory h = [IV0, IV1, IV2, IV3, IV4, IV5, IV6, IV7];
h[0] ^= uint64(0x01010000) ^ (uint64(key.length) << 8) ^ uint64(32);
assembly {
let p := add(buf, 32)
mstore8(add(p, 3), 12) // rounds, big-endian u32 = 12
for { let i := 0 } lt(i, 8) { i := add(i, 1) } {
let w := mload(add(h, mul(i, 32)))
// byte-swap the u64 into little-endian
let s := 0
for { let k := 0 } lt(k, 8) { k := add(k, 1) } {
s := or(shl(8, s), and(shr(mul(k, 8), w), 0xff))
}
mstore(add(p, add(4, mul(i, 8))), shl(192, s))
}
}
uint256 n = data.length;
// The key block
_zeroBlock(buf);
assembly { mcopy(add(buf, 100), add(key, 32), mload(key)) }
_compress(buf, 128, n == 0);
// The message blocks
uint256 done = 0;
while (done < n) {
uint256 take = n - done;
if (take > 128) take = 128;
if (take < 128) _zeroBlock(buf);
assembly { mcopy(add(buf, 100), add(add(data, 32), done), take) }
done += take;
_compress(buf, 128 + done, done == n);
}
assembly { out := mload(add(buf, 36)) }
}
function _zeroBlock(bytes memory buf) private pure {
assembly {
let m := add(buf, 100)
mstore(m, 0)
mstore(add(m, 32), 0)
mstore(add(m, 64), 0)
mstore(add(m, 96), 0)
}
}
function _compress(bytes memory buf, uint256 t, bool last) private view {
uint64 tle = swap64(uint64(t));
bool ok;
assembly {
let p := add(buf, 32)
mstore(add(p, 196), shl(192, tle)) // t0 little-endian, then zeros for t1 and f
if last { mstore8(add(p, 212), 1) }
ok := staticcall(gas(), 0x09, p, 213, add(p, 4), 64)
}
require(ok, "blake2b: precompile");
}
}