Skip to content

Commit

Permalink
Move hex code to new submodule
Browse files Browse the repository at this point in the history
In preparation for adding separate errors move the hex code to its own
private module.
  • Loading branch information
tcharding committed Nov 6, 2023
1 parent ebfb3e2 commit ec02f3f
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 94 deletions.
6 changes: 3 additions & 3 deletions src/ecdh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use secp256k1_sys::types::{c_int, c_uchar, c_void};

use crate::ffi::{self, CPtr};
use crate::key::{PublicKey, SecretKey};
use crate::{constants, Error};
use crate::{constants, hex, Error};

// The logic for displaying shared secrets relies on this (see `secret.rs`).
const SHARED_SECRET_SIZE: usize = constants::SECRET_KEY_SIZE;
Expand Down Expand Up @@ -81,7 +81,7 @@ impl str::FromStr for SharedSecret {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut res = [0u8; SHARED_SECRET_SIZE];
match crate::from_hex(s, &mut res) {
match hex::from_hex(s, &mut res) {
Ok(SHARED_SECRET_SIZE) => Ok(SharedSecret::from_bytes(res)),
_ => Err(Error::InvalidSharedSecret),
}
Expand Down Expand Up @@ -160,7 +160,7 @@ impl ::serde::Serialize for SharedSecret {
fn serialize<S: ::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
if s.is_human_readable() {
let mut buf = [0u8; SHARED_SECRET_SIZE * 2];
s.serialize_str(crate::to_hex(&self.0, &mut buf).expect("fixed-size hex serialization"))
s.serialize_str(hex::to_hex(&self.0, &mut buf).expect("fixed-size hex serialization"))
} else {
s.serialize_bytes(self.as_ref())
}
Expand Down
6 changes: 2 additions & 4 deletions src/ecdsa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ pub use self::serialized_signature::SerializedSignature;
use crate::ffi::CPtr;
#[cfg(feature = "global-context")]
use crate::SECP256K1;
use crate::{
ffi, from_hex, Error, Message, PublicKey, Secp256k1, SecretKey, Signing, Verification,
};
use crate::{ffi, hex, Error, Message, PublicKey, Secp256k1, SecretKey, Signing, Verification};

/// An ECDSA signature
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
Expand All @@ -39,7 +37,7 @@ impl str::FromStr for Signature {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut res = [0u8; 72];
match from_hex(s, &mut res) {
match hex::from_hex(s, &mut res) {
Ok(x) => Signature::from_der(&res[0..x]),
_ => Err(Error::InvalidSignature),
}
Expand Down
8 changes: 4 additions & 4 deletions src/ellswift.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ use core::str::FromStr;
use ffi::CPtr;
use secp256k1_sys::types::{c_int, c_uchar, c_void};

use crate::{constants, ffi, from_hex, Error, PublicKey, Secp256k1, SecretKey, Verification};
use crate::{constants, ffi, hex, Error, PublicKey, Secp256k1, SecretKey, Verification};

unsafe extern "C" fn hash_callback<F>(
output: *mut c_uchar,
Expand Down Expand Up @@ -292,7 +292,7 @@ impl FromStr for ElligatorSwift {

fn from_str(hex: &str) -> Result<Self, Self::Err> {
let mut ser = [0u8; 64];
let parsed = from_hex(hex, &mut ser);
let parsed = hex::from_hex(hex, &mut ser);
match parsed {
Ok(64) => Ok(ElligatorSwift::from_array(ser)),
_ => Err(Error::InvalidEllSwift),
Expand Down Expand Up @@ -329,7 +329,7 @@ mod tests {
use crate::ellswift::{ElligatorSwiftParty, ElligatorSwiftSharedSecret};
#[cfg(all(not(secp256k1_fuzz), feature = "alloc"))]
use crate::SecretKey;
use crate::{from_hex, PublicKey, XOnlyPublicKey};
use crate::{hex, PublicKey, XOnlyPublicKey};

#[test]
#[cfg(all(not(secp256k1_fuzz), feature = "alloc"))]
Expand Down Expand Up @@ -604,7 +604,7 @@ mod tests {
#[inline]
fn parse_test(ell: &str, x: &str, parity: u32) -> EllswiftDecodeTest {
let mut enc = [0u8; 64];
from_hex(ell, &mut enc).unwrap();
hex::from_hex(ell, &mut enc).unwrap();
let xo = XOnlyPublicKey::from_str(x).unwrap();
let parity = if parity == 0 { crate::Parity::Even } else { crate::Parity::Odd };
let pk = PublicKey::from_x_only_public_key(xo, parity);
Expand Down
54 changes: 54 additions & 0 deletions src/hex.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: CC0-1.0

//! Conversion to and from hexadecimal strings.

use core::str;

/// 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.
pub(crate) 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)
}

/// Utility function used to encode hex into a target u8 buffer. Returns
/// a reference to the target buffer as an str. Returns an error if the target
/// buffer isn't big enough.
#[inline]
pub(crate) fn to_hex<'a>(src: &[u8], target: &'a mut [u8]) -> Result<&'a str, ()> {
let hex_len = src.len() * 2;
if target.len() < hex_len {
return Err(());
}
const HEX_TABLE: [u8; 16] = *b"0123456789abcdef";

let mut i = 0;
for &b in src {
target[i] = HEX_TABLE[usize::from(b >> 4)];
target[i + 1] = HEX_TABLE[usize::from(b & 0b00001111)];
i += 2;
}
let result = &target[..hex_len];
debug_assert!(str::from_utf8(result).is_ok());
return unsafe { Ok(str::from_utf8_unchecked(result)) };
}
25 changes: 11 additions & 14 deletions src/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ use crate::ffi::{self, CPtr};
use crate::Error::{self, InvalidPublicKey, InvalidPublicKeySum, InvalidSecretKey};
#[cfg(feature = "global-context")]
use crate::SECP256K1;
use crate::{
constants, ecdsa, from_hex, schnorr, Message, Scalar, Secp256k1, Signing, Verification,
};
use crate::{constants, ecdsa, hex, schnorr, Message, Scalar, Secp256k1, Signing, Verification};
#[cfg(feature = "hashes")]
use crate::{hashes, ThirtyTwoByteHash};

Expand Down Expand Up @@ -114,7 +112,7 @@ impl str::FromStr for SecretKey {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut res = [0u8; constants::SECRET_KEY_SIZE];
match from_hex(s, &mut res) {
match hex::from_hex(s, &mut res) {
Ok(constants::SECRET_KEY_SIZE) => SecretKey::from_slice(&res),
_ => Err(Error::InvalidSecretKey),
}
Expand Down Expand Up @@ -167,7 +165,7 @@ impl str::FromStr for PublicKey {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut res = [0u8; constants::UNCOMPRESSED_PUBLIC_KEY_SIZE];
match from_hex(s, &mut res) {
match hex::from_hex(s, &mut res) {
Ok(constants::PUBLIC_KEY_SIZE) =>
PublicKey::from_slice(&res[0..constants::PUBLIC_KEY_SIZE]),
Ok(constants::UNCOMPRESSED_PUBLIC_KEY_SIZE) => PublicKey::from_slice(&res),
Expand Down Expand Up @@ -385,7 +383,7 @@ impl serde::Serialize for SecretKey {
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
if s.is_human_readable() {
let mut buf = [0u8; constants::SECRET_KEY_SIZE * 2];
s.serialize_str(crate::to_hex(&self.0, &mut buf).expect("fixed-size hex serialization"))
s.serialize_str(hex::to_hex(&self.0, &mut buf).expect("fixed-size hex serialization"))
} else {
let mut tuple = s.serialize_tuple(constants::SECRET_KEY_SIZE)?;
for byte in self.0.iter() {
Expand Down Expand Up @@ -859,7 +857,7 @@ impl Keypair {
#[inline]
pub fn from_seckey_str<C: Signing>(secp: &Secp256k1<C>, s: &str) -> Result<Keypair, Error> {
let mut res = [0u8; constants::SECRET_KEY_SIZE];
match from_hex(s, &mut res) {
match hex::from_hex(s, &mut res) {
Ok(constants::SECRET_KEY_SIZE) =>
Keypair::from_seckey_slice(secp, &res[0..constants::SECRET_KEY_SIZE]),
_ => Err(Error::InvalidPublicKey),
Expand Down Expand Up @@ -1041,8 +1039,7 @@ impl serde::Serialize for Keypair {
if s.is_human_readable() {
let mut buf = [0u8; constants::SECRET_KEY_SIZE * 2];
s.serialize_str(
crate::to_hex(&self.secret_bytes(), &mut buf)
.expect("fixed-size hex serialization"),
hex::to_hex(&self.secret_bytes(), &mut buf).expect("fixed-size hex serialization"),
)
} else {
let mut tuple = s.serialize_tuple(constants::SECRET_KEY_SIZE)?;
Expand Down Expand Up @@ -1134,7 +1131,7 @@ impl str::FromStr for XOnlyPublicKey {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut res = [0u8; constants::SCHNORR_PUBLIC_KEY_SIZE];
match from_hex(s, &mut res) {
match hex::from_hex(s, &mut res) {
Ok(constants::SCHNORR_PUBLIC_KEY_SIZE) =>
XOnlyPublicKey::from_slice(&res[0..constants::SCHNORR_PUBLIC_KEY_SIZE]),
_ => Err(Error::InvalidPublicKey),
Expand Down Expand Up @@ -1559,13 +1556,13 @@ mod test {

use super::{Keypair, Parity, PublicKey, Secp256k1, SecretKey, XOnlyPublicKey, *};
use crate::Error::{InvalidPublicKey, InvalidSecretKey};
use crate::{constants, from_hex, to_hex, Scalar};
use crate::{constants, hex, Scalar};

#[cfg(not(secp256k1_fuzz))]
macro_rules! hex {
($hex:expr) => {{
let mut result = vec![0; $hex.len() / 2];
from_hex($hex, &mut result).expect("valid hex string");
hex::from_hex($hex, &mut result).expect("valid hex string");
result
}};
}
Expand Down Expand Up @@ -1745,7 +1742,7 @@ mod test {

let mut buf = [0u8; constants::SECRET_KEY_SIZE * 2];
assert_eq!(
to_hex(&sk[..], &mut buf).unwrap(),
hex::to_hex(&sk[..], &mut buf).unwrap(),
"0100000000000000020000000000000003000000000000000400000000000000"
);
}
Expand Down Expand Up @@ -2422,7 +2419,7 @@ mod test {
let ctx = crate::Secp256k1::new();
let keypair = Keypair::new(&ctx, &mut rand::thread_rng());
let mut buf = [0_u8; constants::SECRET_KEY_SIZE * 2]; // Holds hex digits.
let s = to_hex(&keypair.secret_key().secret_bytes(), &mut buf).unwrap();
let s = hex::to_hex(&keypair.secret_key().secret_bytes(), &mut buf).unwrap();
let parsed_key = Keypair::from_str(s).unwrap();
assert_eq!(parsed_key, keypair);
}
Expand Down
72 changes: 11 additions & 61 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ mod macros;
#[macro_use]
mod secret;
mod context;
mod hex;
mod key;

pub mod constants;
Expand All @@ -172,7 +173,7 @@ mod serde_util;

use core::marker::PhantomData;
use core::ptr::NonNull;
use core::{fmt, mem, str};
use core::{fmt, mem};

#[cfg(feature = "global-context")]
pub use context::global::SECP256K1;
Expand Down Expand Up @@ -483,55 +484,6 @@ pub fn generate_keypair<R: rand::Rng + ?Sized>(rng: &mut R) -> (key::SecretKey,
SECP256K1.generate_keypair(rng)
}

/// 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)
}

/// Utility function used to encode hex into a target u8 buffer. Returns
/// a reference to the target buffer as an str. Returns an error if the target
/// buffer isn't big enough.
#[inline]
fn to_hex<'a>(src: &[u8], target: &'a mut [u8]) -> Result<&'a str, ()> {
let hex_len = src.len() * 2;
if target.len() < hex_len {
return Err(());
}
const HEX_TABLE: [u8; 16] = *b"0123456789abcdef";

let mut i = 0;
for &b in src {
target[i] = HEX_TABLE[usize::from(b >> 4)];
target[i + 1] = HEX_TABLE[usize::from(b & 0b00001111)];
i += 2;
}
let result = &target[..hex_len];
debug_assert!(str::from_utf8(result).is_ok());
return unsafe { Ok(str::from_utf8_unchecked(result)) };
}

#[cfg(feature = "rand")]
pub(crate) fn random_32_bytes<R: rand::Rng + ?Sized>(rng: &mut R) -> [u8; 32] {
let mut ret = [0u8; 32];
Expand All @@ -551,7 +503,7 @@ mod tests {
macro_rules! hex {
($hex:expr) => {{
let mut result = vec![0; $hex.len() / 2];
from_hex($hex, &mut result).expect("valid hex string");
hex::from_hex($hex, &mut result).expect("valid hex string");
result
}};
}
Expand Down Expand Up @@ -888,8 +840,6 @@ mod tests {
fn test_hex() {
use rand::RngCore;

use super::to_hex;

let mut rng = rand::thread_rng();
const AMOUNT: usize = 1024;
for i in 0..AMOUNT {
Expand All @@ -900,17 +850,17 @@ mod tests {
let src = &mut src_buf[0..i];
rng.fill_bytes(src);

let hex = to_hex(src, &mut hex_buf).unwrap();
assert_eq!(from_hex(hex, &mut result_buf).unwrap(), i);
let hex = hex::to_hex(src, &mut hex_buf).unwrap();
assert_eq!(hex::from_hex(hex, &mut result_buf).unwrap(), i);
assert_eq!(src, &result_buf[..i]);
}

assert!(to_hex(&[1; 2], &mut [0u8; 3]).is_err());
assert!(to_hex(&[1; 2], &mut [0u8; 4]).is_ok());
assert!(from_hex("deadbeaf", &mut [0u8; 3]).is_err());
assert!(from_hex("deadbeaf", &mut [0u8; 4]).is_ok());
assert!(from_hex("a", &mut [0u8; 4]).is_err());
assert!(from_hex("ag", &mut [0u8; 4]).is_err());
assert!(hex::to_hex(&[1; 2], &mut [0u8; 3]).is_err());
assert!(hex::to_hex(&[1; 2], &mut [0u8; 4]).is_ok());
assert!(hex::from_hex("deadbeaf", &mut [0u8; 3]).is_err());
assert!(hex::from_hex("deadbeaf", &mut [0u8; 4]).is_ok());
assert!(hex::from_hex("a", &mut [0u8; 4]).is_err());
assert!(hex::from_hex("ag", &mut [0u8; 4]).is_err());
}

#[test]
Expand Down
10 changes: 4 additions & 6 deletions src/schnorr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ use crate::ffi::{self, CPtr};
use crate::key::{Keypair, XOnlyPublicKey};
#[cfg(feature = "global-context")]
use crate::SECP256K1;
use crate::{
constants, from_hex, impl_array_newtype, Error, Message, Secp256k1, Signing, Verification,
};
use crate::{constants, hex, impl_array_newtype, Error, Message, Secp256k1, Signing, Verification};

/// Represents a schnorr signature.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
Expand Down Expand Up @@ -66,7 +64,7 @@ impl str::FromStr for Signature {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut res = [0u8; constants::SCHNORR_SIGNATURE_SIZE];
match from_hex(s, &mut res) {
match hex::from_hex(s, &mut res) {
Ok(constants::SCHNORR_SIGNATURE_SIZE) =>
Signature::from_slice(&res[0..constants::SCHNORR_SIGNATURE_SIZE]),
_ => Err(Error::InvalidSignature),
Expand Down Expand Up @@ -200,13 +198,13 @@ mod tests {
use super::*;
use crate::schnorr::{Keypair, Signature, XOnlyPublicKey};
use crate::Error::InvalidPublicKey;
use crate::{constants, from_hex, Message, Secp256k1, SecretKey};
use crate::{constants, hex, Message, Secp256k1, SecretKey};

#[cfg(all(not(secp256k1_fuzz), feature = "alloc"))]
macro_rules! hex_32 {
($hex:expr) => {{
let mut result = [0u8; 32];
from_hex($hex, &mut result).expect("valid hex string");
hex::from_hex($hex, &mut result).expect("valid hex string");
result
}};
}
Expand Down
Loading

0 comments on commit ec02f3f

Please sign in to comment.