diff --git a/src/crypto.rs b/src/crypto.rs index d3d90f64..2042f6dc 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -1,6 +1,6 @@ //! Cryptographic structures and functions. -use data_encoding::BASE64URL; +use data_encoding::{BASE64URL, HEXLOWER}; use derp::{self, Der, Tag}; use ring; use ring::digest::{self, SHA256, SHA512}; @@ -207,8 +207,8 @@ pub enum SignatureScheme { } /// Wrapper type for the value of a cryptographic signature. -#[derive(Clone, PartialEq)] -pub struct SignatureValue(Vec); +#[derive(Clone, PartialEq, Serialize, Deserialize)] +pub struct SignatureValue(#[serde(with = "crate::format_hex")] Vec); impl SignatureValue { /// Create a new `SignatureValue` from the given bytes. @@ -218,35 +218,17 @@ impl SignatureValue { SignatureValue(bytes) } - /// Create a new `SignatureValue` from the given base64url string. + /// Create a new `SignatureValue` from the given hex string. /// /// Note: It is unlikely that you ever want to do this manually. - pub fn from_string(string: &str) -> Result { - Ok(SignatureValue(BASE64URL.decode(string.as_bytes())?)) + pub fn from_hex(string: &str) -> Result { + Ok(SignatureValue(HEXLOWER.decode(string.as_bytes())?)) } } impl Debug for SignatureValue { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "SignatureValue {{ \"{}\" }}", BASE64URL.encode(&self.0)) - } -} - -impl Serialize for SignatureValue { - fn serialize(&self, ser: S) -> ::std::result::Result - where - S: Serializer, - { - BASE64URL.encode(&self.0).serialize(ser) - } -} - -impl<'de> Deserialize<'de> for SignatureValue { - fn deserialize>(de: D) -> ::std::result::Result { - let string: String = Deserialize::deserialize(de)?; - SignatureValue::from_string(&string).map_err(|e| { - DeserializeError::custom(format!("Signature value was not valid base64url: {:?}", e)) - }) + write!(f, "SignatureValue {{ \"{}\" }}", HEXLOWER.encode(&self.0)) } } @@ -713,8 +695,8 @@ pub enum HashAlgorithm { } /// Wrapper for the value of a hash digest. -#[derive(Clone, Eq, PartialEq, Hash)] -pub struct HashValue(Vec); +#[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] +pub struct HashValue(#[serde(with = "crate::format_hex")] Vec); impl HashValue { /// Create a new `HashValue` from the given digest bytes. @@ -728,34 +710,15 @@ impl HashValue { } } -impl Serialize for HashValue { - fn serialize(&self, ser: S) -> ::std::result::Result - where - S: Serializer, - { - BASE64URL.encode(&self.0).serialize(ser) - } -} - -impl<'de> Deserialize<'de> for HashValue { - fn deserialize>(de: D) -> ::std::result::Result { - let s: String = Deserialize::deserialize(de)?; - let bytes = BASE64URL - .decode(s.as_bytes()) - .map_err(|e| DeserializeError::custom(format!("Base64: {:?}", e)))?; - Ok(HashValue(bytes)) - } -} - impl Debug for HashValue { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "HashValue {{ \"{}\" }}", BASE64URL.encode(&self.0)) + write!(f, "HashValue {{ \"{}\" }}", HEXLOWER.encode(&self.0)) } } impl Display for HashValue { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", BASE64URL.encode(&self.0)) + write!(f, "{}", HEXLOWER.encode(&self.0)) } } @@ -914,10 +877,10 @@ mod test { #[test] fn serde_signature_value() { - let s = "T5vfRrM1iHpgzGwAHe7MbJH_7r4chkOAphV3OPCCv0I="; + let s = "4750eaf6878740780d6f97b12dbad079fb012bec88c78de2c380add56d3f51db"; let jsn = json!(s); let parsed: SignatureValue = serde_json::from_str(&format!("\"{}\"", s)).unwrap(); - assert_eq!(parsed, SignatureValue::from_string(s).unwrap()); + assert_eq!(parsed, SignatureValue::from_hex(s).unwrap()); let encoded = serde_json::to_value(&parsed).unwrap(); assert_eq!(encoded, jsn); } @@ -960,8 +923,9 @@ mod test { let encoded = serde_json::to_value(&sig).unwrap(); let jsn = json!({ "keyid": "qfrfBrkB4lBBSDEBlZgaTGS_SrE6UfmON9kP4i3dJFY=", - "sig": "_k0Tsqc8Azod5_UQeyBfx7oOFWbLlbkjScrmqkU4lWATv-D3v5d8sHK7Z\ - eh4K18zoFc_54gWKZoBfKW6VZ45DA==", + "sig": "fe4d13b2a73c033a1de7f5107b205fc7ba0e1566cb95b92349cae6aa453\ + 8956013bfe0f7bf977cb072bb65e8782b5f33a0573fe78816299a017ca5ba55\ + 9e390c", } ); assert_eq!(encoded, jsn); diff --git a/src/format_hex.rs b/src/format_hex.rs new file mode 100644 index 00000000..cd32edd0 --- /dev/null +++ b/src/format_hex.rs @@ -0,0 +1,18 @@ +use data_encoding::HEXLOWER; +use serde::{self, Deserialize, Deserializer, Serializer}; +use std::result::Result; + +pub fn serialize(value: &Vec, serializer: S) -> Result +where + S: Serializer, +{ + serializer.serialize_str(&HEXLOWER.encode(value)) +} + +pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let s = String::deserialize(deserializer)?; + HEXLOWER.decode(s.as_bytes()).map_err(serde::de::Error::custom) +} diff --git a/src/interchange/mod.rs b/src/interchange/mod.rs index ffdc8001..4877c2d6 100644 --- a/src/interchange/mod.rs +++ b/src/interchange/mod.rs @@ -84,9 +84,9 @@ pub trait DataInterchange: Debug + PartialEq + Clone { /// /// `SCHEME` is a string (either `ed25519`, `rsassa-pss-sha256`, or `rsassa-pss-sha512` /// -/// `HASH_VALUE` is a base64url encoded hash value. +/// `HASH_VALUE` is a hex encoded hash value. /// -/// `SIG_VALUE` is a base64url encoded signature value. +/// `SIG_VALUE` is a hex encoded signature value. /// /// `METADATA_DESCRIPTION` is the following: /// diff --git a/src/lib.rs b/src/lib.rs index 963d22a8..2c65ca46 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,6 +119,7 @@ pub mod metadata; pub mod repository; pub mod tuf; +mod format_hex; mod shims; mod util; diff --git a/src/metadata.rs b/src/metadata.rs index e52890c4..15896f57 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -857,7 +857,7 @@ impl MetadataPath { /// ["foo".to_string(), "1.bar.json".to_string()]); /// assert_eq!(path.components::( /// &MetadataVersion::Hash(HashValue::new(vec![0x69, 0xb7, 0x1d]))), - /// ["foo".to_string(), "abcd.bar.json".to_string()]); + /// ["foo".to_string(), "69b71d.bar.json".to_string()]); /// ``` pub fn components(&self, version: &MetadataVersion) -> Vec where @@ -1835,7 +1835,8 @@ mod test { let jsn = json!({ "length": 30, "hashes": { - "sha256": "_F10XHEryG6poxJk2sDJVu61OFf2d-7QWCm7cQE8rhg=", + "sha256": "fc5d745c712bc86ea9a31264dac0c956eeb53857f677eed05829\ + bb71013cae18", }, }); let parsed_str: TargetDescription = serde_json::from_str(&jsn_str).unwrap(); @@ -2046,7 +2047,8 @@ mod test { "foo": { "length": 3, "hashes": { - "sha256": "LCa0a2j_xo_5m0U8HTBBNBNCLXBkg7-g-YpeiGJm564=", + "sha256": "2c26b46b68ffc68ff99b453c1d30413413422d706483\ + bfa0f98a5e886266e7ae", }, }, }, @@ -2136,8 +2138,9 @@ mod test { "signatures": [ { "keyid": "qfrfBrkB4lBBSDEBlZgaTGS_SrE6UfmON9kP4i3dJFY=", - "sig": "LpLKsO8-X6-u8KN2130IEWMr4lcp7nt-fEHErwdZlQQGFB0Vmz6MUDNlNZJxSBgBU9\ - LZ2vyolgyfyRjGgDDIAw==", + "sig": "2e92cab0ef3e5fafaef0a376d77d0811632be25729ee7b7e7c\ + 41c4af0759950406141d159b3e8c50336535927148180153d2d9da\ + fca8960c9fc918c68030c803", } ], "signed": {