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

Add RIPEMD160 hash function and EVM precompile #505

Merged
merged 12 commits into from
Jan 13, 2025
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
1 change: 1 addition & 0 deletions constantine.nimble
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[
# ----------------------------------------------------------
("tests/t_hash_sha256_vs_openssl.nim", false),
("tests/t_hash_keccak_sha3_vs_openssl.nim", false),
("tests/t_hash_ripemd160_vs_openssl.nim", false),

# Ciphers
# ----------------------------------------------------------
Expand Down
19 changes: 19 additions & 0 deletions constantine/ethereum_evm_precompiles.nim
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,25 @@ func eth_evm_sha256*(r: var openArray[byte], inputs: openArray[byte]): CttEVMSta
sha256.hash(cast[ptr array[32, byte]](r[0].addr)[], inputs)
return cttEVM_Success

func eth_evm_ripemd160*(r: var openArray[byte], inputs: openArray[byte]): CttEVMStatus {.libPrefix: prefix_ffi, meter.} =
## RIPEMD160
##
## Inputs:
## - Message to hash
##
## Output:
## - 32-byte digest (first 12 bytes zero)
## - status code:
## cttEVM_Success
## cttEVM_InvalidOutputSize

if r.len != 32:
return cttEVM_InvalidOutputSize

# Need to only write to last 20 bytes. Hence fist `toOpenArray` & then cast & deref
ripemd160.hash(cast[ptr array[20, byte]](toOpenArray(r, 12, 31)[0].addr)[], inputs)
return cttEVM_Success

func eth_evm_modexp_result_size*(size: var uint64, inputs: openArray[byte]): CttEVMStatus {.noInline, tags:[Alloca, Vartime], libPrefix: prefix_ffi, meter.} =
## Helper for `eth_evm_modexp`. Returns the size required to be allocated based on the
## given input. Call this function first, then allocate space for the result buffer
Expand Down
7 changes: 5 additions & 2 deletions constantine/hashes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,16 @@ func hash*(

import ./hashes/[
h_keccak,
h_sha256
h_sha256,
h_ripemd160
]
export
h_keccak,
h_sha256
h_sha256,
h_ripemd160

static:
doAssert keccak256 is CryptoHash
doAssert sha256 is CryptoHash
doAssert sha3_256 is CryptoHash
doAssert ripemd160 is CryptoHash
87 changes: 87 additions & 0 deletions constantine/hashes/h_ripemd160.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Constantine
# Copyright (c) 2018-2019 Status Research & Development GmbH
# Copyright (c) 2020-Present Mamy André-Ratsimbazafy
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.

import constantine/zoo_exports

import
constantine/platforms/[abstractions, views],
constantine/serialization/endians,
./ripemd160/ripemd160_generic


# RIPEMD-160, a hash function from the RIPE family
# --------------------------------------------------------------------------------
#
# References:
# - ISO: ISO/IEC 10118-3:2004, https://www.iso.org/standard/67116.html (latest revision)
# - https://homes.esat.kuleuven.be/~bosselae/ripemd160.html
# -> Includes a reference implementation in C, however only accessible via the Wayback Machine
# as of Dec 2024.
# - Bitcoin implementation:
# https://github.com/bitcoin-core/btcdeb/blob/e2c2e7b9fe2ecc0884129b53813a733f93a6e2c7/crypto/ripemd160.cpp#L242
#
# Vectors:
# - https://homes.esat.kuleuven.be/~bosselae/ripemd160.html
# - [ ] Find Bitcoin vectors

# Types and constants
# ----------------------------------------------------------------

type
ripemd160* = Ripemd160Context # defined in generic file atm

export Ripemd160Context

# Internals
# ----------------------------------------------------------------
# defined in `ripemd160/ripemd160_generic.nim` at the moment

# No exceptions allowed in core cryptographic operations
{.push raises: [].}
{.push checks: off.}

# Public API
# ----------------------------------------------------------------

template digestSize*(H: type ripemd160): int =
## Returns the output size in bytes
DigestSize

template internalBlockSize*(H: type ripemd160): int =
## Returns the byte size of the hash function ingested blocks
BlockSize

func init*(ctx: var Ripemd160Context) =
## Initialize or reinitialize a Ripemd160 context
ctx.reset()

func update*(ctx: var Ripemd160Context, message: openarray[byte]) =
## Append a message to a Ripemd160 context for incremental Ripemd160 computation.
##
## Security note: the tail of your message might be stored
## in an internal buffer.
## if sensitive content is used, ensure that
## `ctx.finish(...)` and `ctx.clear()` are called as soon as possible.
## Additionally ensure that the message(s) passed was(were) stored
## in memory considered secure for your threat model.
ctx.write(message, message.len.uint64)

func finish*(ctx: var Ripemd160Context, digest: var array[DigestSize, byte]) =
## Finalize a Ripemd160 computation and output the
## message digest to the `digest` buffer.
##
## Security note: this does not clear the internal buffer.
## if sensitive content is used, use "ctx.clear()"
## and also make sure that the message(s) passed were stored
## in memory considered secure for your threat model.
ctx.finalize(digest)

func clear*(ctx: var Ripemd160Context) =
## Clear the context internal buffers
# TODO: ensure compiler cannot optimize the code away
ctx.reset()
Loading
Loading