From 572adb2873132383eb971558e874fe2caede1905 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sun, 26 Aug 2018 18:39:41 +0000 Subject: [PATCH] add `FromStr` implementation for key types --- src/key.rs | 95 +++++++++++++++++++++++++++++++++++++++++------------- src/lib.rs | 54 +++++++++++++++++++------------ 2 files changed, 106 insertions(+), 43 deletions(-) diff --git a/src/key.rs b/src/key.rs index 2a5ea72..36af2c3 100644 --- a/src/key.rs +++ b/src/key.rs @@ -17,9 +17,9 @@ #[cfg(any(test, feature = "rand"))] use rand::Rng; -use std::{fmt, mem}; +use std::{fmt, mem, str}; -use super::{Secp256k1}; +use super::{from_hex, Secp256k1}; use super::Error::{self, InvalidPublicKey, InvalidSecretKey}; use Signing; use Verification; @@ -40,6 +40,17 @@ impl fmt::Display for SecretKey { } } +impl str::FromStr for SecretKey { + type Err = Error; + fn from_str(s: &str) -> Result { + let mut res = [0; constants::SECRET_KEY_SIZE]; + match from_hex(s, &mut res) { + Ok(constants::SECRET_KEY_SIZE) => Ok(SecretKey(res)), + _ => Err(Error::InvalidSecretKey) + } + } +} + /// The number 1 encoded as a secret key /// Deprecated; `static` is not what I want; use `ONE_KEY` instead pub static ONE: SecretKey = SecretKey([0, 0, 0, 0, 0, 0, 0, 0, @@ -73,6 +84,26 @@ impl fmt::Display for PublicKey { } } +impl str::FromStr for PublicKey { + type Err = Error; + fn from_str(s: &str) -> Result { + let secp = Secp256k1::without_caps(); + let mut res = [0; constants::UNCOMPRESSED_PUBLIC_KEY_SIZE]; + match from_hex(s, &mut res) { + Ok(constants::PUBLIC_KEY_SIZE) => { + PublicKey::from_slice( + &secp, + &res[0..constants::PUBLIC_KEY_SIZE] + ) + } + Ok(constants::UNCOMPRESSED_PUBLIC_KEY_SIZE) => { + PublicKey::from_slice(&secp, &res) + } + _ => Err(Error::InvalidPublicKey) + } + } +} + #[cfg(any(test, feature = "rand"))] fn random_32_bytes(rng: &mut R) -> [u8; 32] { let mut ret = [0u8; 32]; @@ -318,34 +349,22 @@ impl<'de> ::serde::Deserialize<'de> for PublicKey { #[cfg(test)] mod test { - use super::super::{Secp256k1}; + use Secp256k1; + use from_hex; use super::super::Error::{InvalidPublicKey, InvalidSecretKey}; use super::{PublicKey, SecretKey}; use super::super::constants; use rand::{Rng, thread_rng}; + use std::iter; + use std::str::FromStr; macro_rules! hex { - ($hex:expr) => { - { - let mut vec = Vec::new(); - let mut b = 0; - for (idx, c) in $hex.as_bytes().iter().enumerate() { - b <<= 4; - match *c { - b'A'...b'F' => b |= c - b'A' + 10, - b'a'...b'f' => b |= c - b'a' + 10, - b'0'...b'9' => b |= c - b'0', - _ => panic!("Bad hex"), - } - if (idx & 1) == 1 { - vec.push(b); - b = 0; - } - } - vec - } - } + ($hex:expr) => ({ + let mut result = vec![0; $hex.len() / 2]; + from_hex($hex, &mut result).expect("valid hex string"); + result + }); } #[test] @@ -482,10 +501,40 @@ mod test { sk.to_string(), "01010101010101010001020304050607ffff0000ffff00006363636363636363" ); + assert_eq!( + SecretKey::from_str("01010101010101010001020304050607ffff0000ffff00006363636363636363").unwrap(), + sk + ); assert_eq!( pk.to_string(), "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166" ); + assert_eq!( + PublicKey::from_str("0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166").unwrap(), + pk + ); + assert_eq!( + PublicKey::from_str("04\ + 18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166\ + 84B84DB303A340CD7D6823EE88174747D12A67D2F8F2F9BA40846EE5EE7A44F6" + ).unwrap(), + pk + ); + + assert!(SecretKey::from_str("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").is_err()); + assert!(SecretKey::from_str("01010101010101010001020304050607ffff0000ffff0000636363636363636363").is_err()); + assert!(SecretKey::from_str("01010101010101010001020304050607ffff0000ffff0000636363636363636").is_err()); + assert!(SecretKey::from_str("01010101010101010001020304050607ffff0000ffff000063636363636363").is_err()); + assert!(SecretKey::from_str("01010101010101010001020304050607ffff0000ffff000063636363636363xx").is_err()); + assert!(PublicKey::from_str("0300000000000000000000000000000000000000000000000000000000000000000").is_err()); + assert!(PublicKey::from_str("0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd16601").is_err()); + assert!(PublicKey::from_str("0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd16").is_err()); + assert!(PublicKey::from_str("0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd1").is_err()); + assert!(PublicKey::from_str("xx0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd1").is_err()); + + let long_str: String = iter::repeat('a').take(1024 * 1024).collect(); + assert!(SecretKey::from_str(&long_str).is_err()); + assert!(PublicKey::from_str(&long_str).is_err()); } #[test] diff --git a/src/lib.rs b/src/lib.rs index f437448..f7f9440 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -708,36 +708,50 @@ impl Secp256k1 { } } +/// Utility function used to parse hex into a target u8 buffer. Returns +/// the number of bytes converted or an error if it encounters an invalid +/// character or unexpected end of string. +fn from_hex(hex: &str, target: &mut [u8]) -> Result { + if hex.len() % 2 == 1 || hex.len() > target.len() * 2 { + return Err(()); + } + + let mut b = 0; + let mut idx = 0; + for c in hex.bytes() { + b <<= 4; + match c { + b'A'...b'F' => b |= c - b'A' + 10, + b'a'...b'f' => b |= c - b'a' + 10, + b'0'...b'9' => b |= c - b'0', + _ => return Err(()), + } + if (idx & 1) == 1 { + target[idx / 2] = b; + b = 0; + } + idx += 1; + } + Ok(idx / 2) +} + + #[cfg(test)] mod tests { use rand::{Rng, thread_rng}; use key::{SecretKey, PublicKey}; + use super::from_hex; use super::constants; use super::{Secp256k1, Signature, RecoverableSignature, Message, RecoveryId}; use super::Error::{InvalidMessage, IncorrectSignature, InvalidSignature}; macro_rules! hex { - ($hex:expr) => { - { - let mut vec = Vec::new(); - let mut b = 0; - for (idx, c) in $hex.as_bytes().iter().enumerate() { - b <<= 4; - match *c { - b'A'...b'F' => b |= c - b'A' + 10, - b'a'...b'f' => b |= c - b'a' + 10, - b'0'...b'9' => b |= c - b'0', - _ => panic!("Bad hex"), - } - if (idx & 1) == 1 { - vec.push(b); - b = 0; - } - } - vec - } - } + ($hex:expr) => ({ + let mut result = vec![0; $hex.len() / 2]; + from_hex($hex, &mut result).expect("valid hex string"); + result + }); } #[test]