85 lines
3.7 KiB
Solidity
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");
|
|
}
|
|
}
|