Skip to content

Commit

Permalink
[crypto+move] added support for SHA2-512, SHA3-512 and RIPEMD-160 in …
Browse files Browse the repository at this point in the history
…Move
  • Loading branch information
alinush committed Oct 28, 2022
1 parent 8958b01 commit 4026229
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 1 deletion.
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
/aptos-move/framework/aptos-framework/sources/account.move @alinush
/aptos-move/framework/aptos-stdlib/sources/cryptography/ @alinush
/aptos-move/framework/**/*.spec.move @junkil-park
/aptos-move/framework/aptos-stdlib/sources/hash.move @alinush

# Owner for aptos-token, cryptography natives, parallel-executor and vm-genesis.
/aptos-move/framework/aptos-token @areshand
Expand Down
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions aptos-move/aptos-gas/src/aptos_framework.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@ crate::natives::define_gas_parameters_for_natives!(GasParameters, "aptos_framewo
// TODO(Gas): the on-chain name is wrong...
[.type_info.type_name.per_byte_in_str, "type_info.type_name.per_abstract_memory_unit", 5 * MUL],

// Reusing SHA2-512's cost from Ristretto
[.hash.sha2_512.base, optional "hash.sha2_512.base", 3_240],
[.hash.sha2_512.per_byte, optional "hash.sha2_512.per_byte", 60],
// Back-of-the-envelop approximation from SHA3-256's (4000 base, 45 per-byte) costs
[.hash.sha3_512.base, optional "hash.sha3_512.base", 4_500],
[.hash.sha3_512.per_byte, optional "hash.sha3_512.per_byte", 50],
// Using SHA2-256's cost
[.hash.ripemd160.base, optional "hash.ripemd160.base", 3000],
[.hash.ripemd160.per_byte, optional "hash.ripemd160.per_byte", 50],

[.util.from_bytes.base, "util.from_bytes.base", 300 * MUL],
[.util.from_bytes.per_byte, "util.from_bytes.per_byte", 5 * MUL],

Expand Down
2 changes: 2 additions & 0 deletions aptos-move/framework/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,13 @@ once_cell = "1.10.0"
proptest = { version = "1.0.0", optional = true }
proptest-derive = { version = "0.3.0", optional = true }
rayon = "1.5.2"
ripemd = "0.1.1"
serde = { version = "1.0.137", default-features = false }
serde_bytes = "0.11.6"
serde_json = "1.0.81"
serde_yaml = "0.8.24"
sha2 = "0.9.3"
sha3 = "0.9.1"
siphasher = "0.3.10"
smallvec = "1.8.0"
tempfile = "3.3.0"
Expand Down
84 changes: 84 additions & 0 deletions aptos-move/framework/aptos-stdlib/sources/hash.move
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ module aptos_std::aptos_hash {

native public fun keccak256(bytes: vector<u8>): vector<u8>;

native public fun sha2_512(bytes: vector<u8>): vector<u8>;

native public fun sha3_512(bytes: vector<u8>): vector<u8>;

/// WARNING: Only 80-bit security is provided by this function. This means an adversary who can compute roughly 2^80
/// hashes will, with high probability, find a collision x_1 != x_2 such that RIPEMD-160(x_1) = RIPEMD-160(x_2).
native public fun ripemd160(bytes: vector<u8>): vector<u8>;

//
// Testing
//
Expand Down Expand Up @@ -50,4 +58,80 @@ module aptos_std::aptos_hash {
i = i + 1;
};
}

#[test]
fun sha2_512_test() {
let inputs = vector[
b"testing",
b"",
];

// From https://emn178.github.io/online-tools/sha512.html
let outputs = vector[
x"521b9ccefbcd14d179e7a1bb877752870a6d620938b28a66a107eac6e6805b9d0989f45b5730508041aa5e710847d439ea74cd312c9355f1f2dae08d40e41d50",
x"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e",
];

let i = 0;
while (i < std::vector::length(&inputs)) {
let input = *std::vector::borrow(&inputs, i);
let hash_expected = *std::vector::borrow(&outputs, i);
let hash = sha2_512(input);

assert!(hash_expected == hash, 1);

i = i + 1;
};
}

#[test]
fun sha3_512_test() {
let inputs = vector[
b"testing",
b"",
];

// From https://emn178.github.io/online-tools/sha3_512.html
let outputs = vector[
x"881c7d6ba98678bcd96e253086c4048c3ea15306d0d13ff48341c6285ee71102a47b6f16e20e4d65c0c3d677be689dfda6d326695609cbadfafa1800e9eb7fc1",
x"a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26",
];

let i = 0;
while (i < std::vector::length(&inputs)) {
let input = *std::vector::borrow(&inputs, i);
let hash_expected = *std::vector::borrow(&outputs, i);
let hash = sha3_512(input);

assert!(hash_expected == hash, 1);

i = i + 1;
};
}


#[test]
fun ripemd160_test() {
let inputs = vector[
b"testing",
b"",
];

// From https://www.browserling.com/tools/ripemd160-hash
let outputs = vector[
x"b89ba156b40bed29a5965684b7d244c49a3a769b",
x"9c1185a5c5e9fc54612808977ee8f548b2258d31",
];

let i = 0;
while (i < std::vector::length(&inputs)) {
let input = *std::vector::borrow(&inputs, i);
let hash_expected = *std::vector::borrow(&outputs, i);
let hash = ripemd160(input);

assert!(hash_expected == hash, 1);

i = i + 1;
};
}
}
21 changes: 21 additions & 0 deletions aptos-move/framework/aptos-stdlib/sources/hash.spec.move
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,25 @@ spec aptos_std::aptos_hash {
// TODO: temporary mockup.
pragma opaque;
}

spec keccak256 {
// TODO: temporary mockup.
pragma opaque;
}

spec sha2_512 {
// TODO: temporary mockup.
pragma opaque;
}

spec sha3_512 {
// TODO: temporary mockup.
pragma opaque;
}

spec ripemd160 {
// TODO: temporary mockup.
pragma opaque;
}

}
96 changes: 95 additions & 1 deletion aptos-move/framework/src/natives/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use move_vm_runtime::native_functions::{NativeContext, NativeFunction};
use move_vm_types::{
loaded_data::runtime_types::Type, natives::function::NativeResult, pop_arg, values::Value,
};

use ripemd::Digest as OtherDigest;
use sha2::Digest;
use smallvec::smallvec;
use std::{collections::VecDeque, hash::Hasher};
use tiny_keccak::{Hasher as KeccakHasher, Keccak};
Expand Down Expand Up @@ -75,6 +76,84 @@ fn native_keccak256(
Ok(NativeResult::ok(cost, smallvec![Value::vector_u8(output)]))
}

#[derive(Debug, Clone)]
pub struct Sha2_512HashGasParameters {
pub base: InternalGas,
pub per_byte: InternalGasPerByte,
}

fn native_sha2_512(
gas_params: &Sha2_512HashGasParameters,
_context: &mut NativeContext,
mut _ty_args: Vec<Type>,
mut args: VecDeque<Value>,
) -> PartialVMResult<NativeResult> {
debug_assert!(_ty_args.is_empty());
debug_assert!(args.len() == 1);

let bytes = pop_arg!(args, Vec<u8>);

let cost = gas_params.base + gas_params.per_byte * NumBytes::new(bytes.len() as u64);

let mut hasher = sha2::Sha512::new();
hasher.update(&bytes);
let output = hasher.finalize().to_vec();

Ok(NativeResult::ok(cost, smallvec![Value::vector_u8(output)]))
}

#[derive(Debug, Clone)]
pub struct Sha3_512HashGasParameters {
pub base: InternalGas,
pub per_byte: InternalGasPerByte,
}

fn native_sha3_512(
gas_params: &Sha3_512HashGasParameters,
_context: &mut NativeContext,
mut _ty_args: Vec<Type>,
mut args: VecDeque<Value>,
) -> PartialVMResult<NativeResult> {
debug_assert!(_ty_args.is_empty());
debug_assert!(args.len() == 1);

let bytes = pop_arg!(args, Vec<u8>);

let cost = gas_params.base + gas_params.per_byte * NumBytes::new(bytes.len() as u64);

let mut hasher = sha3::Sha3_512::new();
hasher.update(&bytes);
let output = hasher.finalize().to_vec();

Ok(NativeResult::ok(cost, smallvec![Value::vector_u8(output)]))
}

#[derive(Debug, Clone)]
pub struct Ripemd160HashGasParameters {
pub base: InternalGas,
pub per_byte: InternalGasPerByte,
}

fn native_ripemd160(
gas_params: &Ripemd160HashGasParameters,
_context: &mut NativeContext,
mut _ty_args: Vec<Type>,
mut args: VecDeque<Value>,
) -> PartialVMResult<NativeResult> {
debug_assert!(_ty_args.is_empty());
debug_assert!(args.len() == 1);

let bytes = pop_arg!(args, Vec<u8>);

let cost = gas_params.base + gas_params.per_byte * NumBytes::new(bytes.len() as u64);

let mut hasher = ripemd::Ripemd160::new();
hasher.update(&bytes);
let output = hasher.finalize().to_vec();

Ok(NativeResult::ok(cost, smallvec![Value::vector_u8(output)]))
}

/***************************************************************************************************
* module
*
Expand All @@ -83,6 +162,9 @@ fn native_keccak256(
pub struct GasParameters {
pub sip_hash: SipHashGasParameters,
pub keccak256: Keccak256HashGasParameters,
pub sha2_512: Sha2_512HashGasParameters,
pub sha3_512: Sha3_512HashGasParameters,
pub ripemd160: Ripemd160HashGasParameters,
}

pub fn make_all(gas_params: GasParameters) -> impl Iterator<Item = (String, NativeFunction)> {
Expand All @@ -95,6 +177,18 @@ pub fn make_all(gas_params: GasParameters) -> impl Iterator<Item = (String, Nati
"keccak256",
make_native_from_func(gas_params.keccak256, native_keccak256),
),
(
"sha2_512",
make_native_from_func(gas_params.sha2_512, native_sha2_512),
),
(
"sha3_512",
make_native_from_func(gas_params.sha3_512, native_sha3_512),
),
(
"ripemd160",
make_native_from_func(gas_params.ripemd160, native_ripemd160),
),
];

crate::natives::helpers::make_module_natives(natives)
Expand Down
12 changes: 12 additions & 0 deletions aptos-move/framework/src/natives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,18 @@ impl GasParameters {
base: 0.into(),
per_byte: 0.into(),
},
sha2_512: hash::Sha2_512HashGasParameters {
base: 0.into(),
per_byte: 0.into(),
},
sha3_512: hash::Sha3_512HashGasParameters {
base: 0.into(),
per_byte: 0.into(),
},
ripemd160: hash::Ripemd160HashGasParameters {
base: 0.into(),
per_byte: 0.into(),
},
},
type_info: type_info::GasParameters {
type_of: type_info::TypeOfGasParameters {
Expand Down

0 comments on commit 4026229

Please sign in to comment.