Skip to content

Commit ef2a91a

Browse files
authored
Add RIPEMD160 hash function and EVM precompile (#505)
* [ripemd160] add port of bitcoin-core ripemd160 implementation * [ripemd160] add `ripemd160` type with standard hash API * [ripemd160] fix two hasty errors * [ripemd160] use `reset` in `initialize` to `setZero` on buffer too Given that the docstring explicitly promises to also reinitialize, this is the safer bet. * [hashes] add ripemd160 to list of supported hashes * [precompiles] add precompile for RIPEMD160 * [tests] add test case for RIPEMD160 precompile * [openssl] add wrapper for RIPEMD160 hash function via OpenSSL * [ripemd160] remove left over template & `isMainModule` code * [ripemd160] export Ripemd160Context from `h_ripemd160` In this case the context is already defined in the generic implementation, so we just export it again. * [tests] add more RIPEMD160 test vectors & fuzzing against OpenSSL * add RIPEMD160 test vs OpenSSL to nimble file
1 parent 2582fb5 commit ef2a91a

8 files changed

+590
-2
lines changed

constantine.nimble

+1
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[
365365
# ----------------------------------------------------------
366366
("tests/t_hash_sha256_vs_openssl.nim", false),
367367
("tests/t_hash_keccak_sha3_vs_openssl.nim", false),
368+
("tests/t_hash_ripemd160_vs_openssl.nim", false),
368369

369370
# Ciphers
370371
# ----------------------------------------------------------

constantine/ethereum_evm_precompiles.nim

+19
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,25 @@ func eth_evm_sha256*(r: var openArray[byte], inputs: openArray[byte]): CttEVMSta
6767
sha256.hash(cast[ptr array[32, byte]](r[0].addr)[], inputs)
6868
return cttEVM_Success
6969

70+
func eth_evm_ripemd160*(r: var openArray[byte], inputs: openArray[byte]): CttEVMStatus {.libPrefix: prefix_ffi, meter.} =
71+
## RIPEMD160
72+
##
73+
## Inputs:
74+
## - Message to hash
75+
##
76+
## Output:
77+
## - 32-byte digest (first 12 bytes zero)
78+
## - status code:
79+
## cttEVM_Success
80+
## cttEVM_InvalidOutputSize
81+
82+
if r.len != 32:
83+
return cttEVM_InvalidOutputSize
84+
85+
# Need to only write to last 20 bytes. Hence fist `toOpenArray` & then cast & deref
86+
ripemd160.hash(cast[ptr array[20, byte]](toOpenArray(r, 12, 31)[0].addr)[], inputs)
87+
return cttEVM_Success
88+
7089
func eth_evm_modexp_result_size*(size: var uint64, inputs: openArray[byte]): CttEVMStatus {.noInline, tags:[Alloca, Vartime], libPrefix: prefix_ffi, meter.} =
7190
## Helper for `eth_evm_modexp`. Returns the size required to be allocated based on the
7291
## given input. Call this function first, then allocate space for the result buffer

constantine/hashes.nim

+5-2
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,16 @@ func hash*(
6565

6666
import ./hashes/[
6767
h_keccak,
68-
h_sha256
68+
h_sha256,
69+
h_ripemd160
6970
]
7071
export
7172
h_keccak,
72-
h_sha256
73+
h_sha256,
74+
h_ripemd160
7375

7476
static:
7577
doAssert keccak256 is CryptoHash
7678
doAssert sha256 is CryptoHash
7779
doAssert sha3_256 is CryptoHash
80+
doAssert ripemd160 is CryptoHash

constantine/hashes/h_ripemd160.nim

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Constantine
2+
# Copyright (c) 2018-2019 Status Research & Development GmbH
3+
# Copyright (c) 2020-Present Mamy André-Ratsimbazafy
4+
# Licensed and distributed under either of
5+
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
6+
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
7+
# at your option. This file may not be copied, modified, or distributed except according to those terms.
8+
9+
import constantine/zoo_exports
10+
11+
import
12+
constantine/platforms/[abstractions, views],
13+
constantine/serialization/endians,
14+
./ripemd160/ripemd160_generic
15+
16+
17+
# RIPEMD-160, a hash function from the RIPE family
18+
# --------------------------------------------------------------------------------
19+
#
20+
# References:
21+
# - ISO: ISO/IEC 10118-3:2004, https://www.iso.org/standard/67116.html (latest revision)
22+
# - https://homes.esat.kuleuven.be/~bosselae/ripemd160.html
23+
# -> Includes a reference implementation in C, however only accessible via the Wayback Machine
24+
# as of Dec 2024.
25+
# - Bitcoin implementation:
26+
# https://github.com/bitcoin-core/btcdeb/blob/e2c2e7b9fe2ecc0884129b53813a733f93a6e2c7/crypto/ripemd160.cpp#L242
27+
#
28+
# Vectors:
29+
# - https://homes.esat.kuleuven.be/~bosselae/ripemd160.html
30+
# - [ ] Find Bitcoin vectors
31+
32+
# Types and constants
33+
# ----------------------------------------------------------------
34+
35+
type
36+
ripemd160* = Ripemd160Context # defined in generic file atm
37+
38+
export Ripemd160Context
39+
40+
# Internals
41+
# ----------------------------------------------------------------
42+
# defined in `ripemd160/ripemd160_generic.nim` at the moment
43+
44+
# No exceptions allowed in core cryptographic operations
45+
{.push raises: [].}
46+
{.push checks: off.}
47+
48+
# Public API
49+
# ----------------------------------------------------------------
50+
51+
template digestSize*(H: type ripemd160): int =
52+
## Returns the output size in bytes
53+
DigestSize
54+
55+
template internalBlockSize*(H: type ripemd160): int =
56+
## Returns the byte size of the hash function ingested blocks
57+
BlockSize
58+
59+
func init*(ctx: var Ripemd160Context) =
60+
## Initialize or reinitialize a Ripemd160 context
61+
ctx.reset()
62+
63+
func update*(ctx: var Ripemd160Context, message: openarray[byte]) =
64+
## Append a message to a Ripemd160 context for incremental Ripemd160 computation.
65+
##
66+
## Security note: the tail of your message might be stored
67+
## in an internal buffer.
68+
## if sensitive content is used, ensure that
69+
## `ctx.finish(...)` and `ctx.clear()` are called as soon as possible.
70+
## Additionally ensure that the message(s) passed was(were) stored
71+
## in memory considered secure for your threat model.
72+
ctx.write(message, message.len.uint64)
73+
74+
func finish*(ctx: var Ripemd160Context, digest: var array[DigestSize, byte]) =
75+
## Finalize a Ripemd160 computation and output the
76+
## message digest to the `digest` buffer.
77+
##
78+
## Security note: this does not clear the internal buffer.
79+
## if sensitive content is used, use "ctx.clear()"
80+
## and also make sure that the message(s) passed were stored
81+
## in memory considered secure for your threat model.
82+
ctx.finalize(digest)
83+
84+
func clear*(ctx: var Ripemd160Context) =
85+
## Clear the context internal buffers
86+
# TODO: ensure compiler cannot optimize the code away
87+
ctx.reset()

0 commit comments

Comments
 (0)