Skip to content
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Added `map_to_point` and `unmap_from_point` methods [#149]

## [0.15.0] - 2025-02-05

### Changed
Expand Down Expand Up @@ -236,6 +240,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Initial fork from [`zkcrypto/jubjub`]

<!-- ISSUES -->
[#149]: https://github.com/dusk-network/jubjub/issues/149
[#143]: https://github.com/dusk-network/jubjub/issues/143
[#137]: https://github.com/dusk-network/jubjub/issues/137
[#135]: https://github.com/dusk-network/jubjub/issues/135
Expand Down
1 change: 1 addition & 0 deletions benches/fq_bench.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use criterion::{criterion_group, criterion_main, Criterion};
use dusk_jubjub::*;
use ff::Field;

fn bench_add_assign(c: &mut Criterion) {
let mut n = Fq::one();
Expand Down
79 changes: 79 additions & 0 deletions src/dusk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,69 @@ impl JubJubExtended {
}
}

/// Method that maps a [`u64`] input value to a [`JubJubExtended`] point
/// of the curve. This is a bidirectional operation, meaning
/// that the operation can be reverted by using the 'unmap_from_point()'
/// function.
///
/// This is a probabilistic method, meaning that trying to find a correct
/// map can require several tries, until finding a real point on the curve,
/// that lies also on the correct subgroup.
pub fn map_to_point(input: &u64) -> Self {
let input = input.to_le_bytes();

// we take the generator's y coodinate as our starting point
let mut y_coordinate = GENERATOR.get_v();

// this will be the bytes representation of our target point
let mut point_bytes = y_coordinate.to_bytes();

// we craft a point = (y_coordinate[31..8] || u64_input_value)
point_bytes[..u64::SIZE].copy_from_slice(&input);
y_coordinate = BlsScalar::from_bytes(&point_bytes).unwrap();

// the value we'll add on each iteration:
// 0x0000000000000000000000000000000000000000000000010000000000000000
let adder = BlsScalar::from(u64::MAX) + BlsScalar::one();

// we set a maximum number of iterations to avoid an
// 'in-practice' infinte loop
for _ in 0..u64::MAX {
// check if we hit a point on the curve
if let Ok(point) =
<JubJubAffine as Serializable<32>>::from_bytes(&point_bytes)
{
// check if this point is part of the correct subgroup and not
// the identity
if point.is_prime_order().into() {
return point.into();
}
}

// if the previous step doesn't succeed, we keep trying by
// checking all possible combinations for all the 24 bytes
// not belonging to the input value side of the vector
//
// Notice that we we just try out the upper bound of the curve,
// we don't care about negative points since anyways
// we have set a maximum number of tries
y_coordinate += adder;
point_bytes = y_coordinate.to_bytes();
}

panic!("No point is likely to be found soon enough.");
}

/// Method that unmaps a [`JubJubExtended`] point of the curve (created
/// using the 'map_to_point()' function) to its original [`u64`] value.
pub fn unmap_from_point(self) -> u64 {
let point_bytes: [u8; u64::SIZE] = JubJubAffine::from(self).to_bytes()
[..u64::SIZE]
.try_into()
.unwrap();
u64::from_le_bytes(point_bytes)
}

/// Returns true if this point is on the curve. This should always return
/// true unless an "unchecked" API was used.
pub fn is_on_curve(&self) -> Choice {
Expand All @@ -289,6 +352,22 @@ impl JubJubExtended {
}
}

#[test]
fn test_map_to_point() {
use rand::Rng;

let mut rng = rand::thread_rng();

// Test several random values
for _ in 0..500 {
let value: u64 = rng.gen();
let point = JubJubExtended::map_to_point(&value);
let unmapped_value = point.unmap_from_point();

assert_eq!(value, unmapped_value);
}
}

#[test]
fn test_affine_point_generator_has_order_p() {
assert_eq!(GENERATOR.is_prime_order().unwrap_u8(), 1);
Expand Down