use sodiumoxide::crypto::sign::ed25519;
use error::{Error, Result};
use signature::{DecoratedSignature, Signature, SignatureHint};
use network::Network;
use crypto::strkey;
use crypto;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PublicKey {
key: ed25519::PublicKey,
}
impl PublicKey {
pub fn from_account_id(account_id: &str) -> Result<PublicKey> {
let bytes = strkey::decode_account_id(&account_id)?;
Self::from_slice(&bytes)
}
pub fn from_slice(data: &[u8]) -> Result<PublicKey> {
let key = ed25519::PublicKey::from_slice(&data).ok_or(Error::InvalidPublicKey)?;
Ok(PublicKey { key })
}
pub fn account_id(&self) -> Result<String> {
strkey::encode_account_id(&self.key.0)
}
pub fn buf(&self) -> &[u8] {
&self.key.0
}
pub fn inner(&self) -> &ed25519::PublicKey {
&self.key
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SecretKey {
key: ed25519::SecretKey,
}
impl SecretKey {
pub fn inner(&self) -> &ed25519::SecretKey {
&self.key
}
pub fn secret_seed(&self) -> Result<String> {
strkey::encode_secret_seed(&self.key.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct KeyPair {
public: PublicKey,
secret: SecretKey,
}
impl KeyPair {
pub fn from_secret_seed(data: &str) -> Result<KeyPair> {
let bytes = strkey::decode_secret_seed(&data)?;
Self::from_seed_bytes(&bytes)
}
pub fn random() -> Result<KeyPair> {
let seed = crypto::random_bytes(32);
Self::from_seed_bytes(&seed)
}
pub fn from_network(network: &Network) -> Result<KeyPair> {
let bytes = network.network_id();
Self::from_seed_bytes(&bytes)
}
pub fn from_seed_bytes(data: &[u8]) -> Result<KeyPair> {
let the_seed = ed25519::Seed::from_slice(&data).ok_or(Error::InvalidSeed)?;
let (pk, sk) = ed25519::keypair_from_seed(&the_seed);
let public = PublicKey { key: pk };
let secret = SecretKey { key: sk };
Ok(KeyPair { public, secret })
}
pub fn public_key(&self) -> &PublicKey {
&self.public
}
pub fn secret_key(&self) -> &SecretKey {
&self.secret
}
pub fn sign(&self, message: &[u8]) -> Signature {
Signature::sign(&self.secret, &message)
}
pub fn sign_decorated(&self, message: &[u8]) -> DecoratedSignature {
let hint = self.signature_hint();
let signature = self.sign(message);
DecoratedSignature::new(hint, signature)
}
pub fn verify(&self, message: &[u8], signature: &Signature) -> bool {
signature.verify(&self.public, &message)
}
pub fn signature_hint(&self) -> SignatureHint {
SignatureHint::from_public_key(&self.public)
}
}
#[cfg(test)]
mod tests {
use super::KeyPair;
use Network;
#[test]
fn test_from_secret_seed() {
let keypairs = [
(
"SCRG6SFG64YDEVGWDWTZBE6BWEW25WICOGOUTODVICCA3L3FOQIG26E6",
"GDRZ6UR4VI3DWATUKAXCGCGAUXGF3IJYG5T3CTJDUJ674TFLN4AR6RV4",
),
(
"SCOHYGOOQBONAMHLFFTS3OHG2V45GHRVU6Q5HI7VZ2T4C3WRSVCUAWRN",
"GB5YW3UGJQ635LBGCXJVU3D2TXJIQRX7LRRWFMD2PKZLQ3G2OIZY4ZWO",
),
(
"SD4IDUZJZKPWU2T2AJWB35AN42L3NADBRCXHL5HY4WXPWO43MQTMCIIF",
"GCEAKB6W342KSAQ6SVJYROF5W5FJTPZDDOSIOT3Y6CNQ3U2ZBAH7AQN3",
),
(
"SBB3NC6JM6IT6RXXQKMWH5XBGWWVWXL2MDBVOUPWWKP52QRFB2ISF2IR",
"GCG5C6WXDSN55I25M7C6734EYVJ562KR4CKWEKK7RISUISNO36LX5J7G",
),
(
"SDGLIKRUQX5WJCLKGJUSAG3XK7GW262H7SQMLCKV4K5RWXNOWFD5VFG6",
"GAGKPXZNQ7QDYKRMKE34IB7S5SUKPK4AWETZRBG6CYK3TNANV4HQHJGV",
),
(
"SD3AXQQRD4HXWHWVX7R56UHPT7ZDJPADTYTC3UTGRYHZ2AR37O66OZQB",
"GD6KUX4TY4BFKCQBNAFK577IWNB3PNPASBG2JEBIJK6X4CSXNRWCINHU",
),
(
"SBHRV6BPRPIMTY7EYWFOLUNILQDU3UP5KWQA43UPW7R6WBTWTUGCY446",
"GARHX6B4IAM2E3WUISLBWYY6TVWWQIWZPUYWXHSQ5BSSV5NKQX5CT4MC",
),
(
"SA7M4FOPTIT3CNW3B44CQMQRA4A7ZJCAOVHRUEHAMGFVWWCLIA3UXULE",
"GASQUYLK6GRHBE6Z4A4HH5NHQPAHMHWAUSQP5N7D465QYYBEHDOPFXVP",
),
(
"SBKNQCZGK2X2VDOIAZWWZ7H3J5XWSU23UGUU62IXVTMWC6AGVN4N3OX7",
"GD7EMEPGLBYDHUEDSJMAI64BS6HX46SAIPGIF3E5HI4CJ53VQ4OWEEBE",
),
(
"SA7UGZLM6Q6RR4BC7252IMUAHCZJLDHGBJFKVENIE2HKQ66T7L4T4ZMF",
"GC22QEDRADRMZ2RPSBGY5N3RMGGSBKCMBQBFCJH3M4RZQ4KQQXUVQNID",
),
];
for &(secret, address) in keypairs.iter() {
let keypair = KeyPair::from_secret_seed(&secret).unwrap();
let account_id = keypair.public_key().account_id().unwrap();
assert_eq!(&account_id, address);
}
}
#[test]
fn test_from_network() {
let network = Network::public_network();
let kp = KeyPair::from_network(&network).unwrap();
let public = kp.public_key().account_id().unwrap();
assert_eq!(
public,
"GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7"
);
}
#[test]
fn test_sign_and_verify() {
let the_secret = "SD7X7LEHBNMUIKQGKPARG5TDJNBHKC346OUARHGZL5ITC6IJPXHILY36";
let kp = KeyPair::from_secret_seed(&the_secret).unwrap();
let message = "test post please ignore".as_bytes();
let sign = kp.sign(&message);
let expected_sign = vec![
0x19, 0xDB, 0xFD, 0xAF, 0x0A, 0xA8, 0x4D, 0xF9, 0xA7, 0xFF, 0x6F, 0xE3, 0xC1, 0x0E,
0xBC, 0x1F, 0xE2, 0x14, 0xC5, 0x10, 0xB9, 0x5D, 0xB0, 0xD6, 0x33, 0xBE, 0xD9, 0x3D,
0xF9, 0x25, 0x6B, 0xA9, 0x92, 0xEF, 0x7D, 0x94, 0xB2, 0xA6, 0xE4, 0x54, 0xDE, 0x8F,
0x21, 0x9, 0x28, 0xCA, 0x96, 0x11, 0x39, 0x03, 0x29, 0xC8, 0x40, 0xC8, 0xE5, 0x64,
0xE7, 0xA0, 0x72, 0x16, 0x02, 0x7A, 0xB4, 0xA,
];
assert_eq!(sign.to_vec(), expected_sign);
assert!(kp.verify(&message, &sign));
}
#[test]
fn test_sign_decorated() {
let the_secret = "SD7X7LEHBNMUIKQGKPARG5TDJNBHKC346OUARHGZL5ITC6IJPXHILY36";
let kp = KeyPair::from_secret_seed(&the_secret).unwrap();
let message = "test post please ignore".as_bytes();
let sign = kp.sign_decorated(&message);
assert_eq!(sign.hint().to_vec(), vec![0x0B, 0xFA, 0xD1, 0x34]);
}
}