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

add FromStr implementation for key types #60

Merged
merged 3 commits into from
Nov 4, 2018
Merged
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
95 changes: 72 additions & 23 deletions src/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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]
Expand Down
131 changes: 109 additions & 22 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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);
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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(
Expand Down