mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-12 14:09:25 +00:00
This commit replaces all usage of `unwrap` with `expect` within `networks/monero`, clarifying why the panic risked is unreachable. This commit also replaces some uses of `unwrap` with solutions which are guaranteed not to fail. Notably, compilation on 128-bit systems is prevented, ensuring `u64::try_from(usize::MAX)` will never panic at runtime. Slight breaking changes are additionally included as necessary to massage out some avoidable panics.
72 lines
2.3 KiB
Rust
72 lines
2.3 KiB
Rust
use subtle::ConditionallySelectable;
|
|
|
|
use curve25519_dalek::edwards::EdwardsPoint;
|
|
|
|
use group::ff::{Field, PrimeField};
|
|
use dalek_ff_group::FieldElement;
|
|
|
|
use monero_io::decompress_point;
|
|
|
|
use crate::keccak256;
|
|
|
|
/// Monero's `hash_to_ec` function.
|
|
pub fn hash_to_point(bytes: [u8; 32]) -> EdwardsPoint {
|
|
#[allow(non_snake_case)]
|
|
let A = FieldElement::from(486662u64);
|
|
|
|
let v = FieldElement::from_square(keccak256(&bytes)).double();
|
|
let w = v + FieldElement::ONE;
|
|
let x = w.square() + (-A.square() * v);
|
|
|
|
// This isn't the complete X, yet its initial value
|
|
// We don't calculate the full X, and instead solely calculate Y, letting dalek reconstruct X
|
|
// While inefficient, it solves API boundaries and reduces the amount of work done here
|
|
#[allow(non_snake_case)]
|
|
let X = {
|
|
let u = w;
|
|
let v = x;
|
|
let v3 = v * v * v;
|
|
let uv3 = u * v3;
|
|
let v7 = v3 * v3 * v;
|
|
let uv7 = u * v7;
|
|
uv3 *
|
|
uv7.pow(
|
|
(-FieldElement::from(5u8)) *
|
|
FieldElement::from(8u8).invert().expect("eight was coprime with the prime 2^{255}-19"),
|
|
)
|
|
};
|
|
let x = X.square() * x;
|
|
|
|
let y = w - x;
|
|
let non_zero_0 = !y.is_zero();
|
|
let y_if_non_zero_0 = w + x;
|
|
let sign = non_zero_0 & (!y_if_non_zero_0.is_zero());
|
|
|
|
let mut z = -A;
|
|
z *= FieldElement::conditional_select(&v, &FieldElement::from(1u8), sign);
|
|
#[allow(non_snake_case)]
|
|
let Z = z + w;
|
|
#[allow(non_snake_case)]
|
|
let mut Y = z - w;
|
|
|
|
/*
|
|
If sign, `z = -486662`, else, `z = -486662 * v`
|
|
`w = v + 1`
|
|
|
|
We need `z + w \ne 0`, which would require `z \cong -w \mod 2^{255}-19`. This requires:
|
|
- If `sign`, `v \mod 2^{255}-19 \ne 486661`.
|
|
- If `!sign`, `(v + 1) \mod 2^{255}-19 \ne (v * 486662) \mod 2^{255}-19` which is equivalent to
|
|
`(v * 486661) \mod 2^{255}-19 \ne 1`.
|
|
|
|
In summary, if `sign`, `v` must not `486661`, and if `!sign`, `v` must not be the
|
|
multiplicative inverse of `486661`. Since `v` is the output of a hash function, this should
|
|
have negligible probability. Additionally, since the definition of `sign` is dependent on `v`,
|
|
it may be truly impossible to reach.
|
|
*/
|
|
Y *= Z.invert().expect("if sign, v was 486661. if !sign, v was 486661^{-1}");
|
|
let mut bytes = Y.to_repr();
|
|
bytes[31] |= sign.unwrap_u8() << 7;
|
|
|
|
decompress_point(bytes).expect("point from hash-to-curve wasn't on-curve").mul_by_cofactor()
|
|
}
|