Skip to content

Commit

Permalink
initial polyseed support
Browse files Browse the repository at this point in the history
  • Loading branch information
detherminal committed Apr 5, 2024
1 parent eb446cb commit c83046d
Show file tree
Hide file tree
Showing 19 changed files with 2,186 additions and 33 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ jh = "0.1.0"
blake-hash = "0.4.1"
# Hashes needed for implementing the final step (end)
serde_json = "1.0.113"
reed-solomon = "0.2.1"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ If you have any questions, you can ask either at the [discussions](https://githu

As you may notice, we don't make any money from this project. We are working on this project for the Monero community and for the love of open-source. If you want to support us, you can donate to the following Monero address:
```
48SK3AeqELVKwsyKkSvxw9YkY8YU5eBHTZTQS2eWZb9yNtb7FYAJqxnhY41x5uGb2UJew8pBek4Cdg41CaA3CHpCCsGGBjF
42monume4tTXqMHPDAQ3wSgUo1oN3kT9hKytkyEGYKP8g3DAWoLk2hUJ2SiwNyvC93cq3wb7VgEe7CZ4NH4dzemWFD1xd5ye
```

## Licensing
Expand Down
118 changes: 100 additions & 18 deletions src/keys/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@
//! This module is for everything related to keys, such as generating seeds, deriving keys from seeds, deriving public keys from private keys, and deriving addresses from public keys etc.

use crate::crypt::ed25519::sc_reduce32;
use crate::wordsets::{WordsetOriginal, WORDSETSORIGINAL};
use crate::mnemonics::original::wordsets::{WordsetOriginal, WORDSETSORIGINAL};
use crate::mnemonics::polyseed::wordsets::WORDSETSPOLYSEED;
use crc32fast::Hasher;
use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, EdwardsPoint, Scalar};
use rand::Rng;
use sha3::{Digest, Keccak256};
use std::ops::Mul;
use std::time::{SystemTime, UNIX_EPOCH};
use std::vec;

/// Returns cryptographically secure random element of the given array
fn secure_random_element<'x>(array: &'x [&'x str]) -> &'x str {
Expand All @@ -27,6 +30,16 @@ fn secure_random_element<'x>(array: &'x [&'x str]) -> &'x str {
array[random_index]
}

// Returns cryptographically secure random bits of given length
fn get_random_bits(length: u64) -> Vec<bool> {
let mut rng = rand::thread_rng();
let mut bit_array = Vec::new();
for _ in 0..length {
bit_array.push(rng.gen_bool(0.5));
}
bit_array
}

/// Calculates CRC32 checksum index for given array (probably the seed)
fn get_checksum_index(array: &[&str], prefix_length: usize) -> usize {
let mut trimmed_words: String = String::new();
Expand Down Expand Up @@ -94,8 +107,77 @@ fn generate_mymonero_seed(language: &str) -> Vec<&str> {
seed
}

/// Generates a cryptographically secure 2048-type (16-word) seed for given language
fn generate_polyseed_seed(language: &str) -> Vec<&str> {
// Check if language is supported
if !WORDSETSPOLYSEED.iter().any(|x| x.name == language) {
panic!("Language not found");
}
// Get birthday
const POLYSEEDEPOCH: u64 = 1635768000; // The epoch for Polyseed birthdays. 1st November 2021 12:00 UTC
const TIMESTEP: u64 = 2629746; // The time step for Polyseed. 1/12 of the Gregorian year
let birthday: u16 = ((SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
- POLYSEEDEPOCH)
/ TIMESTEP)
.try_into()
.unwrap(); // The birthday of the seed from how much approximate months have passed since the epoch
let mut birthday_bits: Vec<bool> = birthday
.to_be_bytes()
.to_vec()
.iter()
.flat_map(|&byte| (0..8).rev().map(move |i| (byte >> i) & 1 == 1))
.collect();
birthday_bits.drain(..6);
let seed_bits = get_random_bits(150); // Get 150 random bits
let features_bits = vec![false, false, false, false, false]; // We don't use any feature while generating the seed
let mut words_bits: Vec<Vec<bool>> = Vec::with_capacity(16); // The seed of Polyseed is 16 words long
// Calulcate checksum bits
let checksum_bits = vec![false, false, false, false, false, false, false, false, false, false, false];
words_bits.push(checksum_bits);
// Add secret seed and features bits
for i in 0..5 {
let mut word: Vec<bool> = Vec::with_capacity(11);
let sss = i * 10;
let sse = (i + 1) * 10;
let ssi = seed_bits[sss..sse].to_vec();
for bit in ssi {
word.push(bit);
}
word.push(features_bits[i]);
words_bits.push(word);
}
// Add rest of the seed and birthday bits
for i in 5..15 {
let mut word: Vec<bool> = Vec::with_capacity(11);
let sss = i * 10;
let sse = (i + 1) * 10;
let ssi = seed_bits[sss..sse].to_vec();
for bit in ssi {
word.push(bit);
}
word.push(birthday_bits[i - 5]);
words_bits.push(word);
}
// Choose words based on each bits, corresponding to 0-2047
let mut seed: Vec<&str> = Vec::new();
for word_bits in words_bits {
let mut word_index: u16 = 0;
for (i, bit) in word_bits.iter().enumerate() {
if *bit {
word_index += 2u16.pow((10 - i) as u32);
}
}
seed.push(WORDSETSPOLYSEED[0].words[word_index as usize]);
}
// Finally, return the seed
seed
}

/// Generates a cryptographically secure mnemonic phrase for given language and seed type
///
///
/// Available seed types:
/// - `original` : (25-word)
/// - `en` (English)
Expand All @@ -110,11 +192,11 @@ fn generate_mymonero_seed(language: &str) -> Vec<&str> {
/// - `en`, `eo`, `fr`, `it`, `jp`, `lj`, `pt`, `ru` (same as original)
/// - `polyseed` : (TO BE IMPLEMENTED)
/// > DISCLAIMER: polyseed is not implemented yet
///
///
/// Example:
/// ```
/// use libmonero::keys::generate_seed;
///
///
/// let mnemonic: Vec<String> = generate_seed("en", "original");
/// // Not equal to the example below because the seed is generated randomly, but the seed is valid
/// assert_ne!(mnemonic, vec!["tissue", "raking", "haunted", "huts", "afraid", "volcano", "howls", "liar", "egotistic", "befit", "rounded", "older", "bluntly", "imbalance", "pivot", "exotic", "tuxedo", "amaze", "mostly", "lukewarm", "macro", "vocal", "hounded", "biplane", "rounded"].iter().map(|&s| s.to_string()).collect::<Vec<String>>());
Expand All @@ -123,7 +205,7 @@ pub fn generate_seed(language: &str, seed_type: &str) -> Vec<String> {
let seed = match seed_type {
"original" => generate_original_seed(language),
"mymonero" => generate_mymonero_seed(language),
"polyseed" => panic!("Polyseed not implemented yet"),
"polyseed" => generate_polyseed_seed(language),
_ => panic!("Invalid seed type"),
};
let mut seed_string: Vec<String> = Vec::new();
Expand All @@ -139,11 +221,11 @@ fn swap_endian_4_byte(s: &str) -> String {
}

/// Derives hexadecimal seed from the given mnemonic seed
///
///
/// Example:
/// ```
/// use libmonero::keys::derive_hex_seed;
///
///
/// let mnemonic: Vec<String> = vec!["tissue", "raking", "haunted", "huts", "afraid", "volcano", "howls", "liar", "egotistic", "befit", "rounded", "older", "bluntly", "imbalance", "pivot", "exotic", "tuxedo", "amaze", "mostly", "lukewarm", "macro", "vocal", "hounded", "biplane", "rounded"].iter().map(|s| s.to_string()).collect();
/// let hex_seed: String = derive_hex_seed(mnemonic);
/// assert_eq!(hex_seed, "f7b3beabc9bd6ced864096c0891a8fdf94dc714178a09828775dba01b4df9ab8".to_string());
Expand Down Expand Up @@ -304,13 +386,13 @@ fn derive_mymonero_priv_keys(hex_seed: String) -> Vec<String> {
}

/// Derives private keys from given hex seed
///
///
/// Vector's first element is private spend key, second element is private view key
///
///
/// Example:
/// ```
/// use libmonero::keys::derive_priv_keys;
///
///
/// let hex_seed: String = "f7b3beabc9bd6ced864096c0891a8fdf94dc714178a09828775dba01b4df9ab8".to_string();
/// let priv_keys: Vec<String> = derive_priv_keys(hex_seed);
/// assert_eq!(priv_keys, vec!["c8982eada77ba2245183f2bff85dfaf993dc714178a09828775dba01b4df9a08", "0d13a94c82d7a60abb54d2217d38935c3f715295e30378f8848a1ca1abc8d908"].iter().map(|&s| s.to_string()).collect::<Vec<String>>());
Expand All @@ -324,11 +406,11 @@ pub fn derive_priv_keys(hex_seed: String) -> Vec<String> {
}

/// Derives private view key from given private spend key
///
///
/// Example:
/// ```
/// use libmonero::keys::derive_priv_vk_from_priv_sk;
///
///
/// let private_spend_key: String = "c8982eada77ba2245183f2bff85dfaf993dc714178a09828775dba01b4df9a08".to_string();
/// let private_view_key: String = derive_priv_vk_from_priv_sk(private_spend_key);
/// assert_eq!(private_view_key, "0d13a94c82d7a60abb54d2217d38935c3f715295e30378f8848a1ca1abc8d908".to_string());
Expand Down Expand Up @@ -359,11 +441,11 @@ fn ge_scalar_mult_base(scalar: &Scalar) -> EdwardsPoint {
}

/// Derives public key from given private key (spend or view)
///
///
/// Example:
/// ```
/// use libmonero::keys::derive_pub_key;
///
///
/// let private_spend_key: String = "c8982eada77ba2245183f2bff85dfaf993dc714178a09828775dba01b4df9a08".to_string();
/// let public_spend_key: String = derive_pub_key(private_spend_key);
/// assert_eq!(public_spend_key, "e78d891dd2be407f24e6470caad956e1b746ae0b41cd8252f96684090bc05d95".to_string());
Expand Down Expand Up @@ -391,15 +473,15 @@ pub fn derive_pub_key(private_key: String) -> String {
}

/// Derives main public address from given public spend key, public view key and network
///
///
/// Networks:
/// - `0` : Monero Mainnet
/// - `1` : Monero Testnet
///
///
/// Example:
/// ```
/// use libmonero::keys::derive_address;
///
///
/// let public_spend_key: String = "e78d891dd2be407f24e6470caad956e1b746ae0b41cd8252f96684090bc05d95".to_string();
/// let public_view_key: String = "157d278aa3aee4e11c5a8243a43a78527a2691009562b8c18654975f1347cb47".to_string();
/// let public_address: String = derive_address(public_spend_key, public_view_key, 0);
Expand All @@ -418,4 +500,4 @@ pub fn derive_address(public_spend_key: String, public_view_key: String, network
data.append(&mut hash[..4].to_vec());

base58_monero::encode(&data).unwrap()
}
}
7 changes: 6 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
//! - Utils
//! - [`is_valid_addr(address: &str) -> bool`](utils/fn.is_valid_addr.html)

pub(crate) use mnemonics::original::wordsets;

pub(crate) mod mnemonics {
pub mod original {
Expand All @@ -63,6 +62,12 @@ pub(crate) mod mnemonics {
pub mod spanish;
}
}
pub mod polyseed {
pub mod wordsets;
pub mod languages {
pub mod english;
}
}
}

/// Cryptographic functions
Expand Down
2 changes: 1 addition & 1 deletion src/mnemonics/original/languages/chinese_simplified.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

#![allow(warnings)]
use crate::wordsets::WordsetOriginal;
use crate::mnemonics::original::wordsets::WordsetOriginal;

// https://github.com/monero-project/monero/blob/master/src/mnemonics/chinese_simplified.h
pub(crate) const CHINESESIMPLIFIEDORIGINAL: WordsetOriginal = WordsetOriginal {
Expand Down
2 changes: 1 addition & 1 deletion src/mnemonics/original/languages/dutch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

#![allow(warnings)]
use crate::wordsets::WordsetOriginal;
use crate::mnemonics::original::wordsets::WordsetOriginal;

// https://github.com/monero-project/monero/blob/master/src/mnemonics/dutch.h
pub(crate) const DUTCHORIGINAL: WordsetOriginal = WordsetOriginal {
Expand Down
2 changes: 1 addition & 1 deletion src/mnemonics/original/languages/english.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*
*/

use crate::wordsets::WordsetOriginal;
use crate::mnemonics::original::wordsets::WordsetOriginal;

// https://github.com/monero-project/monero/blob/master/src/mnemonics/english.h
pub(crate) const ENGLISHORIGINAL: WordsetOriginal = WordsetOriginal {
Expand Down
2 changes: 1 addition & 1 deletion src/mnemonics/original/languages/esperanto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

#![allow(warnings)]
use crate::wordsets::WordsetOriginal;
use crate::mnemonics::original::wordsets::WordsetOriginal;

// https://github.com/monero-project/monero/blob/master/src/mnemonics/esperanto.h
pub(crate) const ESPERANTOORIGINAL: WordsetOriginal = WordsetOriginal {
Expand Down
2 changes: 1 addition & 1 deletion src/mnemonics/original/languages/french.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

#![allow(warnings)]
use crate::wordsets::WordsetOriginal;
use crate::mnemonics::original::wordsets::WordsetOriginal;

// https://github.com/monero-project/monero/blob/master/src/mnemonics/french.h
pub(crate) const FRENCHORIGINAL: WordsetOriginal = WordsetOriginal {
Expand Down
2 changes: 1 addition & 1 deletion src/mnemonics/original/languages/german.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

#![allow(warnings)]
use crate::wordsets::WordsetOriginal;
use crate::mnemonics::original::wordsets::WordsetOriginal;

// https://github.com/monero-project/monero/blob/master/src/mnemonics/german.h
pub(crate) const GERMANORIGINAL: WordsetOriginal = WordsetOriginal {
Expand Down
2 changes: 1 addition & 1 deletion src/mnemonics/original/languages/italian.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

#![allow(warnings)]
use crate::wordsets::WordsetOriginal;
use crate::mnemonics::original::wordsets::WordsetOriginal;

// https://github.com/monero-project/monero/blob/master/src/mnemonics/italian.h
pub(crate) const ITALIANORIGINAL: WordsetOriginal = WordsetOriginal {
Expand Down
2 changes: 1 addition & 1 deletion src/mnemonics/original/languages/japanese.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

#![allow(warnings)]
use crate::wordsets::WordsetOriginal;
use crate::mnemonics::original::wordsets::WordsetOriginal;

// https://github.com/monero-project/monero/blob/master/src/mnemonics/japanese.h
pub(crate) const JAPANESEORIGINAL: WordsetOriginal = WordsetOriginal {
Expand Down
2 changes: 1 addition & 1 deletion src/mnemonics/original/languages/lojban.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

#![allow(warnings)]
use crate::wordsets::WordsetOriginal;
use crate::mnemonics::original::wordsets::WordsetOriginal;

// https://github.com/monero-project/monero/blob/master/src/mnemonics/lojban.h
// This language does not have ISO639 code, we will use the empty "lj" code
Expand Down
2 changes: 1 addition & 1 deletion src/mnemonics/original/languages/portuguese.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

#![allow(warnings)]
use crate::wordsets::WordsetOriginal;
use crate::mnemonics::original::wordsets::WordsetOriginal;

// https://github.com/monero-project/monero/blob/master/src/mnemonics/portuguese.h
pub(crate) const PORTUGUESEORIGINAL: WordsetOriginal = WordsetOriginal {
Expand Down
2 changes: 1 addition & 1 deletion src/mnemonics/original/languages/russian.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

#![allow(warnings)]
use crate::wordsets::WordsetOriginal;
use crate::mnemonics::original::wordsets::WordsetOriginal;

// https://github.com/monero-project/monero/blob/master/src/mnemonics/russian.h
pub(crate) const RUSSIANORIGINAL: WordsetOriginal = WordsetOriginal {
Expand Down
2 changes: 1 addition & 1 deletion src/mnemonics/original/languages/spanish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

#![allow(warnings)]
use crate::wordsets::WordsetOriginal;
use crate::mnemonics::original::wordsets::WordsetOriginal;

// https://github.com/monero-project/monero/blob/master/src/mnemonics/spanish.h
pub(crate) const SPANISHORIGINAL: WordsetOriginal = WordsetOriginal {
Expand Down
2 changes: 1 addition & 1 deletion src/mnemonics/original/wordsets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#![allow(warnings)]
use crate::mnemonics::original::languages::english::ENGLISHORIGINAL;

use super::languages::{dutch::DUTCHORIGINAL, chinese_simplified::CHINESESIMPLIFIEDORIGINAL, esperanto::ESPERANTOORIGINAL, french::FRENCHORIGINAL, german::GERMANORIGINAL, italian::ITALIANORIGINAL, japanese::JAPANESEORIGINAL, lojban::LOJBANORIGINAL, portuguese::PORTUGUESEORIGINAL, russian::RUSSIANORIGINAL, spanish::SPANISHORIGINAL};
use super::languages::{chinese_simplified::CHINESESIMPLIFIEDORIGINAL, dutch::DUTCHORIGINAL, esperanto::ESPERANTOORIGINAL, french::FRENCHORIGINAL, german::GERMANORIGINAL, italian::ITALIANORIGINAL, japanese::JAPANESEORIGINAL, lojban::LOJBANORIGINAL, portuguese::PORTUGUESEORIGINAL, russian::RUSSIANORIGINAL, spanish::SPANISHORIGINAL};

// WordsetOriginal is a struct that contains the name of the wordset, the prefix length and the words
// Name is the ISO639 language code (https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes)
Expand Down
Loading

0 comments on commit c83046d

Please sign in to comment.