-
Notifications
You must be signed in to change notification settings - Fork 252
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #793 from zcash/zip32_seed_signature
[#792] zcash_primitives: implement Seed Signature section of ZIP-32
- Loading branch information
Showing
2 changed files
with
81 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
//! Seed Fingerprints according to ZIP 32 | ||
//! | ||
//! Implements section `Seed Fingerprints` of Shielded Hierarchical Deterministic Wallets (ZIP 32) | ||
//! | ||
//! [Section Seed Fingerprints]: https://zips.z.cash/zip-0032#seed-fingerprints | ||
use blake2b_simd::Params as Blake2bParams; | ||
|
||
pub const ZIP32_SEED_FP_PERSONALIZATION: &[u8; 16] = b"Zcash_HD_Seed_FP"; | ||
pub struct SeedFingerprint([u8; 32]); | ||
|
||
impl SeedFingerprint { | ||
/// Return the seed fingerprint of the wallet as defined in | ||
/// <https://zips.z.cash/zip-0032#seed-fingerprints> or None | ||
/// if the length of `seed_bytes` is less than 32 or | ||
/// greater than 252. | ||
pub fn from_seed(seed_bytes: &[u8]) -> Option<SeedFingerprint> { | ||
let seed_len = seed_bytes.len(); | ||
|
||
if (32..=252).contains(&seed_len) { | ||
let seed_len: u8 = seed_len.try_into().unwrap(); | ||
Some(SeedFingerprint( | ||
Blake2bParams::new() | ||
.hash_length(32) | ||
.personal(ZIP32_SEED_FP_PERSONALIZATION) | ||
.to_state() | ||
.update(&[seed_len]) | ||
.update(seed_bytes) | ||
.finalize() | ||
.as_bytes() | ||
.try_into() | ||
.expect("hash length should be 32 bytes"), | ||
)) | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
/// Returns the fingerprint as a byte array. | ||
pub fn to_bytes(&self) -> [u8; 32] { | ||
self.0 | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_seed_fingerprint() { | ||
struct TestVector { | ||
root_seed: Vec<u8>, | ||
fingerprint: Vec<u8>, | ||
} | ||
|
||
let test_vectors = vec![TestVector { | ||
root_seed: vec![ | ||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, | ||
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, | ||
0x1c, 0x1d, 0x1e, 0x1f, | ||
], | ||
fingerprint: vec![ | ||
0xde, 0xff, 0x60, 0x4c, 0x24, 0x67, 0x10, 0xf7, 0x17, 0x6d, 0xea, 0xd0, 0x2a, 0xa7, | ||
0x46, 0xf2, 0xfd, 0x8d, 0x53, 0x89, 0xf7, 0x7, 0x25, 0x56, 0xdc, 0xb5, 0x55, 0xfd, | ||
0xbe, 0x5e, 0x3a, 0xe3, | ||
], | ||
}]; | ||
|
||
for tv in test_vectors { | ||
let fp = SeedFingerprint::from_seed(&tv.root_seed).expect("root_seed has valid length"); | ||
assert_eq!(&fp.to_bytes(), &tv.fingerprint[..]); | ||
} | ||
} | ||
#[test] | ||
fn test_seed_fingerprint_is_none() { | ||
let odd_seed = vec![ | ||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, | ||
0x0f, | ||
]; | ||
|
||
assert!( | ||
SeedFingerprint::from_seed(&odd_seed).is_none(), | ||
"fingerprint from short seed should be `None`" | ||
); | ||
} |