Skip to content

Commit

Permalink
hex encode signature and file hash values
Browse files Browse the repository at this point in the history
According to theupdateframework/specification#42,
the signature and file hash values should be hex encoded.

Change-Id: I14710a062fae3adfc4bc6b86fbbf533ce1b97070
  • Loading branch information
erickt committed Aug 1, 2019
1 parent bfdbeb2 commit 0069f17
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 59 deletions.
68 changes: 16 additions & 52 deletions 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};
Expand Down Expand Up @@ -207,8 +207,8 @@ pub enum SignatureScheme {
}

/// Wrapper type for the value of a cryptographic signature.
#[derive(Clone, PartialEq)]
pub struct SignatureValue(Vec<u8>);
#[derive(Clone, PartialEq, Serialize, Deserialize)]
pub struct SignatureValue(#[serde(with = "crate::format_hex")] Vec<u8>);

impl SignatureValue {
/// Create a new `SignatureValue` from the given bytes.
Expand All @@ -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<Self> {
Ok(SignatureValue(BASE64URL.decode(string.as_bytes())?))
pub fn from_hex(string: &str) -> Result<Self> {
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<S>(&self, ser: S) -> ::std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
BASE64URL.encode(&self.0).serialize(ser)
}
}

impl<'de> Deserialize<'de> for SignatureValue {
fn deserialize<D: Deserializer<'de>>(de: D) -> ::std::result::Result<Self, D::Error> {
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))
}
}

Expand Down Expand Up @@ -712,8 +694,8 @@ pub enum HashAlgorithm {
}

/// Wrapper for the value of a hash digest.
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct HashValue(Vec<u8>);
#[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct HashValue(#[serde(with = "crate::format_hex")] Vec<u8>);

impl HashValue {
/// Create a new `HashValue` from the given digest bytes.
Expand All @@ -727,34 +709,15 @@ impl HashValue {
}
}

impl Serialize for HashValue {
fn serialize<S>(&self, ser: S) -> ::std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
BASE64URL.encode(&self.0).serialize(ser)
}
}

impl<'de> Deserialize<'de> for HashValue {
fn deserialize<D: Deserializer<'de>>(de: D) -> ::std::result::Result<Self, D::Error> {
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))
}
}

Expand Down Expand Up @@ -913,10 +876,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);
}
Expand Down Expand Up @@ -959,8 +922,9 @@ mod test {
let encoded = serde_json::to_value(&sig).unwrap();
let jsn = json!({
"keyid": "qfrfBrkB4lBBSDEBlZgaTGS_SrE6UfmON9kP4i3dJFY=",
"value": "_k0Tsqc8Azod5_UQeyBfx7oOFWbLlbkjScrmqkU4lWATv-D3v5d8sHK7Z\
eh4K18zoFc_54gWKZoBfKW6VZ45DA==",
"value": "fe4d13b2a73c033a1de7f5107b205fc7ba0e1566cb95b92349cae6aa4\
538956013bfe0f7bf977cb072bb65e8782b5f33a0573fe78816299a017ca5ba\
559e390c",
}
);
assert_eq!(encoded, jsn);
Expand Down
18 changes: 18 additions & 0 deletions 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<S>(value: &Vec<u8>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&HEXLOWER.encode(value))
}

pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
HEXLOWER.decode(s.as_bytes()).map_err(serde::de::Error::custom)
}
4 changes: 2 additions & 2 deletions src/interchange/mod.rs
Expand Up @@ -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:
///
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Expand Up @@ -119,6 +119,7 @@ pub mod metadata;
pub mod repository;
pub mod tuf;

mod format_hex;
mod shims;
mod util;

Expand Down
13 changes: 8 additions & 5 deletions src/metadata.rs
Expand Up @@ -857,7 +857,7 @@ impl MetadataPath {
/// ["foo".to_string(), "1.bar.json".to_string()]);
/// assert_eq!(path.components::<Json>(
/// &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<D>(&self, version: &MetadataVersion) -> Vec<String>
where
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -2046,7 +2047,8 @@ mod test {
"foo": {
"length": 3,
"hashes": {
"sha256": "LCa0a2j_xo_5m0U8HTBBNBNCLXBkg7-g-YpeiGJm564=",
"sha256": "2c26b46b68ffc68ff99b453c1d30413413422d706483\
bfa0f98a5e886266e7ae",
},
},
},
Expand Down Expand Up @@ -2136,8 +2138,9 @@ mod test {
"signatures": [
{
"keyid": "qfrfBrkB4lBBSDEBlZgaTGS_SrE6UfmON9kP4i3dJFY=",
"value": "5zv0RA1_apWwMHNRgcuhkNwnz1iNj8aPkZrA3bruehf5ncKiPekLHGVJWWodPEIESj\
9k9FBLI4TK422Y7RR2AQ==",
"value": "e73bf4440d7f6a95b030735181cba190dc27cf588d8fc68f9\
19ac0ddbaee7a17f99dc2a23de90b1c6549596a1d3c42044a3f64f4\
504b2384cae36d98ed147601",
}
],
"signed": {
Expand Down

0 comments on commit 0069f17

Please sign in to comment.