Merge pull request #60 from rust-bitcoin/2018-08-fromstr
add `FromStr` implementation for key types
This commit is contained in:
commit
724192e081
95
src/key.rs
95
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<SecretKey, Error> {
|
||||
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<PublicKey, Error> {
|
||||
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<R: Rng>(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]
|
||||
|
|
131
src/lib.rs
131
src/lib.rs
|
@ -142,7 +142,7 @@
|
|||
extern crate libc;
|
||||
|
||||
use libc::size_t;
|
||||
use std::{error, fmt, ops, ptr};
|
||||
use std::{error, fmt, ops, ptr, str};
|
||||
#[cfg(any(test, feature = "rand"))] use rand::Rng;
|
||||
|
||||
#[macro_use]
|
||||
|
@ -161,9 +161,44 @@ use std::marker::PhantomData;
|
|||
pub struct RecoveryId(i32);
|
||||
|
||||
/// An ECDSA signature
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Signature(ffi::Signature);
|
||||
|
||||
impl fmt::Debug for Signature {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Signature {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut v = [0; 72];
|
||||
let mut len = v.len() as size_t;
|
||||
let secp = Secp256k1::without_caps();
|
||||
unsafe {
|
||||
let err = ffi::secp256k1_ecdsa_signature_serialize_der(secp.ctx, v.as_mut_ptr(),
|
||||
&mut len, self.as_ptr());
|
||||
debug_assert!(err == 1);
|
||||
}
|
||||
for ch in &v[..] {
|
||||
write!(f, "{:02x}", *ch)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl str::FromStr for Signature {
|
||||
type Err = Error;
|
||||
fn from_str(s: &str) -> Result<Signature, Error> {
|
||||
let secp = Secp256k1::without_caps();
|
||||
let mut res = [0; 72];
|
||||
match from_hex(s, &mut res) {
|
||||
Ok(x) => Signature::from_der(&secp, &res[0..x]),
|
||||
_ => Err(Error::InvalidSignature),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An ECDSA signature with a recovery ID for pubkey recovery
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct RecoverableSignature(ffi::RecoverableSignature);
|
||||
|
@ -708,36 +743,51 @@ impl<C: Verification> Secp256k1<C> {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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<usize, ()> {
|
||||
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 std::str::FromStr;
|
||||
|
||||
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]
|
||||
|
@ -836,6 +886,43 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signature_display() {
|
||||
let secp = Secp256k1::without_caps();
|
||||
let hex_str = "3046022100839c1fbc5304de944f697c9f4b1d01d1faeba32d751c0f7acb21ac8a0f436a72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011c4fcc72eab45";
|
||||
let byte_str = hex!(hex_str);
|
||||
|
||||
assert_eq!(
|
||||
Signature::from_der(&secp, &byte_str).expect("byte str decode"),
|
||||
Signature::from_str(&hex_str).expect("byte str decode")
|
||||
);
|
||||
|
||||
let sig = Signature::from_str(&hex_str).expect("byte str decode");
|
||||
assert_eq!(&sig.to_string(), hex_str);
|
||||
assert_eq!(&format!("{:?}", sig), hex_str);
|
||||
|
||||
assert!(Signature::from_str(
|
||||
"3046022100839c1fbc5304de944f697c9f4b1d01d1faeba32d751c0f7acb21ac8a0f436a\
|
||||
72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011c4fcc72eab4"
|
||||
).is_err());
|
||||
assert!(Signature::from_str(
|
||||
"3046022100839c1fbc5304de944f697c9f4b1d01d1faeba32d751c0f7acb21ac8a0f436a\
|
||||
72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011c4fcc72eab"
|
||||
).is_err());
|
||||
assert!(Signature::from_str(
|
||||
"3046022100839c1fbc5304de944f697c9f4b1d01d1faeba32d751c0f7acb21ac8a0f436a\
|
||||
72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011c4fcc72eabxx"
|
||||
).is_err());
|
||||
assert!(Signature::from_str(
|
||||
"3046022100839c1fbc5304de944f697c9f4b1d01d1faeba32d751c0f7acb21ac8a0f436a\
|
||||
72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011c4fcc72eab45\
|
||||
72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011c4fcc72eab45\
|
||||
72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011c4fcc72eab45\
|
||||
72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011c4fcc72eab45\
|
||||
72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011c4fcc72eab45"
|
||||
).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signature_lax_der() {
|
||||
macro_rules! check_lax_sig(
|
||||
|
|
Loading…
Reference in New Issue