Skip to content

Commit 3ae48e2

Browse files
authored
hex encode/decode pushed to ironfish-zkp (#5561)
1 parent e9bec69 commit 3ae48e2

File tree

4 files changed

+137
-108
lines changed

4 files changed

+137
-108
lines changed

ironfish-rust/src/errors.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pub enum IronfishErrorKind {
3333
FailedXChaCha20Poly1305Decryption,
3434
FailedXChaCha20Poly1305Encryption,
3535
FailedHkdfExpansion,
36+
HexError,
3637
IllegalValue,
3738
InconsistentWitness,
3839
InvalidAssetIdentifier,
@@ -145,3 +146,9 @@ impl From<ironfish_frost::frost::Error> for IronfishError {
145146
IronfishError::new_with_source(IronfishErrorKind::FrostLibError, e)
146147
}
147148
}
149+
150+
impl From<ironfish_zkp::hex::HexError> for IronfishError {
151+
fn from(e: ironfish_zkp::hex::HexError) -> IronfishError {
152+
IronfishError::new_with_source(IronfishErrorKind::HexError, e)
153+
}
154+
}

ironfish-rust/src/serializing/mod.rs

Lines changed: 1 addition & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
pub mod aead;
66
pub mod fr;
77
use crate::errors::{IronfishError, IronfishErrorKind};
8+
pub use ironfish_zkp::hex::{bytes_to_hex, hex_to_bytes, hex_to_vec_bytes};
89

910
/// Helper functions to convert pairing parts to bytes
1011
///
@@ -16,8 +17,6 @@ use group::GroupEncoding;
1617

1718
use std::io;
1819

19-
const HEX_CHARS: &[u8; 16] = b"0123456789abcdef";
20-
2120
pub(crate) fn read_scalar<F: PrimeField, R: io::Read>(mut reader: R) -> Result<F, IronfishError> {
2221
let mut fr_repr = F::Repr::default();
2322
reader.read_exact(fr_repr.as_mut())?;
@@ -43,109 +42,3 @@ pub(crate) fn read_point_unchecked<G: GroupEncoding, R: io::Read>(
4342
Option::from(G::from_bytes_unchecked(&point_repr))
4443
.ok_or_else(|| IronfishError::new(IronfishErrorKind::InvalidData))
4544
}
46-
47-
/// Output the bytes as a hexadecimal String
48-
pub fn bytes_to_hex(bytes: &[u8]) -> String {
49-
let mut hex: Vec<u8> = vec![0; bytes.len() * 2];
50-
51-
for (i, b) in bytes.iter().enumerate() {
52-
hex[i * 2] = HEX_CHARS[(b >> 4) as usize];
53-
hex[i * 2 + 1] = HEX_CHARS[(b & 0x0f) as usize];
54-
}
55-
56-
unsafe { String::from_utf8_unchecked(hex) }
57-
}
58-
59-
/// Output the hexadecimal String as bytes
60-
pub fn hex_to_bytes<const SIZE: usize>(hex: &str) -> Result<[u8; SIZE], IronfishError> {
61-
if hex.len() != SIZE * 2 {
62-
return Err(IronfishError::new(IronfishErrorKind::InvalidData));
63-
}
64-
65-
let mut bytes = [0; SIZE];
66-
67-
let hex_iter = hex.as_bytes().chunks_exact(2);
68-
69-
for (i, hex) in hex_iter.enumerate() {
70-
bytes[i] = hex_to_u8(hex[0])? << 4 | hex_to_u8(hex[1])?;
71-
}
72-
73-
Ok(bytes)
74-
}
75-
76-
pub fn hex_to_vec_bytes(hex: &str) -> Result<Vec<u8>, IronfishError> {
77-
if hex.len() % 2 != 0 {
78-
return Err(IronfishError::new(IronfishErrorKind::InvalidData));
79-
}
80-
81-
let mut bytes = Vec::new();
82-
83-
let hex_iter = hex.as_bytes().chunks_exact(2);
84-
85-
for hex in hex_iter {
86-
bytes.push(hex_to_u8(hex[0])? << 4 | hex_to_u8(hex[1])?);
87-
}
88-
89-
Ok(bytes)
90-
}
91-
92-
#[inline]
93-
fn hex_to_u8(char: u8) -> Result<u8, IronfishError> {
94-
match char {
95-
b'0'..=b'9' => Ok(char - b'0'),
96-
b'a'..=b'f' => Ok(char - b'a' + 10),
97-
b'A'..=b'F' => Ok(char - b'A' + 10),
98-
_ => Err(IronfishError::new(IronfishErrorKind::InvalidData)),
99-
}
100-
}
101-
102-
#[cfg(test)]
103-
mod test {
104-
use crate::serializing::{bytes_to_hex, hex_to_bytes, hex_to_vec_bytes};
105-
106-
#[test]
107-
fn test_hex_to_vec_bytes_valid() {
108-
let hex = "A1B2C3";
109-
let expected_bytes = vec![161, 178, 195];
110-
111-
let result = hex_to_vec_bytes(hex).expect("valid hex");
112-
113-
assert_eq!(result, expected_bytes);
114-
}
115-
116-
#[test]
117-
fn test_hex_to_vec_bytes_invalid_char() {
118-
let hex = "A1B2G3";
119-
hex_to_vec_bytes(hex).expect_err("invalid hex should throw an error");
120-
}
121-
122-
#[test]
123-
fn test_hex_to_vec_bytes_invalid_hex_with_odd_length() {
124-
let hex = "A1B2C";
125-
hex_to_vec_bytes(hex).expect_err("invalid hex should throw an error");
126-
}
127-
128-
#[test]
129-
fn hex_serde() {
130-
const HEX_STRING: &str = "68656C6C6F20776F726C6420616E64207374756666";
131-
const HEX_LOWER: &str = "68656c6c6f20776f726c6420616e64207374756666";
132-
const BYTE_LENGTH: usize = HEX_STRING.len() / 2;
133-
// Same as above with the last character removed, which makes the hex
134-
// invalid as the length of a hex string must be divisible by 2
135-
const INVALID_HEX: &str = "68656C6C6F20776F726C6420616E6420737475666";
136-
137-
hex_to_bytes::<BYTE_LENGTH>(INVALID_HEX).expect_err("invalid hex should throw an error");
138-
139-
let bytes: [u8; BYTE_LENGTH] = hex_to_bytes(HEX_STRING).expect("converts hex to bytes");
140-
let lower_bytes: [u8; BYTE_LENGTH] =
141-
hex_to_bytes(HEX_STRING).expect("converts hex to bytes");
142-
143-
assert_eq!(bytes, lower_bytes);
144-
145-
let hex = bytes_to_hex(&bytes);
146-
let lower_hex = bytes_to_hex(&lower_bytes);
147-
148-
assert_eq!(HEX_LOWER, hex);
149-
assert_eq!(HEX_LOWER, lower_hex);
150-
}
151-
}

ironfish-zkp/src/hex.rs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
use std::fmt;
2+
3+
const HEX_CHARS: &[u8; 16] = b"0123456789abcdef";
4+
5+
#[derive(Debug)]
6+
pub enum HexError {
7+
ByteLengthMismatch,
8+
InvalidCharacter,
9+
OddStringLength,
10+
}
11+
12+
impl fmt::Display for HexError {
13+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14+
match self {
15+
HexError::ByteLengthMismatch => write!(f, "Byte length mismatch"),
16+
HexError::InvalidCharacter => write!(f, "Invalid character"),
17+
HexError::OddStringLength => write!(f, "Odd string length"),
18+
}
19+
}
20+
}
21+
22+
impl std::error::Error for HexError {}
23+
24+
/// Output the bytes as a hexadecimal String
25+
pub fn bytes_to_hex(bytes: &[u8]) -> String {
26+
let mut hex: Vec<u8> = vec![0; bytes.len() * 2];
27+
28+
for (i, b) in bytes.iter().enumerate() {
29+
hex[i * 2] = HEX_CHARS[(b >> 4) as usize];
30+
hex[i * 2 + 1] = HEX_CHARS[(b & 0x0f) as usize];
31+
}
32+
33+
unsafe { String::from_utf8_unchecked(hex) }
34+
}
35+
36+
/// Output the hexadecimal String as bytes
37+
pub fn hex_to_bytes<const SIZE: usize>(hex: &str) -> Result<[u8; SIZE], HexError> {
38+
if hex.len() != SIZE * 2 {
39+
return Err(HexError::ByteLengthMismatch);
40+
}
41+
42+
let mut bytes = [0; SIZE];
43+
44+
let hex_iter = hex.as_bytes().chunks_exact(2);
45+
46+
for (i, hex) in hex_iter.enumerate() {
47+
bytes[i] = hex_to_u8(hex[0])? << 4 | hex_to_u8(hex[1])?;
48+
}
49+
50+
Ok(bytes)
51+
}
52+
53+
pub fn hex_to_vec_bytes(hex: &str) -> Result<Vec<u8>, HexError> {
54+
if hex.len() % 2 != 0 {
55+
return Err(HexError::OddStringLength);
56+
}
57+
58+
let mut bytes = Vec::new();
59+
60+
let hex_iter = hex.as_bytes().chunks_exact(2);
61+
62+
for hex in hex_iter {
63+
bytes.push(hex_to_u8(hex[0])? << 4 | hex_to_u8(hex[1])?);
64+
}
65+
66+
Ok(bytes)
67+
}
68+
69+
#[inline]
70+
fn hex_to_u8(char: u8) -> Result<u8, HexError> {
71+
match char {
72+
b'0'..=b'9' => Ok(char - b'0'),
73+
b'a'..=b'f' => Ok(char - b'a' + 10),
74+
b'A'..=b'F' => Ok(char - b'A' + 10),
75+
_ => Err(HexError::InvalidCharacter),
76+
}
77+
}
78+
79+
#[cfg(test)]
80+
mod test {
81+
use crate::hex::{bytes_to_hex, hex_to_bytes, hex_to_vec_bytes};
82+
83+
#[test]
84+
fn test_hex_to_vec_bytes_valid() {
85+
let hex = "A1B2C3";
86+
let expected_bytes = vec![161, 178, 195];
87+
88+
let result = hex_to_vec_bytes(hex).expect("valid hex");
89+
90+
assert_eq!(result, expected_bytes);
91+
}
92+
93+
#[test]
94+
fn test_hex_to_vec_bytes_invalid_char() {
95+
let hex = "A1B2G3";
96+
hex_to_vec_bytes(hex).expect_err("invalid hex should throw an error");
97+
}
98+
99+
#[test]
100+
fn test_hex_to_vec_bytes_invalid_hex_with_odd_length() {
101+
let hex = "A1B2C";
102+
hex_to_vec_bytes(hex).expect_err("invalid hex should throw an error");
103+
}
104+
105+
#[test]
106+
fn hex_serde() {
107+
const HEX_STRING: &str = "68656C6C6F20776F726C6420616E64207374756666";
108+
const HEX_LOWER: &str = "68656c6c6f20776f726c6420616e64207374756666";
109+
const BYTE_LENGTH: usize = HEX_STRING.len() / 2;
110+
// Same as above with the last character removed, which makes the hex
111+
// invalid as the length of a hex string must be divisible by 2
112+
const INVALID_HEX: &str = "68656C6C6F20776F726C6420616E6420737475666";
113+
114+
hex_to_bytes::<BYTE_LENGTH>(INVALID_HEX).expect_err("invalid hex should throw an error");
115+
116+
let bytes: [u8; BYTE_LENGTH] = hex_to_bytes(HEX_STRING).expect("converts hex to bytes");
117+
let lower_bytes: [u8; BYTE_LENGTH] =
118+
hex_to_bytes(HEX_STRING).expect("converts hex to bytes");
119+
120+
assert_eq!(bytes, lower_bytes);
121+
122+
let hex = bytes_to_hex(&bytes);
123+
let lower_hex = bytes_to_hex(&lower_bytes);
124+
125+
assert_eq!(HEX_LOWER, hex);
126+
assert_eq!(HEX_LOWER, lower_hex);
127+
}
128+
}

ironfish-zkp/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod circuits;
22
pub mod constants;
3+
pub mod hex;
34
pub mod primitives;
45
pub mod util;
56

0 commit comments

Comments
 (0)