Update to latest interop keypair spec
This commit is contained in:
parent
2e11faf763
commit
25f2e212c3
@ -1,5 +1,5 @@
|
|||||||
use crate::*;
|
use crate::*;
|
||||||
use eth2_interop_keypairs::be_private_key;
|
use eth2_interop_keypairs::keypair;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
|
|
||||||
@ -15,8 +15,8 @@ pub fn generate_deterministic_keypairs(validator_count: usize) -> Vec<Keypair> {
|
|||||||
|
|
||||||
let keypairs: Vec<Keypair> = (0..validator_count)
|
let keypairs: Vec<Keypair> = (0..validator_count)
|
||||||
.collect::<Vec<usize>>()
|
.collect::<Vec<usize>>()
|
||||||
.par_iter()
|
.into_par_iter()
|
||||||
.map(|&i| generate_deterministic_keypair(i))
|
.map(generate_deterministic_keypair)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
keypairs
|
keypairs
|
||||||
@ -26,8 +26,9 @@ pub fn generate_deterministic_keypairs(validator_count: usize) -> Vec<Keypair> {
|
|||||||
///
|
///
|
||||||
/// This is used for testing only, and not to be used in production!
|
/// This is used for testing only, and not to be used in production!
|
||||||
pub fn generate_deterministic_keypair(validator_index: usize) -> Keypair {
|
pub fn generate_deterministic_keypair(validator_index: usize) -> Keypair {
|
||||||
let sk = SecretKey::from_bytes(&be_private_key(validator_index))
|
let raw = keypair(validator_index);
|
||||||
.expect("be_private_key always returns valid keys");
|
Keypair {
|
||||||
let pk = PublicKey::from_secret_key(&sk);
|
pk: PublicKey::from_raw(raw.pk),
|
||||||
Keypair { sk, pk }
|
sk: SecretKey::from_raw(raw.sk),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,10 @@ impl PublicKey {
|
|||||||
PublicKey(RawPublicKey::from_secret_key(secret_key.as_raw()))
|
PublicKey(RawPublicKey::from_secret_key(secret_key.as_raw()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_raw(raw: RawPublicKey) -> Self {
|
||||||
|
Self(raw)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the underlying signature.
|
/// Returns the underlying signature.
|
||||||
pub fn as_raw(&self) -> &RawPublicKey {
|
pub fn as_raw(&self) -> &RawPublicKey {
|
||||||
&self.0
|
&self.0
|
||||||
|
@ -20,6 +20,10 @@ impl SecretKey {
|
|||||||
SecretKey(RawSecretKey::random(&mut rand::thread_rng()))
|
SecretKey(RawSecretKey::random(&mut rand::thread_rng()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_raw(raw: RawSecretKey) -> Self {
|
||||||
|
Self(raw)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the underlying point as compressed bytes.
|
/// Returns the underlying point as compressed bytes.
|
||||||
fn as_bytes(&self) -> Vec<u8> {
|
fn as_bytes(&self) -> Vec<u8> {
|
||||||
self.as_raw().as_bytes()
|
self.as_raw().as_bytes()
|
||||||
|
@ -7,5 +7,13 @@ edition = "2018"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
lazy_static = "1.4"
|
||||||
num-bigint = "0.2"
|
num-bigint = "0.2"
|
||||||
eth2_hashing = "0.1"
|
eth2_hashing = "0.1"
|
||||||
|
milagro_bls = { git = "https://github.com/sigp/milagro_bls", tag = "v0.10.0" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
base64 = "0.10"
|
||||||
|
serde = "1.0"
|
||||||
|
serde_derive = "1.0"
|
||||||
|
serde_yaml = "0.8"
|
||||||
|
@ -5,126 +5,54 @@
|
|||||||
//! keys generated here are **not secret** and are **not for production use**.
|
//! keys generated here are **not secret** and are **not for production use**.
|
||||||
//!
|
//!
|
||||||
//! Note: these keys have not been tested against a reference implementation, yet.
|
//! Note: these keys have not been tested against a reference implementation, yet.
|
||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
|
|
||||||
use eth2_hashing::hash;
|
use eth2_hashing::hash;
|
||||||
|
use milagro_bls::{Keypair, PublicKey, SecretKey};
|
||||||
use num_bigint::BigUint;
|
use num_bigint::BigUint;
|
||||||
|
|
||||||
pub const CURVE_ORDER_BITS: usize = 255;
|
pub const CURVE_ORDER_BITS: usize = 255;
|
||||||
pub const PRIVATE_KEY_BYTES: usize = 48;
|
pub const PRIVATE_KEY_BYTES: usize = 48;
|
||||||
pub const HASH_BYTES: usize = 32;
|
pub const HASH_BYTES: usize = 32;
|
||||||
|
|
||||||
fn hash_big_int_le(uint: BigUint) -> BigUint {
|
lazy_static! {
|
||||||
let mut preimage = uint.to_bytes_le();
|
static ref CURVE_ORDER: BigUint =
|
||||||
preimage.resize(32, 0_u8);
|
"52435875175126190479447740508185965837690552500527637822603658699938581184513"
|
||||||
BigUint::from_bytes_le(&hash(&preimage))
|
.parse::<BigUint>()
|
||||||
|
.expect("Curve order should be valid");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn private_key(validator_index: usize) -> BigUint {
|
|
||||||
let mut key = BigUint::from(validator_index);
|
|
||||||
loop {
|
|
||||||
key = hash_big_int_le(key);
|
|
||||||
if key.bits() <= CURVE_ORDER_BITS {
|
|
||||||
break key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates an **unsafe** BLS12-381 private key for the given validator index, where that private
|
|
||||||
/// key is represented in big-endian bytes.
|
|
||||||
pub fn be_private_key(validator_index: usize) -> [u8; PRIVATE_KEY_BYTES] {
|
|
||||||
let vec = private_key(validator_index).to_bytes_be();
|
|
||||||
|
|
||||||
let mut out = [0; PRIVATE_KEY_BYTES];
|
|
||||||
out[PRIVATE_KEY_BYTES - vec.len()..PRIVATE_KEY_BYTES].copy_from_slice(&vec);
|
|
||||||
out
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates an **unsafe** BLS12-381 private key for the given validator index, where that private
|
|
||||||
/// key is represented in little-endian bytes.
|
|
||||||
pub fn le_private_key(validator_index: usize) -> [u8; PRIVATE_KEY_BYTES] {
|
pub fn le_private_key(validator_index: usize) -> [u8; PRIVATE_KEY_BYTES] {
|
||||||
let vec = private_key(validator_index).to_bytes_le();
|
let preimage = {
|
||||||
|
let mut bytes = [0; HASH_BYTES];
|
||||||
|
let index = validator_index.to_le_bytes();
|
||||||
|
bytes[0..index.len()].copy_from_slice(&index);
|
||||||
|
bytes
|
||||||
|
};
|
||||||
|
|
||||||
let mut out = [0; PRIVATE_KEY_BYTES];
|
let privkey = BigUint::from_bytes_le(&hash(&preimage)) % &*CURVE_ORDER;
|
||||||
out[0..vec.len()].copy_from_slice(&vec);
|
|
||||||
out
|
let mut bytes = [0; PRIVATE_KEY_BYTES];
|
||||||
|
let privkey_bytes = privkey.to_bytes_le();
|
||||||
|
bytes[0..privkey_bytes.len()].copy_from_slice(&privkey_bytes);
|
||||||
|
bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
pub fn keypair(validator_index: usize) -> Keypair {
|
||||||
mod tests {
|
let bytes = le_private_key(validator_index);
|
||||||
use super::*;
|
|
||||||
|
|
||||||
fn flip(vec: &[u8]) -> Vec<u8> {
|
let sk =
|
||||||
let len = vec.len();
|
SecretKey::from_bytes(&swap_bytes(bytes.to_vec())).expect("Should be valid private key");
|
||||||
let mut out = vec![0; len];
|
|
||||||
for i in 0..len {
|
|
||||||
out[len - 1 - i] = vec[i];
|
|
||||||
}
|
|
||||||
out
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pad_le_bls(mut vec: Vec<u8>) -> Vec<u8> {
|
Keypair {
|
||||||
vec.resize(PRIVATE_KEY_BYTES, 0_u8);
|
pk: PublicKey::from_secret_key(&sk),
|
||||||
vec
|
sk,
|
||||||
}
|
|
||||||
|
|
||||||
fn pad_be_bls(mut vec: Vec<u8>) -> Vec<u8> {
|
|
||||||
let mut out = vec![0; PRIVATE_KEY_BYTES - vec.len()];
|
|
||||||
out.append(&mut vec);
|
|
||||||
out
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pad_le_hash(index: usize) -> Vec<u8> {
|
|
||||||
let mut vec = index.to_le_bytes().to_vec();
|
|
||||||
vec.resize(HASH_BYTES, 0_u8);
|
|
||||||
vec
|
|
||||||
}
|
|
||||||
|
|
||||||
fn multihash(index: usize, rounds: usize) -> Vec<u8> {
|
|
||||||
let mut vec = pad_le_hash(index);
|
|
||||||
for _ in 0..rounds {
|
|
||||||
vec = hash(&vec);
|
|
||||||
}
|
|
||||||
vec
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compare(validator_index: usize, preimage: &[u8]) {
|
|
||||||
assert_eq!(
|
|
||||||
&le_private_key(validator_index)[..],
|
|
||||||
&pad_le_bls(hash(preimage))[..]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
&be_private_key(validator_index)[..],
|
|
||||||
&pad_be_bls(flip(&hash(preimage)))[..]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn consistency() {
|
|
||||||
for i in 0..256 {
|
|
||||||
let le = BigUint::from_bytes_le(&le_private_key(i));
|
|
||||||
let be = BigUint::from_bytes_be(&be_private_key(i));
|
|
||||||
assert_eq!(le, be);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn non_repeats() {
|
|
||||||
// These indices only need one hash to be in the curve order.
|
|
||||||
compare(0, &pad_le_hash(0));
|
|
||||||
compare(3, &pad_le_hash(3));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn repeats() {
|
|
||||||
// Index 5 needs 5x hashes to get into the curve order.
|
|
||||||
compare(5, &multihash(5, 5));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn doesnt_panic() {
|
|
||||||
for i in 0..256 {
|
|
||||||
be_private_key(i);
|
|
||||||
le_private_key(i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn swap_bytes<T>(input: Vec<T>) -> Vec<T> {
|
||||||
|
let mut output = vec![];
|
||||||
|
input.into_iter().rev().for_each(|byte| output.push(byte));
|
||||||
|
output
|
||||||
|
}
|
||||||
|
64
eth2/utils/eth2_interop_keypairs/tests/test.rs
Normal file
64
eth2/utils/eth2_interop_keypairs/tests/test.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#![cfg(test)]
|
||||||
|
use eth2_interop_keypairs::{keypair, le_private_key};
|
||||||
|
use num_bigint::BigUint;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reference_private_keys() {
|
||||||
|
// Sourced from:
|
||||||
|
//
|
||||||
|
// https://github.com/ethereum/eth2.0-pm/blob/6e41fcf383ebeb5125938850d8e9b4e9888389b4/interop/mocked_start/keygen_test_vector.yaml
|
||||||
|
let reference = [
|
||||||
|
"16808672146709759238327133555736750089977066230599028589193936481731504400486",
|
||||||
|
"37006103240406073079686739739280712467525465637222501547219594975923976982528",
|
||||||
|
"22330876536127119444572216874798222843352868708084730796787004036811744442455",
|
||||||
|
"17048462031355941381150076874414096388968985457797372268770826099852902060945",
|
||||||
|
"28647806952216650698330424381872693846361470773871570637461872359310549743691",
|
||||||
|
"2416304019107052589452838695606585506736351107897780798170812672519914514344",
|
||||||
|
"7300215445567548136411883691093515822872548648751398235557229381530420545683",
|
||||||
|
"26495790445032093722332687600112008700915252495659977774957922313678954054133",
|
||||||
|
"2908643403277969554503670470854573663206729491025062456164283925661321952518",
|
||||||
|
"19554639423851580804889717218680781396599791537051606512605582393920758869044",
|
||||||
|
];
|
||||||
|
reference
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.for_each(|(i, reference)| {
|
||||||
|
let bytes = le_private_key(i);
|
||||||
|
let num = BigUint::from_bytes_le(&bytes);
|
||||||
|
assert_eq!(&num.to_str_radix(10), reference)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reference_public_keys() {
|
||||||
|
// Sourced from:
|
||||||
|
//
|
||||||
|
// https://github.com/ethereum/eth2.0-pm/blob/6e41fcf383ebeb5125938850d8e9b4e9888389b4/interop/mocked_start/keygen_test_vector.yaml
|
||||||
|
let reference = [
|
||||||
|
"qZp27XeW974i1bfoXe63xWd+iOUR4LM3YY+MTrYTSbS/LRU/ZJ97UzWf6LlKOORM",
|
||||||
|
"uJvrxpl2lyajGMjplxvTFxKXxhrqSmV4p6T5S1R9y6W6wWqJEItrah/jaV0ah0oL",
|
||||||
|
"o6MrD4tN24PxoKhT2B3XJd/ld9T0w9uOzlLOKwJuyoSBXBp+jpKk3j11VzO/fkqb",
|
||||||
|
"iMFB33fNnY16cadcgmxBqcnwPG7hsYDz54UvaigAmd7TUbWNZuZTr45CgWpNj1Mu",
|
||||||
|
"gSg7eiDhykYOvZu9dwBdVXNwyrsfmkT1MMTExmIw9nX434tMKBiFGqfXeoDKWkpe",
|
||||||
|
"qwvdoPhfhC9DG+rM8SUL8f17pRtBAP1kNktkAf2oW7AGmz5xW1iBloTn/AsQpyo0",
|
||||||
|
"mXfxyLcxqNVVgUa/uGyuomQ088WHi1ib8oCkLJFZ5wDp3w5AhilsILAR0ueMJ9Nz",
|
||||||
|
"qNTHwneVpyWWExfvWVOnAy7W2Dc524sOinI1PRuLRDlCf376LInKoDzJ8o+Muris",
|
||||||
|
"ptMQ27+rmiJFD1mZP4ekzl22Ij87Xx8w0sTscYki1ADgs8d0HejlmWD3JBGg7hCn",
|
||||||
|
"mJNBPAAoOj+e2f2YRd2hzqOCKNIlZ/lUHczDV+VKLWpuIEEDySVky8BfSQWsfEk6",
|
||||||
|
];
|
||||||
|
reference
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.for_each(|(i, reference)| {
|
||||||
|
let pair = keypair(i);
|
||||||
|
let reference = base64::decode(reference).expect("Reference should be valid base64");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
reference.len(),
|
||||||
|
48,
|
||||||
|
"Reference should be 48 bytes (public key size)"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(pair.pk.as_bytes(), reference);
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user