Skip to content

Commit

Permalink
Implement flexible parsing of ECDSA keys
Browse files Browse the repository at this point in the history
TUF specification suggests to encode ECDSA keys using PEM. This is the
behaviour of the python reference implementation.

However, `go-tuf` encodes the keys as Hex numbers.

This commit makes the decoding of ECDSA keys more relaxed, the code will
try to decode them as PEM strings and it will resort to Hex when the
first decode fails.

Signed-off-by: Flavio Castelli <[email protected]>
Co-authored-by: Matthew James Briggs <[email protected]>
  • Loading branch information
flavio and webern committed Jan 26, 2022
1 parent 9a0897c commit eb1bdb8
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 3 deletions.
11 changes: 11 additions & 0 deletions tough/src/schema/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,15 @@ mod tests {
))
.is_err());
}

/// Ensure that we can deserialize a root.json file that has hex-encoded ECDSA keys. This uses
/// sigstore's root.json file taken from here:
/// https://sigstore-tuf-root.storage.googleapis.com/2.root.json
#[test]
fn ecdsa_hex_encoded_keys() {
assert!(serde_json::from_str::<Signed<Root>>(include_str!(
"../../tests/data/hex-encoded-ecdsa-sig-keys/root.json"
))
.is_ok());
}
}
26 changes: 26 additions & 0 deletions tough/src/schema/decoded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,32 @@ impl Encode for EcdsaPem {
}
}

/// [`Decode`]/[`Encode`] implementation for ECDSA public keys.
/// This is a flexible implementation, it will try to decode the key assuming
/// it is PEM encoded, if the decode fails it will then try to decode it
/// assuming it's Hex encoded.
/// The official TUF specification suggests ECDSA keys to be PEM encoded,
/// however the go-tuf implementation encodes them as Hex numbers.
/// This flexible decoder tries to cover both cases in a transparent way.
#[derive(Debug, Clone, Copy)]
pub struct EcdsaFlex {}

impl Decode for EcdsaFlex {
fn decode(s: &str) -> Result<Vec<u8>, Error> {
if s.starts_with("-----BEGIN ") {
EcdsaPem::decode(s)
} else {
Hex::decode(s)
}
}
}

impl Encode for EcdsaFlex {
fn encode(b: &[u8]) -> String {
EcdsaPem::encode(b)
}
}

// =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^=

impl<'de, T: Decode> Deserialize<'de> for Decoded<T> {
Expand Down
6 changes: 3 additions & 3 deletions tough/src/schema/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

//! Handles cryptographic keys and their serialization in TUF metadata files.
use crate::schema::decoded::{Decoded, EcdsaPem, Hex, RsaPem};
use crate::schema::decoded::{Decoded, EcdsaFlex, Hex, RsaPem};
use crate::schema::error::{self, Result};
use olpc_cjson::CanonicalFormatter;
use ring::digest::{digest, SHA256};
Expand Down Expand Up @@ -121,7 +121,7 @@ pub enum EcdsaScheme {
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct EcdsaKey {
/// The public key.
pub public: Decoded<EcdsaPem>,
pub public: Decoded<EcdsaFlex>,

/// Any additional fields read during deserialization; will not be used.
#[serde(flatten)]
Expand Down Expand Up @@ -204,7 +204,7 @@ impl FromStr for Key {
} else {
Err(KeyParseError(()))
}
} else if let Ok(public) = serde_plain::from_str::<Decoded<EcdsaPem>>(s) {
} else if let Ok(public) = serde_plain::from_str::<Decoded<EcdsaFlex>>(s) {
Ok(Key::Ecdsa {
keyval: EcdsaKey {
public,
Expand Down
144 changes: 144 additions & 0 deletions tough/tests/data/hex-encoded-ecdsa-sig-keys/root.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
{
"signatures": [
{
"keyid": "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97",
"sig": "3046022100d3ea59490b253beae0926c6fa63f54336dea1ed700555be9f27ff55cd347639c0221009157d1ba012cead81948a4ab777d355451d57f5c4a2d333fc68d2e3f358093c2"
},
{
"keyid": "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62",
"sig": "304502206eaef40564403ce572c6d062e0c9b0aab5e0223576133e081e1b495e8deb9efd02210080fd6f3464d759601b4afec596bbd5952f3a224cd06ed1cdfc3c399118752ba2"
},
{
"keyid": "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b",
"sig": "304502207baace02f56d8e6069f10b6ff098a26e7f53a7f9324ad62cffa0557bdeb9036c022100fb3032baaa090d0040c3f2fd872571c84479309b773208601d65948df87a9720"
},
{
"keyid": "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb",
"sig": "304402205180c01905505dd88acd7a2dad979dd75c979b3722513a7bdedac88c6ae8dbeb022056d1ddf7a192f0b1c2c90ff487de2fb3ec9f0c03f66ea937c78d3b6a493504ca"
},
{
"keyid": "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209",
"sig": "3046022100c8806d4647c514d80fd8f707d3369444c4fd1d0812a2d25f828e564c99790e3f022100bb51f12e862ef17a7d3da2ac103bebc5c7e792237006c4cafacd76267b249c2f"
}
],
"signed": {
"_type": "root",
"consistent_snapshot": false,
"expires": "2022-05-11T19:09:02.663975009Z",
"keys": {
"2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97": {
"keyid_hash_algorithms": [
"sha256",
"sha512"
],
"keytype": "ecdsa-sha2-nistp256",
"keyval": {
"public": "04cbc5cab2684160323c25cd06c3307178a6b1d1c9b949328453ae473c5ba7527e35b13f298b41633382241f3fd8526c262d43b45adee5c618fa0642c82b8a9803"
},
"scheme": "ecdsa-sha2-nistp256"
},
"b6710623a30c010738e64c5209d367df1c0a18cf90e6ab5292fb01680f83453d": {
"keyid_hash_algorithms": [
"sha256",
"sha512"
],
"keytype": "ecdsa-sha2-nistp256",
"keyval": {
"public": "04fa1a3e42f2300cd3c5487a61509348feb1e936920fef2f83b7cd5dbe7ba045f538725ab8f18a666e6233edb7e0db8766c8dc336633449c5e1bbe0c182b02df0b"
},
"scheme": "ecdsa-sha2-nistp256"
},
"bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62": {
"keyid_hash_algorithms": [
"sha256",
"sha512"
],
"keytype": "ecdsa-sha2-nistp256",
"keyval": {
"public": "04a71aacd835dc170ba6db3fa33a1a33dee751d4f8b0217b805b9bd3242921ee93672fdcfd840576c5bb0dc0ed815edf394c1ee48c2b5e02485e59bfc512f3adc7"
},
"scheme": "ecdsa-sha2-nistp256"
},
"eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b": {
"keyid_hash_algorithms": [
"sha256",
"sha512"
],
"keytype": "ecdsa-sha2-nistp256",
"keyval": {
"public": "04117b33dd265715bf23315e368faa499728db8d1f0a377070a1c7b1aba2cc21be6ab1628e42f2cdd7a35479f2dce07b303a8ba646c55569a8d2a504ba7e86e447"
},
"scheme": "ecdsa-sha2-nistp256"
},
"f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb": {
"keyid_hash_algorithms": [
"sha256",
"sha512"
],
"keytype": "ecdsa-sha2-nistp256",
"keyval": {
"public": "04cc1cd53a61c23e88cc54b488dfae168a257c34fac3e88811c55962b24cffbfecb724447999c54670e365883716302e49da57c79a33cd3e16f81fbc66f0bcdf48"
},
"scheme": "ecdsa-sha2-nistp256"
},
"f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209": {
"keyid_hash_algorithms": [
"sha256",
"sha512"
],
"keytype": "ecdsa-sha2-nistp256",
"keyval": {
"public": "048a78a44ac01099890d787e5e62afc29c8ccb69a70ec6549a6b04033b0a8acbfb42ab1ab9c713d225cdb52b858886cf46c8e90a7f3b9e6371882f370c259e1c5b"
},
"scheme": "ecdsa-sha2-nistp256"
},
"fc61191ba8a516fe386c7d6c97d918e1d241e1589729add09b122725b8c32451": {
"keyid_hash_algorithms": [
"sha256",
"sha512"
],
"keytype": "ecdsa-sha2-nistp256",
"keyval": {
"public": "044c7793ab74b9ddd713054e587b8d9c75c5f6025633d0fef7ca855ed5b8d5a474b23598fe33eb4a63630d526f74d4bdaec8adcb51993ed65652d651d7c49203eb"
},
"scheme": "ecdsa-sha2-nistp256"
}
},
"roles": {
"root": {
"keyids": [
"2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97",
"bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62",
"eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b",
"f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb",
"f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209"
],
"threshold": 3
},
"snapshot": {
"keyids": [
"fc61191ba8a516fe386c7d6c97d918e1d241e1589729add09b122725b8c32451"
],
"threshold": 1
},
"targets": {
"keyids": [
"2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97",
"bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62",
"eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b",
"f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb",
"f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209"
],
"threshold": 3
},
"timestamp": {
"keyids": [
"b6710623a30c010738e64c5209d367df1c0a18cf90e6ab5292fb01680f83453d"
],
"threshold": 1
}
},
"spec_version": "1.0",
"version": 2
}
}

0 comments on commit eb1bdb8

Please sign in to comment.