@@ -34,7 +34,7 @@ use dock_crypto_utils::hashing_utils::hash_to_field;
3434use dock_crypto_utils:: msm:: multiply_field_elems_with_same_group_elem;
3535use dock_crypto_utils:: randomized_mult_checker:: RandomizedMultChecker ;
3636use dock_crypto_utils:: transcript:: { MerlinTranscript , Transcript } ;
37- use polymesh_dart_common:: { AssetId , NullifierSkGenCounter } ;
37+ use polymesh_dart_common:: { AssetId , NullifierSkGenCounter , SkGenCounter } ;
3838use rand_core:: CryptoRngCore ;
3939use schnorr_pok:: discrete_log:: {
4040 PokDiscreteLog , PokDiscreteLogProtocol , PokPedersenCommitment , PokPedersenCommitmentProtocol ,
@@ -43,7 +43,7 @@ use schnorr_pok::partial::{
4343 Partial1PokPedersenCommitment , PartialPokPedersenCommitment , PartialSchnorrResponse ,
4444} ;
4545use schnorr_pok:: { SchnorrChallengeContributor , SchnorrCommitment , SchnorrResponse } ;
46- use zeroize:: Zeroize ;
46+ use zeroize:: { Zeroize , ZeroizeOnDrop } ;
4747
4848pub const PK_T_LABEL : & ' static [ u8 ; 4 ] = b"pk_t" ;
4949pub const PK_T_GEN_LABEL : & ' static [ u8 ; 8 ] = b"pk_t_gen" ;
@@ -1470,42 +1470,63 @@ pub fn powers_of_base<F: PrimeField, const BASE_BITS: usize, const NUM_DIGITS: u
14701470 powers
14711471}
14721472
1473- /// Generate keypairs and fresh account state for investor from a seed
1474- pub fn setup_investor < G : AffineRepr , D : FullDigest > (
1475- seed : & [ u8 ] ,
1476- id : G :: ScalarField , // User can hash its string ID onto the field
1477- asset_id : AssetId ,
1478- counter : NullifierSkGenCounter ,
1479- j : G ,
1480- g : G ,
1481- poseidon_config : Poseidon2Params < G :: ScalarField > ,
1482- ) -> Result < (
1483- ( SigKey < G > , VerKey < G > ) ,
1484- ( DecKey < G > , EncKey < G > ) ,
1485- AccountState < G > ,
1486- ) > {
1487- // For creating secret keys, use seed||asset_id
1488- let mut extended_seed = seed. to_vec ( ) ;
1489- extended_seed. extend ( asset_id. to_le_bytes ( ) . as_slice ( ) ) ;
1490-
1491- let sig_sk = hash_to_field :: < G :: ScalarField , D > ( b"Signing key" , & extended_seed) ;
1492- let enc_sk = hash_to_field :: < G :: ScalarField , D > ( b"Encryption key" , & extended_seed) ;
1493-
1494- // For creating commitment randomness, use seed||asset_id||counter
1495- extended_seed. extend_from_slice ( counter. to_le_bytes ( ) . as_slice ( ) ) ;
1496- let randomness = hash_to_field :: < G :: ScalarField , D > ( b"Commitment randomness" , & extended_seed) ;
1497-
1498- let ( sk, vk) = keygen_sig_given_sk ( sig_sk, j) ;
1499- let ( dk, ek) = keygen_enc_given_sk ( enc_sk, g) ;
1500- let account = AccountState :: new_given_randomness (
1501- id,
1502- sk. 0 ,
1503- asset_id,
1504- counter,
1505- randomness,
1506- poseidon_config,
1507- ) ?;
1508- Ok ( ( ( sk, vk) , ( dk, ek) , account) )
1473+ const SEPARATOR : & [ u8 ; 2 ] = b"//" ;
1474+
1475+ #[ derive( Clone , Debug , CanonicalSerialize , CanonicalDeserialize , Zeroize , ZeroizeOnDrop ) ]
1476+ pub struct MasterSeed ( Vec < u8 > ) ;
1477+
1478+ impl MasterSeed {
1479+ pub fn new ( seed : Vec < u8 > ) -> Self {
1480+ Self ( seed)
1481+ }
1482+
1483+ /// Doesn't include asset-id to allow accounts to share secret keys
1484+ pub fn derive_keys < G : AffineRepr , D : FullDigest > (
1485+ & self ,
1486+ path : & [ u8 ] ,
1487+ counter : SkGenCounter ,
1488+ j : G ,
1489+ g : G ,
1490+ ) -> ( ( SigKey < G > , VerKey < G > ) , ( DecKey < G > , EncKey < G > ) ) {
1491+ // For creating secret keys, use `seed//path//counter`
1492+ let mut extended_seed = self . 0 . to_vec ( ) ;
1493+ extended_seed. extend ( SEPARATOR ) ;
1494+ extended_seed. extend_from_slice ( path) ;
1495+ extended_seed. extend ( SEPARATOR ) ;
1496+ extended_seed. extend ( counter. to_le_bytes ( ) ) ;
1497+
1498+ let sig_sk = hash_to_field :: < G :: ScalarField , D > ( b"Signing key" , & extended_seed) ;
1499+ let enc_sk = hash_to_field :: < G :: ScalarField , D > ( b"Encryption key" , & extended_seed) ;
1500+
1501+ let ( sk, vk) = keygen_sig_given_sk ( sig_sk, j) ;
1502+ let ( dk, ek) = keygen_enc_given_sk ( enc_sk, g) ;
1503+
1504+ extended_seed. zeroize ( ) ;
1505+ ( ( sk, vk) , ( dk, ek) )
1506+ }
1507+
1508+ pub fn derive_account_randomness < G : AffineRepr , D : FullDigest > (
1509+ & self ,
1510+ path : & [ u8 ] ,
1511+ counter_s : SkGenCounter ,
1512+ asset_id : AssetId ,
1513+ counter : NullifierSkGenCounter ,
1514+ ) -> G :: ScalarField {
1515+ // For creating randomness, use `seed//path//counter_s//asset_id//counter_n`
1516+ let mut extended_seed = self . 0 . to_vec ( ) ;
1517+ extended_seed. extend ( SEPARATOR ) ;
1518+ extended_seed. extend_from_slice ( path) ;
1519+ extended_seed. extend ( SEPARATOR ) ;
1520+ extended_seed. extend ( counter_s. to_le_bytes ( ) ) ;
1521+ extended_seed. extend ( SEPARATOR ) ;
1522+ extended_seed. extend ( asset_id. to_le_bytes ( ) . as_slice ( ) ) ;
1523+ extended_seed. extend ( SEPARATOR ) ;
1524+ extended_seed. extend ( counter. to_le_bytes ( ) ) ;
1525+ let randomness =
1526+ hash_to_field :: < G :: ScalarField , D > ( b"Commitment randomness" , & extended_seed) ;
1527+ extended_seed. zeroize ( ) ;
1528+ randomness
1529+ }
15091530}
15101531
15111532#[ cfg( test) ]
@@ -1684,82 +1705,115 @@ pub mod tests {
16841705 }
16851706
16861707 #[ test]
1687- fn test_setup_investor ( ) {
1708+ fn test_deterministic_key_and_randomness_generation ( ) {
16881709 let mut rng = rand:: thread_rng ( ) ;
16891710
16901711 let id = Fr :: rand ( & mut rng) ;
16911712 let asset_id = 1 ;
1692- let counter = 1 ;
1713+ let counter_s = 1 ;
1714+ let counter_n = 1 ;
16931715
1694- let seed1 = b"test_seed" ;
1716+ let seed1 = MasterSeed :: new ( b"test_seed" . to_vec ( ) ) ;
1717+
1718+ let seed2 = MasterSeed :: new ( b"different_seed" . to_vec ( ) ) ;
16951719
16961720 let j = PallasA :: rand ( & mut rng) ;
16971721 let g = PallasA :: rand ( & mut rng) ;
1722+
16981723 let params = test_params_for_poseidon2 ( ) ;
16991724
1700- let result1 = setup_investor :: < PallasA , Blake2b512 > (
1701- seed1,
1702- id,
1725+ let result1 = seed1. derive_keys :: < PallasA , Blake2b512 > ( b"path" , counter_s, j, g) ;
1726+ let result2 = seed1. derive_keys :: < PallasA , Blake2b512 > ( b"path" , counter_s, j, g) ;
1727+ assert_eq ! ( result1, result2) ;
1728+
1729+ // Same seed, different path
1730+ let result3 = seed1. derive_keys :: < PallasA , Blake2b512 > ( b"path1" , counter_s, j, g) ;
1731+ assert_ne ! ( result1, result3) ;
1732+
1733+ // Same seed, same path, different counter
1734+ let result4 = seed1. derive_keys :: < PallasA , Blake2b512 > ( b"path" , counter_s + 1 , j, g) ;
1735+ assert_ne ! ( result1, result4) ;
1736+
1737+ // Different seed, same path and counter
1738+ let result5 = seed2. derive_keys :: < PallasA , Blake2b512 > ( b"path" , counter_s, j, g) ;
1739+ assert_ne ! ( result1, result5) ;
1740+
1741+ let rand1 = seed1. derive_account_randomness :: < PallasA , Blake2b512 > (
1742+ b"path" , counter_s, asset_id, counter_n,
1743+ ) ;
1744+ let rand2 = seed1. derive_account_randomness :: < PallasA , Blake2b512 > (
1745+ b"path" , counter_s, asset_id, counter_n,
1746+ ) ;
1747+ assert_eq ! ( rand1, rand2) ;
1748+
1749+ // Different counter
1750+ let rand3 = seed2. derive_account_randomness :: < PallasA , Blake2b512 > (
1751+ b"path" ,
1752+ counter_s,
17031753 asset_id,
1704- counter,
1705- j,
1706- g,
1707- params. clone ( ) ,
1708- )
1709- . unwrap ( ) ;
1710- let result2 = setup_investor :: < PallasA , Blake2b512 > (
1711- seed1,
1754+ counter_n + 1 ,
1755+ ) ;
1756+ assert_ne ! ( rand1, rand3) ;
1757+
1758+ let rand4 = seed2. derive_account_randomness :: < PallasA , Blake2b512 > (
1759+ b"path" ,
1760+ counter_s + 1 ,
1761+ asset_id,
1762+ counter_n,
1763+ ) ;
1764+ assert_ne ! ( rand1, rand4) ;
1765+
1766+ let rand5 = seed1. derive_account_randomness :: < PallasA , Blake2b512 > (
1767+ b"path1" , counter_s, asset_id, counter_n,
1768+ ) ;
1769+ assert_ne ! ( rand1, rand5) ;
1770+
1771+ let sk1 = result1. 0 . 0 . 0 ;
1772+ let sk2 = result3. 0 . 0 . 0 ;
1773+
1774+ let account1 = AccountState :: < PallasA > :: new_given_randomness (
17121775 id,
1776+ sk1,
17131777 asset_id,
1714- counter,
1715- j,
1716- g,
1778+ counter_n,
1779+ rand1,
17171780 params. clone ( ) ,
17181781 )
17191782 . unwrap ( ) ;
1720- assert_eq ! ( result1, result2) ;
1721-
1722- let seed2 = b"different_seed" ;
1723- let result3 = setup_investor :: < PallasA , Blake2b512 > (
1724- seed2,
1783+ let account2 = AccountState :: < PallasA > :: new_given_randomness (
17251784 id,
1785+ sk1,
17261786 asset_id,
1727- counter,
1728- j,
1729- g,
1787+ counter_n,
1788+ rand1,
17301789 params. clone ( ) ,
17311790 )
17321791 . unwrap ( ) ;
1733- assert_ne ! ( result1 , result3 ) ;
1792+ assert_eq ! ( account1 , account2 ) ;
17341793
1735- // seed same but asset id different
1736- let result4 = setup_investor :: < PallasA , Blake2b512 > (
1737- seed1,
1794+ // Different randomness
1795+ let account3 = AccountState :: < PallasA > :: new_given_randomness (
17381796 id,
1739- asset_id + 1 ,
1740- counter ,
1741- j ,
1742- g ,
1797+ sk1 ,
1798+ asset_id ,
1799+ counter_n ,
1800+ rand3 ,
17431801 params. clone ( ) ,
17441802 )
17451803 . unwrap ( ) ;
1746- assert_ne ! ( result1 , result4 ) ;
1804+ assert_ne ! ( account1 , account3 ) ;
17471805
1748- // seed and asset id same but counter different
1749- let result5 = setup_investor :: < PallasA , Blake2b512 > (
1750- seed1,
1806+ // Same randomness, different sk
1807+ let account4 = AccountState :: < PallasA > :: new_given_randomness (
17511808 id,
1809+ sk2,
17521810 asset_id,
1753- counter + 1 ,
1754- j,
1755- g,
1811+ counter_n,
1812+ rand1,
17561813 params. clone ( ) ,
17571814 )
17581815 . unwrap ( ) ;
1759- // Keys same but randomness different
1760- assert_eq ! ( result1. 0 , result5. 0 ) ;
1761- assert_eq ! ( result1. 1 , result5. 1 ) ;
1762- assert_ne ! ( result1. 2 . randomness, result5. 2 . randomness) ;
1816+ assert_ne ! ( account1, account4) ;
17631817 }
17641818
17651819 #[ test]
0 commit comments