Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Signing a byte slice with and PrivateKey and verifying with PublicKey… #2

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ blake2 = "0.9.1"
byteorder = "1.4.3"
data-encoding = "2.3.2"
data-encoding-macro = "0.1.12"
ed25519-dalek = "1.0.1"
ed25519-dalek = { version = "1.0.1", package = "ed25519-dalek-blake2-feeless" }
curve25519-dalek = "1.2.1"
chrono = "0.4"
strum = "0.21.0"
Expand Down
2 changes: 2 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,6 @@ pub enum Error {
},
#[error("Try from slice error")]
TryFromSliceError(#[from] std::array::TryFromSliceError),
#[error("Signature does not match.")]
SignatureError(),
}
35 changes: 21 additions & 14 deletions src/types/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,29 @@ pub struct Address(pub String);
impl Address {
/// Compute [PublicKey] from the Banano address
pub fn to_public_key(&self) -> Result<PublicKey, Error> {
if let Some("ban_") = self.0.get(..4) {
if self.0.len() == 64 {
let mut encoded_addr = String::from(self.0.get(4..56).unwrap());
encoded_addr.insert_str(0, "1111");
let checksum = self.0.get(56..).unwrap();
let pkey_bytes = super::BAN_ENCODING.decode(encoded_addr.as_bytes())?;
let derived_checksum = super::BAN_ENCODING.encode(&super::compute_address_checksum(&pkey_bytes[3..]));
if checksum != derived_checksum {
return Err(Error::InvalidAddress);
}
return Ok(super::pubkey::PublicKey(pkey_bytes[3..].try_into().expect("Not enough bytes for key")))
}
return Err(Error::InvalidAddressLength(self.0.len()));
Address::create_public_key(&self.0)
}

pub fn create_public_key(pub_str :&String) -> Result<PublicKey, Error> {
if pub_str.get(..4).unwrap() != "ban_" {
return Err(Error::InvalidAddress);
}

if pub_str.len() != 64 {
return Err(Error::InvalidAddressLength(pub_str.len()));
}
Err(Error::InvalidAddress)

let mut encoded_addr = String::from(pub_str.get(4..56).unwrap());
encoded_addr.insert_str(0, "1111");
let checksum = pub_str.get(56..).unwrap();
let pkey_bytes = super::BAN_ENCODING.decode(encoded_addr.as_bytes())?;
let derived_checksum = super::BAN_ENCODING.encode(&super::compute_address_checksum(&pkey_bytes[3..]));
if checksum != derived_checksum {
return Err(Error::InvalidAddress);
}
Ok(super::pubkey::PublicKey(pkey_bytes[3..].try_into().expect("Not enough bytes for key")))
}

}

impl From<PublicKey> for Address {
Expand Down
3 changes: 2 additions & 1 deletion src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ mod tests {

expected_output.into_iter().enumerate().for_each(|(index, address)| {
let priv_key = PrivateKey::from_seed(seed.clone(), index as u32);
let account: Account = priv_key.into();
let public_key : PublicKey = priv_key.into();
let account: Account = public_key.into();

assert_eq!(account.address.0, address)
})
Expand Down
188 changes: 163 additions & 25 deletions src/types/privkey.rs
Original file line number Diff line number Diff line change
@@ -1,45 +1,183 @@
use super::Seed;
use ed25519_dalek::SecretKey;
use blake2::{
VarBlake2b,
digest::{Update, VariableOutput},
};
use byteorder::{BigEndian, WriteBytesExt};
use super::PublicKey;

use ed25519_dalek::{SecretKey, ExpandedSecretKey};

use blake2::{VarBlake2b};
use blake2::digest::{Update, VariableOutput};

use std::ops::{Deref, DerefMut};

const SIGNATURE_LENGTH : usize = 64;

/// Banano PrivateKey
#[derive(Debug)]
pub struct PrivateKey(SecretKey);
#[derive(Debug, Copy, Clone)]
pub struct PrivateKey(pub(crate)[u8; 32]);

impl PrivateKey {
pub fn from_seed(seed: Seed, index: u32) -> PrivateKey {
let mut blake = VarBlake2b::new(32).unwrap();
let mut index_buf = Vec::with_capacity(4);
index_buf.write_u32::<BigEndian>(index).unwrap();
blake.update(&*seed);
blake.update(&index_buf);

// Update conflicts with the update trate in blake2::Blake2 so it is shadowed like this
// instead of imported above.
let mut blake = VarBlake2b::new(32).expect("Output size was zero");
blake.update(seed.0);
blake.update(index.to_be_bytes());
let mut buf = [0u8; 32];
blake.finalize_variable(|res| buf.copy_from_slice(res));
PrivateKey(SecretKey::from_bytes(&buf).unwrap())
PrivateKey(buf)
}
}

impl Deref for PrivateKey {
type Target = SecretKey;
fn deref(&self) -> &Self::Target {
&self.0
pub fn copy_bytes(&self) -> [u8; 32] {
self.0.clone()
}
}

impl DerefMut for PrivateKey {
fn deref_mut(&mut self) -> &mut SecretKey {
&mut self.0
// pub fn sign<T: AsRef<[u8]>>(&self, data: T) -> [u8; 64]
// {
// let mut keys = Vec::new();
// keys.append_elements(+)
// let expanded: ExpandedSecretKey = (&self.0).into();
// // Less complex API but requires more math by deriving the public key each time
// // vs. having a KeyPair type do the signing which is more common.
// let pkey_copy = PrivateKey(SecretKey::from_bytes(&self.copy_bytes()).expect("Could not create private key from bytes"));
// let public_key: PublicKey = pkey_copy.into();
// let public_key: DalicPublicKey = public_key.into();
// self.0.sign
// expanded.sign(data.as_ref(), &public_key).to_bytes()

// }

pub fn sign<T: AsRef<[u8]>>(&self, message: T) -> [u8; SIGNATURE_LENGTH]
{
let secret_key = SecretKey::from_bytes(&self.0).expect("Could not get secret from bytes.");
let expanded_secret = ExpandedSecretKey::from(&secret_key);
let signed = expanded_secret.sign(message.as_ref(), &ed25519_dalek::PublicKey::from(&secret_key));
signed.to_bytes()
}
}

impl From<PrivateKey> for [u8; 32] {
fn from(key: PrivateKey) -> Self {
key.0.to_bytes()
key.0
}
}
}

impl From<PrivateKey> for PublicKey {
fn from(key: PrivateKey) -> Self {
let secret_key = SecretKey::from_bytes(&key.0).expect("Could not get secret from bytes.");
let pk = ed25519_dalek::PublicKey::from(&secret_key);
PublicKey(pk.to_bytes())
}
}


#[cfg(test)]
mod tests {
use super::*;
use crate::types::Address;
use crate::types::PublicKey;
use crate::Raw;

use std::str::FromStr;

#[test]
/**
* A not so random block on the chain to validate signing is working. This block
* was from a crypto puzzle where the private key is publicly known already.
*
* TODO: This test can also be the basis for writing the block hash function.
{
"block_account": "ban_3wtsduys8b7jkbfwwfzx3jgpgpsi9b8zurfe9bp1p5cdxkqiz7a5wxcoo7ba",
"amount": "500000000000000000000000000000",
"balance": "5500000000000000000000000000000",
"height": "2",
"local_timestamp": "1623383021",
"confirmed": "true",
"contents": {
"type": "state",
"account": "ban_3wtsduys8b7jkbfwwfzx3jgpgpsi9b8zurfe9bp1p5cdxkqiz7a5wxcoo7ba",
"previous": "04BC2337372B048F838977A0608214253536C82F2895573A381B6070B8C1F279",
"representative": "ban_1bananobh5rat99qfgt1ptpieie5swmoth87thi74qgbfrij7dcgjiij94xr",
"balance": "5500000000000000000000000000000",
"link": "AC9BFECE984B21EBC0663866050FFA110CDA3A3D8EFA65E08B22FCB2AC6FA08D",
"link_as_account": "ban_3d6uzu9biks3xh18eg581n9zn6aeuax5u5qteqiapaqwpcp8za6fs6fddhjy",
"signature": "12B1F6AB3A15C11C079791B1301731CBE7878DBFF8D81831675127B10DA2B683BF36BC886BF11A9DC53FF53311E0F11C022722A7B0284EE10699D3315E48140C",
"work": "f0aabba178b4573e",
"subtype": "receive"
},
"subtype": "receive",
"pending": "0",
"source_account": "ban_1m5ngqyrq58egujc3nz67wmtpay3t38bcuu5ymi7qddpzzznt1xii13ippu1",
"timestamp": "1623383021655"
}
*/

fn can_generate_valid_signature() {
// use blake2::digest::{Update, VariableOutput};
use hex_literal::hex;
use hex as hex_parse;
// What type of an attack uses the same id as another key on a public-key crypto system.
let private_seed = Seed(hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
let private_key = PrivateKey::from_seed(private_seed, 0);
let expected_signature = String::from("12B1F6AB3A15C11C079791B1301731CBE7878DBFF8D81831675127B10DA2B683BF36BC886BF11A9DC53FF53311E0F11C022722A7B0284EE10699D3315E48140C");

// The following comment out code has some issue not hashing correctly. I'm not sure why.

// let mut preamble = [0u8; 32];
// preamble[31] = 6u8;


// let block_account: [u8; 32] = Address::create_public_key(&String::from("ban_3wtsduys8b7jkbfwwfzx3jgpgpsi9b8zurfe9bp1p5cdxkqiz7a5wxcoo7ba"))
// .unwrap().into();
// let previous = hex!("04BC2337372B048F838977A0608214253536C82F2895573A381B6070B8C1F279");
// let represenative: [u8; 32] = Address::create_public_key(&String::from("ban_1bananobh5rat99qfgt1ptpieie5swmoth87thi74qgbfrij7dcgjiij94xr"))
// .unwrap().into();
// // Balance has to be padded to 32 characters
// let balance : [u8; 16] = Raw::from_str("5500000000000000000000000000000").unwrap().to_u128().to_be_bytes();
// let link = hex!("AC9BFECE984B21EBC0663866050FFA110CDA3A3D8EFA65E08B22FCB2AC6FA08D");

// let hasher = VarBlake2b::new(64).unwrap();

// println!("{:?} {:?} {:?} {:?} {:?} {:?}", preamble, block_account, previous, represenative, balance, link);
// hasher.update(preamble);
// hasher.update(block_account);
// hasher.update(previous);
// hasher.update(represenative);
// hasher.update(balance);
// hasher.update(link);
// let mut hash = [0u8; 64];

/* This is the result of the command to a node for this block. I do not know why I can't derive the hash correctly, something to solve more generally anyway.
curl -H "Content-Type: application/json" --data '{ "action": "block_hash", "json_block": "true", "block": {
"type": "state",
"account": "ban_3wtsduys8b7jkbfwwfzx3jgpgpsi9b8zurfe9bp1p5cdxkqiz7a5wxcoo7ba",
"previous": "04BC2337372B048F838977A0608214253536C82F2895573A381B6070B8C1F279",
"representative": "ban_1bananobh5rat99qfgt1ptpieie5swmoth87thi74qgbfrij7dcgjiij94xr",
"balance": "5500000000000000000000000000000",
"link": "AC9BFECE984B21EBC0663866050FFA110CDA3A3D8EFA65E08B22FCB2AC6FA08D",
"link_as_account": "ban_3d6uzu9biks3xh18eg581n9zn6aeuax5u5qteqiapaqwpcp8za6fs6fddhjy",
"signature": "12B1F6AB3A15C11C079791B1301731CBE7878DBFF8D81831675127B10DA2B683BF36BC886BF11A9DC53FF53311E0F11C022722A7B0284EE10699D3315E48140C",
"work": "f0aabba178b4573e",
"subtype": "receive"
}}' http://rimmer:7072


{
"hash": "938719851C7BAC4700B52FD0C0DD1CB4F47A1577B370F26616FA4D26B800B3E1"
}
*/
let hash = hex!("938719851C7BAC4700B52FD0C0DD1CB4F47A1577B370F26616FA4D26B800B3E1");
//hasher.finalize_variable(|res| hash.copy_from_slice(res));
// println!("HASH: {}", hex_parse::encode_upper(&hash));
let signature = hex_parse::encode_upper(private_key.sign(hash));
println!("{:?}", signature);
assert_eq!(expected_signature, signature);
}

#[test]
fn validate_private_key(){
let private_seed = Seed::from("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef").expect("Couldn't create seed.");
let private_key = PrivateKey::from_seed(private_seed, 0);
let pubKey = PublicKey::from(private_key);
assert_eq!(Address::from(pubKey).0, String::from("ban_3wtsduys8b7jkbfwwfzx3jgpgpsi9b8zurfe9bp1p5cdxkqiz7a5wxcoo7ba"));
}
}
61 changes: 42 additions & 19 deletions src/types/pubkey.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,53 @@
use super::privkey::PrivateKey;
use blake2::{Blake2b, Digest};
use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE;
use curve25519_dalek::scalar::Scalar as CurveScaler;
use ed25519_dalek::PublicKey as DalekPublicKey;
use crate::Error;
use ed25519_dalek::{Verifier, Signature};
use std::convert::TryInto;
use std::convert::TryFrom;

#[derive(Debug, Clone)]
#[derive(Debug, Copy, Clone)]
pub struct PublicKey(pub [u8; 32]);

impl From<PrivateKey> for PublicKey {
fn from(key: PrivateKey) -> Self {
let mut bhasher = Blake2b::default();
let bkey: [u8; 32] = key.into();
bhasher.update(&bkey);
let hresult = bhasher.finalize();
let mut scaler = [0u8; 32];
scaler.copy_from_slice(&hresult.as_slice()[..32]);
scaler[0] &= 248;
scaler[31] &= 63;
scaler[31] |= 64;
let cscaler = CurveScaler::from_bits(scaler);
let point = &cscaler * &ED25519_BASEPOINT_TABLE;
PublicKey(point.compress().to_bytes())
impl PublicKey {
pub fn to_ed25519_public_key(&self) -> DalekPublicKey {
DalekPublicKey::from_bytes(&self.0).expect("Could not get private key from bytes.")

}
pub fn verify(&self, message: &[u8], signature: &[u8; 64]) -> Result<(), Error> {
let key = self.to_ed25519_public_key();
key.verify(message, &Signature::try_from(&signature[..]).expect("Could not create signature from bytes."))
.map_err(|e| Error::SignatureError())
}

}

impl From<PublicKey> for[u8; 32] {
fn from(key: PublicKey) -> Self {
key.0
}
}

impl From<PublicKey> for DalekPublicKey {
fn from(key: PublicKey) -> Self {
DalekPublicKey::from_bytes(&key.0[..]).expect("Could not get public key from bytes.")
}
}

#[cfg(test)]
mod tests {

#[test]
fn can_validate_signatures() {
use crate::types::Seed;
use crate::types::PrivateKey;
use crate::types::PublicKey;
use hex_literal::hex;

let hash = hex!("938719851C7BAC4700B52FD0C0DD1CB4F47A1577B370F26616FA4D26B800B3E1");
let seed = Seed([0u8; 32]);
let private_key = PrivateKey::from_seed(seed, 0);
let public_key = PublicKey::from(private_key);
let signature = private_key.sign(hash);
assert_eq!((), public_key.verify(&hash, &signature).unwrap());

}
}
2 changes: 1 addition & 1 deletion src/types/seed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::ops::{Deref, DerefMut};

/// Banano Seed
#[derive(Clone)]
pub struct Seed(pub [u8; 32]);
pub struct Seed(pub(crate) [u8; 32]);

impl Seed {
pub fn from<T: AsRef<[u8]>>(seed: T) -> Result<Self, Error> {
Expand Down