From 12a93c864ddb940fa00b24d2e3fb1777f75d70ae Mon Sep 17 00:00:00 2001 From: Jannik Peters Date: Wed, 11 Sep 2024 14:45:51 +0200 Subject: [PATCH 1/6] Add private key file parsing and generation --- src/lib.rs | 1 + src/utils/mod.rs | 1 + src/utils/private_key_file_parser.rs | 281 +++++++++++++++++++++++++++ 3 files changed, 283 insertions(+) create mode 100644 src/utils/mod.rs create mode 100644 src/utils/private_key_file_parser.rs diff --git a/src/lib.rs b/src/lib.rs index de6645c8..c33cca70 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,3 +3,4 @@ pub use self::args::Args; pub mod args; pub mod commands; pub mod error; +pub mod utils; diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 00000000..d79c5c7c --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1 @@ +pub mod private_key_file_parser; diff --git a/src/utils/private_key_file_parser.rs b/src/utils/private_key_file_parser.rs new file mode 100644 index 00000000..262a20a3 --- /dev/null +++ b/src/utils/private_key_file_parser.rs @@ -0,0 +1,281 @@ +use crate::error::Error; +use domain::base::iana::SecAlg; +use domain::utils::base64; +use std::slice::Iter; + +macro_rules! parse_next_line { + ($lines:ident, $match_text:expr) => { + if let Some(line) = $lines.next() { + if !line.starts_with($match_text) { + return Err(Error::from(concat!( + "expected line starting with ", + $match_text + ))); + } + + if let Some((_, m)) = line.split_once(' ') { + m + } else { + return Err(Error::from("malformed line")); + } + } else { + return Err(Error::from("expected more private key data")); + } + }; +} + +#[derive(Default, Debug)] +pub struct RsaKeyData { + algorithm_id: u8, + modulus: Vec, + public_exponent: Vec, + private_exponent: Vec, + prime1: Vec, + prime2: Vec, + exponent1: Vec, + exponent2: Vec, + coefficient: Vec, +} + +impl RsaKeyData { + fn parse_lines(algorithm: u8, lines: &mut Iter<&str>) -> Result { + let modulus = parse_next_line!(lines, "Modulus: "); + let public_exponent = parse_next_line!(lines, "PublicExponent: "); + let private_exponent = parse_next_line!(lines, "PrivateExponent: "); + let prime1 = parse_next_line!(lines, "Prime1: "); + let prime2 = parse_next_line!(lines, "Prime2: "); + let exponent1 = parse_next_line!(lines, "Exponent1: "); + let exponent2 = parse_next_line!(lines, "Exponent2: "); + let coefficient = parse_next_line!(lines, "Coefficient: "); + + Ok(RsaKeyData { + algorithm_id: algorithm, + modulus: base64::decode(modulus).expect("failed decoding base64 data"), + public_exponent: base64::decode(public_exponent).expect("failed decoding base64 data"), + private_exponent: base64::decode(private_exponent) + .expect("failed decoding base64 data"), + prime1: base64::decode(prime1).expect("failed decoding base64 data"), + prime2: base64::decode(prime2).expect("failed decoding base64 data"), + exponent1: base64::decode(exponent1).expect("failed decoding base64 data"), + exponent2: base64::decode(exponent2).expect("failed decoding base64 data"), + coefficient: base64::decode(coefficient).expect("failed decoding base64 data"), + }) + } +} + +#[derive(Default, Debug)] +pub struct EcKeyData { + algorithm_id: u8, + private_key: Vec, +} + +impl EcKeyData { + fn parse_lines(algorithm: u8, lines: &mut Iter<&str>) -> Result { + let private_key = parse_next_line!(lines, "PrivateKey: "); + + Ok(EcKeyData { + algorithm_id: algorithm, + private_key: base64::decode(private_key).expect("failed decoding base64 data"), + }) + } +} + +#[derive(Default, Debug)] +pub struct HmacKeyData { + algorithm_id: u8, + key: Vec, +} + +impl HmacKeyData { + fn parse_lines(algorithm: u8, lines: &mut Iter<&str>) -> Result { + let key = parse_next_line!(lines, "Key: "); + + Ok(HmacKeyData { + algorithm_id: algorithm, + key: base64::decode(key).expect("failed decoding base64 data"), + }) + } +} + +#[derive(Default, Debug)] +pub struct DsaKeyData { + algorithm_id: u8, + prime: Vec, + subprime: Vec, + base: Vec, + private_value: Vec, + public_value: Vec, +} + +impl DsaKeyData { + fn parse_lines(algorithm: u8, lines: &mut Iter<&str>) -> Result { + let prime = parse_next_line!(lines, "Prime(p): "); + let subprime = parse_next_line!(lines, "Subprime(q): "); + let base = parse_next_line!(lines, "Base(g): "); + let private_value = parse_next_line!(lines, "Private_value(x): "); + let public_value = parse_next_line!(lines, "Public_value(y): "); + + Ok(DsaKeyData { + algorithm_id: algorithm, + prime: base64::decode(prime).expect("failed decoding base64 data"), + subprime: base64::decode(subprime).expect("failed decoding base64 data"), + base: base64::decode(base).expect("failed decoding base64 data"), + private_value: base64::decode(private_value).expect("failed decoding base64 data"), + public_value: base64::decode(public_value).expect("failed decoding base64 data"), + }) + } +} + +#[derive(Debug)] +pub enum KeyData { + Rsa(RsaKeyData), + Ec(EcKeyData), + Hmac(HmacKeyData), + Dsa(DsaKeyData), +} + +pub fn gen_private_key_file_text(key: KeyData) -> Result { + match key { + KeyData::Rsa(rsa) => Ok(format!( + "Private-key-format: v1.2 +Algorithm: {alg_id} ({alg_name}) +Modulus: {modulus} +PublicExponent: {pub_exp} +PrivateExponent: {priv_exp} +Prime1: {prime1} +Prime2: {prime2} +Exponent1: {exp1} +Exponent2: {exp2} +Coefficient: {coeff}\n", + alg_id = rsa.algorithm_id, + alg_name = SecAlg::from_int(rsa.algorithm_id), + modulus = base64::encode_string(&rsa.modulus), + pub_exp = base64::encode_string(&rsa.public_exponent), + priv_exp = base64::encode_string(&rsa.private_exponent), + prime1 = base64::encode_string(&rsa.prime1), + prime2 = base64::encode_string(&rsa.prime2), + exp1 = base64::encode_string(&rsa.exponent1), + exp2 = base64::encode_string(&rsa.exponent2), + coeff = base64::encode_string(&rsa.coefficient), + )), + KeyData::Ec(ec) => Ok(format!( + "Private-key-format: v1.2 +Algorithm: {alg_id} ({alg_name}) +PrivateKey: {key}\n", + alg_id = ec.algorithm_id, + alg_name = SecAlg::from_int(ec.algorithm_id), + key = base64::encode_string(&ec.private_key), + )), + KeyData::Hmac(hmac) => Ok(format!( + "Private-key-format: v1.2 +Algorithm: {alg_id} ({alg_name}) +Key: {key}\n", + alg_id = hmac.algorithm_id, + alg_name = SecAlg::from_int(hmac.algorithm_id), + key = base64::encode_string(&hmac.key), + )), + KeyData::Dsa(dsa) => Ok(format!( + "Private-key-format: v1.2 +Algorithm: {alg_id} ({alg_name}) +Prime(p): {p} +Subprime(q): {q} +Base(g): {g} +Private_value(x): {x} +Public_value(y): {y}\n", + alg_id = dsa.algorithm_id, + alg_name = SecAlg::from_int(dsa.algorithm_id), + p = base64::encode_string(&dsa.prime), + q = base64::encode_string(&dsa.subprime), + g = base64::encode_string(&dsa.base), + x = base64::encode_string(&dsa.private_value), + y = base64::encode_string(&dsa.public_value), + )), + } +} + +pub fn parse_private_key_file(mut lines: Iter<&str>) -> Result { + // File format in ABNF (no data validity encoded here) + + // KEYFILE = HEADER ALGORITHM KEY_SPECIFIC + // HEADER = %s"Private-key-format: v" DIGIT "." DIGIT LF + // ALGORITHM = %s"Algorithm: " 1*3DIGIT *1( " (" ALG_NAME ")" ) LF + // KEY_SPECIFIC = RSA / EDDSA / HMAC / DSA + // + // RSA = MODULUS PUBLIC_EXPONENT PRIVATE_EXPONENT PRIME1 PRIME2 EXPONENT1 EXPONENT2 COEFFICIENT + // DSA = PRIME SUBPRIME BASE PRIVATE_VALUE PUBLIC_VALUE + // EDDSA = %s"PrivateKey: " BASE64_DATA + // HMAC = %s"Key: " BASE64_DATA + // + // PRIME = %s"Prime(p): " BASE64_DATA LF + // SUBPRIME = %s"Subprime(q): " BASE64_DATA LF + // BASE = %s"Base(g): " BASE64_DATA LF + // PRIVATE_VALUE = %s"Private_value(x): " BASE64_DATA LF + // PUBLIC_VALUE = %s"Public_value(y): " BASE64_DATA LF + // + // MODULUS = %s"Modulus: " BASE64_DATA LF + // PUBLIC_EXPONENT = %s"PublicExponent: " BASE64_DATA LF + // PRIVATE_EXPONENT = %s"PrivateExponent: " BASE64_DATA LF + // PRIME1 = %s"Prim1: " BASE64_DATA LF + // PRIME2 = %s"Prime2: " BASE64_DATA LF + // EXPONENT1 = %s"Exponent1: " BASE64_DATA LF + // EXPONENT2 = %s"Exponent2: " BASE64_DATA LF + // COEFFICIENT = %s"Coefficient: " BASE64_DATA LF + // + // BASE64_DATA = *(ALPHA / DIGIT / "/" / "+") + + let mut algorithm = 0; + + if let Some(line) = lines.next() { + if !line.starts_with("Private-key-format: v1.") { + return Err(Error::from( + "expected private key format version (v1.x) specifier", + )); + } + }; + + if let Some(line) = lines.next() { + if !line.starts_with("Algorithm: ") { + return Err(Error::from("expected algorithm specifier")); + } + + // "Algorithm: 123 (NAMEXYZ)" + let mut parts = line.split(' '); + parts.next(); // "Algorithm:" + if let Some(alg) = parts.next() { + algorithm = alg.parse()?; + } else { + return Err(Error::from("expected algorithm identifier number")); + } + }; + + match SecAlg::from_int(algorithm) { + SecAlg::RSAMD5 + | SecAlg::RSASHA1 + | SecAlg::RSASHA1_NSEC3_SHA1 + | SecAlg::RSASHA256 + | SecAlg::RSASHA512 => Ok(KeyData::Rsa(RsaKeyData::parse_lines( + algorithm, &mut lines, + )?)), + SecAlg::DSA | SecAlg::DSA_NSEC3_SHA1 => Ok(KeyData::Dsa(DsaKeyData::parse_lines( + algorithm, &mut lines, + )?)), + SecAlg::ECDSAP256SHA256 | SecAlg::ECDSAP384SHA384 | SecAlg::ED25519 | SecAlg::ED448 => { + Ok(KeyData::Ec(EcKeyData::parse_lines(algorithm, &mut lines)?)) + } + + /* might be a hmac algorithm */ + _ => match algorithm { + 157 /* HMAC_MD5 */ + | 158 /* HMAC_SHA1 */ + | 159 /* HMAC_SHA256 */ + | 161 /* HMAC_SHA1 */ + | 162 /* HMAC_SHA224 */ + | 163 /* HMAC_SHA256 */ + | 164 /* HMAC_SHA384 */ + | 165 /* HMAC_SHA512 */ + => Ok(KeyData::Hmac(HmacKeyData::parse_lines(algorithm, &mut lines)?)), + /* unknown algorithm number */ + _ => Err(Error::from("unsupported algorithm")), + }, + } +} From 2cd8ab8bb7151f2d874606d814828385c8da4e8b Mon Sep 17 00:00:00 2001 From: Jannik Peters Date: Wed, 11 Sep 2024 16:11:52 +0200 Subject: [PATCH 2/6] Print human readable hmac algorithm name --- src/utils/private_key_file_parser.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/utils/private_key_file_parser.rs b/src/utils/private_key_file_parser.rs index 262a20a3..9b4b7db1 100644 --- a/src/utils/private_key_file_parser.rs +++ b/src/utils/private_key_file_parser.rs @@ -171,7 +171,17 @@ PrivateKey: {key}\n", Algorithm: {alg_id} ({alg_name}) Key: {key}\n", alg_id = hmac.algorithm_id, - alg_name = SecAlg::from_int(hmac.algorithm_id), + alg_name = match hmac.algorithm_id { + 157 => "HMAC_MD5", + 158 => "HMAC_SHA1", + 159 => "HMAC_SHA256", + 161 => "HMAC_SHA1", + 162 => "HMAC_SHA224", + 163 => "HMAC_SHA256", + 164 => "HMAC_SHA384", + 165 => "HMAC_SHA512", + _ => return Err(Error::from("unknown hmac algorithm")) + }, key = base64::encode_string(&hmac.key), )), KeyData::Dsa(dsa) => Ok(format!( From 3a71fb9ecf23acba16a78661bc2322c4579e255b Mon Sep 17 00:00:00 2001 From: Jannik Peters Date: Tue, 17 Sep 2024 16:49:09 +0200 Subject: [PATCH 3/6] Turn line parsing marco into function --- src/utils/private_key_file_parser.rs | 60 ++++++++++++++-------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/src/utils/private_key_file_parser.rs b/src/utils/private_key_file_parser.rs index 9b4b7db1..491221ac 100644 --- a/src/utils/private_key_file_parser.rs +++ b/src/utils/private_key_file_parser.rs @@ -3,25 +3,23 @@ use domain::base::iana::SecAlg; use domain::utils::base64; use std::slice::Iter; -macro_rules! parse_next_line { - ($lines:ident, $match_text:expr) => { - if let Some(line) = $lines.next() { - if !line.starts_with($match_text) { - return Err(Error::from(concat!( - "expected line starting with ", - $match_text - ))); - } +fn parse_next_line<'a>(lines: &mut Iter<&'a str>, match_text: &str) -> Result<&'a str, Error> { + if let Some(line) = lines.next() { + if !line.starts_with(match_text) { + return Err(Error::from(format!( + "expected line starting with {}", + match_text + ))); + } - if let Some((_, m)) = line.split_once(' ') { - m - } else { - return Err(Error::from("malformed line")); - } + if let Some((_, m)) = line.split_once(' ') { + Ok(m) } else { - return Err(Error::from("expected more private key data")); + return Err(Error::from("malformed line")); } - }; + } else { + return Err(Error::from("expected more private key data")); + } } #[derive(Default, Debug)] @@ -39,14 +37,14 @@ pub struct RsaKeyData { impl RsaKeyData { fn parse_lines(algorithm: u8, lines: &mut Iter<&str>) -> Result { - let modulus = parse_next_line!(lines, "Modulus: "); - let public_exponent = parse_next_line!(lines, "PublicExponent: "); - let private_exponent = parse_next_line!(lines, "PrivateExponent: "); - let prime1 = parse_next_line!(lines, "Prime1: "); - let prime2 = parse_next_line!(lines, "Prime2: "); - let exponent1 = parse_next_line!(lines, "Exponent1: "); - let exponent2 = parse_next_line!(lines, "Exponent2: "); - let coefficient = parse_next_line!(lines, "Coefficient: "); + let modulus = parse_next_line(lines, "Modulus: ")?; + let public_exponent = parse_next_line(lines, "PublicExponent: ")?; + let private_exponent = parse_next_line(lines, "PrivateExponent: ")?; + let prime1 = parse_next_line(lines, "Prime1: ")?; + let prime2 = parse_next_line(lines, "Prime2: ")?; + let exponent1 = parse_next_line(lines, "Exponent1: ")?; + let exponent2 = parse_next_line(lines, "Exponent2: ")?; + let coefficient = parse_next_line(lines, "Coefficient: ")?; Ok(RsaKeyData { algorithm_id: algorithm, @@ -71,7 +69,7 @@ pub struct EcKeyData { impl EcKeyData { fn parse_lines(algorithm: u8, lines: &mut Iter<&str>) -> Result { - let private_key = parse_next_line!(lines, "PrivateKey: "); + let private_key = parse_next_line(lines, "PrivateKey: ")?; Ok(EcKeyData { algorithm_id: algorithm, @@ -88,7 +86,7 @@ pub struct HmacKeyData { impl HmacKeyData { fn parse_lines(algorithm: u8, lines: &mut Iter<&str>) -> Result { - let key = parse_next_line!(lines, "Key: "); + let key = parse_next_line(lines, "Key: ")?; Ok(HmacKeyData { algorithm_id: algorithm, @@ -109,11 +107,11 @@ pub struct DsaKeyData { impl DsaKeyData { fn parse_lines(algorithm: u8, lines: &mut Iter<&str>) -> Result { - let prime = parse_next_line!(lines, "Prime(p): "); - let subprime = parse_next_line!(lines, "Subprime(q): "); - let base = parse_next_line!(lines, "Base(g): "); - let private_value = parse_next_line!(lines, "Private_value(x): "); - let public_value = parse_next_line!(lines, "Public_value(y): "); + let prime = parse_next_line(lines, "Prime(p): ")?; + let subprime = parse_next_line(lines, "Subprime(q): ")?; + let base = parse_next_line(lines, "Base(g): ")?; + let private_value = parse_next_line(lines, "Private_value(x): ")?; + let public_value = parse_next_line(lines, "Public_value(y): ")?; Ok(DsaKeyData { algorithm_id: algorithm, From 0ebf2609c4d90d40fd38d09e3bc372863f14273b Mon Sep 17 00:00:00 2001 From: Jannik Peters Date: Tue, 17 Sep 2024 16:51:02 +0200 Subject: [PATCH 4/6] Indent key file generation template --- src/utils/private_key_file_parser.rs | 64 ++++++++++++++-------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/utils/private_key_file_parser.rs b/src/utils/private_key_file_parser.rs index 491221ac..fa48676b 100644 --- a/src/utils/private_key_file_parser.rs +++ b/src/utils/private_key_file_parser.rs @@ -135,16 +135,16 @@ pub enum KeyData { pub fn gen_private_key_file_text(key: KeyData) -> Result { match key { KeyData::Rsa(rsa) => Ok(format!( - "Private-key-format: v1.2 -Algorithm: {alg_id} ({alg_name}) -Modulus: {modulus} -PublicExponent: {pub_exp} -PrivateExponent: {priv_exp} -Prime1: {prime1} -Prime2: {prime2} -Exponent1: {exp1} -Exponent2: {exp2} -Coefficient: {coeff}\n", + "Private-key-format: v1.2\n\ + Algorithm: {alg_id} ({alg_name})\n\ + Modulus: {modulus}\n\ + PublicExponent: {pub_exp}\n\ + PrivateExponent: {priv_exp}\n\ + Prime1: {prime1}\n\ + Prime2: {prime2}\n\ + Exponent1: {exp1}\n\ + Exponent2: {exp2}\n\ + Coefficient: {coeff}\n", alg_id = rsa.algorithm_id, alg_name = SecAlg::from_int(rsa.algorithm_id), modulus = base64::encode_string(&rsa.modulus), @@ -157,39 +157,39 @@ Coefficient: {coeff}\n", coeff = base64::encode_string(&rsa.coefficient), )), KeyData::Ec(ec) => Ok(format!( - "Private-key-format: v1.2 -Algorithm: {alg_id} ({alg_name}) -PrivateKey: {key}\n", + "Private-key-format: v1.2\n\ + Algorithm: {alg_id} ({alg_name})\n\ + PrivateKey: {key}\n", alg_id = ec.algorithm_id, alg_name = SecAlg::from_int(ec.algorithm_id), key = base64::encode_string(&ec.private_key), )), KeyData::Hmac(hmac) => Ok(format!( - "Private-key-format: v1.2 -Algorithm: {alg_id} ({alg_name}) -Key: {key}\n", + "Private-key-format: v1.2\n\ + Algorithm: {alg_id} ({alg_name})\n\ + Key: {key}\n", alg_id = hmac.algorithm_id, alg_name = match hmac.algorithm_id { - 157 => "HMAC_MD5", - 158 => "HMAC_SHA1", - 159 => "HMAC_SHA256", - 161 => "HMAC_SHA1", - 162 => "HMAC_SHA224", - 163 => "HMAC_SHA256", - 164 => "HMAC_SHA384", - 165 => "HMAC_SHA512", - _ => return Err(Error::from("unknown hmac algorithm")) + 157 => "HMAC_MD5", + 158 => "HMAC_SHA1", + 159 => "HMAC_SHA256", + 161 => "HMAC_SHA1", + 162 => "HMAC_SHA224", + 163 => "HMAC_SHA256", + 164 => "HMAC_SHA384", + 165 => "HMAC_SHA512", + _ => return Err(Error::from("unknown hmac algorithm")), }, key = base64::encode_string(&hmac.key), )), KeyData::Dsa(dsa) => Ok(format!( - "Private-key-format: v1.2 -Algorithm: {alg_id} ({alg_name}) -Prime(p): {p} -Subprime(q): {q} -Base(g): {g} -Private_value(x): {x} -Public_value(y): {y}\n", + "Private-key-format: v1.2\n\ + Algorithm: {alg_id} ({alg_name})\n\ + Prime(p): {p}\n\ + Subprime(q): {q}\n\ + Base(g): {g}\n\ + Private_value(x): {x}\n\ + Public_value(y): {y}\n", alg_id = dsa.algorithm_id, alg_name = SecAlg::from_int(dsa.algorithm_id), p = base64::encode_string(&dsa.prime), From af5c2dc73e91a7d27b0ca8e7829004be55aed525 Mon Sep 17 00:00:00 2001 From: Jannik Peters Date: Tue, 17 Sep 2024 17:12:01 +0200 Subject: [PATCH 5/6] Make private key file text functions associated functions of KeyData --- src/utils/private_key_file_parser.rs | 290 ++++++++++++++------------- 1 file changed, 146 insertions(+), 144 deletions(-) diff --git a/src/utils/private_key_file_parser.rs b/src/utils/private_key_file_parser.rs index fa48676b..03b17b54 100644 --- a/src/utils/private_key_file_parser.rs +++ b/src/utils/private_key_file_parser.rs @@ -132,158 +132,160 @@ pub enum KeyData { Dsa(DsaKeyData), } -pub fn gen_private_key_file_text(key: KeyData) -> Result { - match key { - KeyData::Rsa(rsa) => Ok(format!( - "Private-key-format: v1.2\n\ - Algorithm: {alg_id} ({alg_name})\n\ - Modulus: {modulus}\n\ - PublicExponent: {pub_exp}\n\ - PrivateExponent: {priv_exp}\n\ - Prime1: {prime1}\n\ - Prime2: {prime2}\n\ - Exponent1: {exp1}\n\ - Exponent2: {exp2}\n\ - Coefficient: {coeff}\n", - alg_id = rsa.algorithm_id, - alg_name = SecAlg::from_int(rsa.algorithm_id), - modulus = base64::encode_string(&rsa.modulus), - pub_exp = base64::encode_string(&rsa.public_exponent), - priv_exp = base64::encode_string(&rsa.private_exponent), - prime1 = base64::encode_string(&rsa.prime1), - prime2 = base64::encode_string(&rsa.prime2), - exp1 = base64::encode_string(&rsa.exponent1), - exp2 = base64::encode_string(&rsa.exponent2), - coeff = base64::encode_string(&rsa.coefficient), - )), - KeyData::Ec(ec) => Ok(format!( - "Private-key-format: v1.2\n\ - Algorithm: {alg_id} ({alg_name})\n\ - PrivateKey: {key}\n", - alg_id = ec.algorithm_id, - alg_name = SecAlg::from_int(ec.algorithm_id), - key = base64::encode_string(&ec.private_key), - )), - KeyData::Hmac(hmac) => Ok(format!( - "Private-key-format: v1.2\n\ - Algorithm: {alg_id} ({alg_name})\n\ - Key: {key}\n", - alg_id = hmac.algorithm_id, - alg_name = match hmac.algorithm_id { - 157 => "HMAC_MD5", - 158 => "HMAC_SHA1", - 159 => "HMAC_SHA256", - 161 => "HMAC_SHA1", - 162 => "HMAC_SHA224", - 163 => "HMAC_SHA256", - 164 => "HMAC_SHA384", - 165 => "HMAC_SHA512", - _ => return Err(Error::from("unknown hmac algorithm")), - }, - key = base64::encode_string(&hmac.key), - )), - KeyData::Dsa(dsa) => Ok(format!( - "Private-key-format: v1.2\n\ - Algorithm: {alg_id} ({alg_name})\n\ - Prime(p): {p}\n\ - Subprime(q): {q}\n\ - Base(g): {g}\n\ - Private_value(x): {x}\n\ - Public_value(y): {y}\n", - alg_id = dsa.algorithm_id, - alg_name = SecAlg::from_int(dsa.algorithm_id), - p = base64::encode_string(&dsa.prime), - q = base64::encode_string(&dsa.subprime), - g = base64::encode_string(&dsa.base), - x = base64::encode_string(&dsa.private_value), - y = base64::encode_string(&dsa.public_value), - )), +impl KeyData { + pub fn gen_private_key_file_text(&self) -> Result { + match &self { + KeyData::Rsa(rsa) => Ok(format!( + "Private-key-format: v1.2\n\ + Algorithm: {alg_id} ({alg_name})\n\ + Modulus: {modulus}\n\ + PublicExponent: {pub_exp}\n\ + PrivateExponent: {priv_exp}\n\ + Prime1: {prime1}\n\ + Prime2: {prime2}\n\ + Exponent1: {exp1}\n\ + Exponent2: {exp2}\n\ + Coefficient: {coeff}\n", + alg_id = rsa.algorithm_id, + alg_name = SecAlg::from_int(rsa.algorithm_id), + modulus = base64::encode_string(&rsa.modulus), + pub_exp = base64::encode_string(&rsa.public_exponent), + priv_exp = base64::encode_string(&rsa.private_exponent), + prime1 = base64::encode_string(&rsa.prime1), + prime2 = base64::encode_string(&rsa.prime2), + exp1 = base64::encode_string(&rsa.exponent1), + exp2 = base64::encode_string(&rsa.exponent2), + coeff = base64::encode_string(&rsa.coefficient), + )), + KeyData::Ec(ec) => Ok(format!( + "Private-key-format: v1.2\n\ + Algorithm: {alg_id} ({alg_name})\n\ + PrivateKey: {key}\n", + alg_id = ec.algorithm_id, + alg_name = SecAlg::from_int(ec.algorithm_id), + key = base64::encode_string(&ec.private_key), + )), + KeyData::Hmac(hmac) => Ok(format!( + "Private-key-format: v1.2\n\ + Algorithm: {alg_id} ({alg_name})\n\ + Key: {key}\n", + alg_id = hmac.algorithm_id, + alg_name = match hmac.algorithm_id { + 157 => "HMAC_MD5", + 158 => "HMAC_SHA1", + 159 => "HMAC_SHA256", + 161 => "HMAC_SHA1", + 162 => "HMAC_SHA224", + 163 => "HMAC_SHA256", + 164 => "HMAC_SHA384", + 165 => "HMAC_SHA512", + _ => return Err(Error::from("unknown hmac algorithm")), + }, + key = base64::encode_string(&hmac.key), + )), + KeyData::Dsa(dsa) => Ok(format!( + "Private-key-format: v1.2\n\ + Algorithm: {alg_id} ({alg_name})\n\ + Prime(p): {p}\n\ + Subprime(q): {q}\n\ + Base(g): {g}\n\ + Private_value(x): {x}\n\ + Public_value(y): {y}\n", + alg_id = dsa.algorithm_id, + alg_name = SecAlg::from_int(dsa.algorithm_id), + p = base64::encode_string(&dsa.prime), + q = base64::encode_string(&dsa.subprime), + g = base64::encode_string(&dsa.base), + x = base64::encode_string(&dsa.private_value), + y = base64::encode_string(&dsa.public_value), + )), + } } -} -pub fn parse_private_key_file(mut lines: Iter<&str>) -> Result { - // File format in ABNF (no data validity encoded here) + pub fn parse_lines(mut lines: Iter<&str>) -> Result { + // File format in ABNF (no data validity encoded here) - // KEYFILE = HEADER ALGORITHM KEY_SPECIFIC - // HEADER = %s"Private-key-format: v" DIGIT "." DIGIT LF - // ALGORITHM = %s"Algorithm: " 1*3DIGIT *1( " (" ALG_NAME ")" ) LF - // KEY_SPECIFIC = RSA / EDDSA / HMAC / DSA - // - // RSA = MODULUS PUBLIC_EXPONENT PRIVATE_EXPONENT PRIME1 PRIME2 EXPONENT1 EXPONENT2 COEFFICIENT - // DSA = PRIME SUBPRIME BASE PRIVATE_VALUE PUBLIC_VALUE - // EDDSA = %s"PrivateKey: " BASE64_DATA - // HMAC = %s"Key: " BASE64_DATA - // - // PRIME = %s"Prime(p): " BASE64_DATA LF - // SUBPRIME = %s"Subprime(q): " BASE64_DATA LF - // BASE = %s"Base(g): " BASE64_DATA LF - // PRIVATE_VALUE = %s"Private_value(x): " BASE64_DATA LF - // PUBLIC_VALUE = %s"Public_value(y): " BASE64_DATA LF - // - // MODULUS = %s"Modulus: " BASE64_DATA LF - // PUBLIC_EXPONENT = %s"PublicExponent: " BASE64_DATA LF - // PRIVATE_EXPONENT = %s"PrivateExponent: " BASE64_DATA LF - // PRIME1 = %s"Prim1: " BASE64_DATA LF - // PRIME2 = %s"Prime2: " BASE64_DATA LF - // EXPONENT1 = %s"Exponent1: " BASE64_DATA LF - // EXPONENT2 = %s"Exponent2: " BASE64_DATA LF - // COEFFICIENT = %s"Coefficient: " BASE64_DATA LF - // - // BASE64_DATA = *(ALPHA / DIGIT / "/" / "+") + // KEYFILE = HEADER ALGORITHM KEY_SPECIFIC + // HEADER = %s"Private-key-format: v" DIGIT "." DIGIT LF + // ALGORITHM = %s"Algorithm: " 1*3DIGIT *1( " (" ALG_NAME ")" ) LF + // KEY_SPECIFIC = RSA / EDDSA / HMAC / DSA + // + // RSA = MODULUS PUBLIC_EXPONENT PRIVATE_EXPONENT PRIME1 PRIME2 EXPONENT1 EXPONENT2 COEFFICIENT + // DSA = PRIME SUBPRIME BASE PRIVATE_VALUE PUBLIC_VALUE + // EDDSA = %s"PrivateKey: " BASE64_DATA + // HMAC = %s"Key: " BASE64_DATA + // + // PRIME = %s"Prime(p): " BASE64_DATA LF + // SUBPRIME = %s"Subprime(q): " BASE64_DATA LF + // BASE = %s"Base(g): " BASE64_DATA LF + // PRIVATE_VALUE = %s"Private_value(x): " BASE64_DATA LF + // PUBLIC_VALUE = %s"Public_value(y): " BASE64_DATA LF + // + // MODULUS = %s"Modulus: " BASE64_DATA LF + // PUBLIC_EXPONENT = %s"PublicExponent: " BASE64_DATA LF + // PRIVATE_EXPONENT = %s"PrivateExponent: " BASE64_DATA LF + // PRIME1 = %s"Prim1: " BASE64_DATA LF + // PRIME2 = %s"Prime2: " BASE64_DATA LF + // EXPONENT1 = %s"Exponent1: " BASE64_DATA LF + // EXPONENT2 = %s"Exponent2: " BASE64_DATA LF + // COEFFICIENT = %s"Coefficient: " BASE64_DATA LF + // + // BASE64_DATA = *(ALPHA / DIGIT / "/" / "+") - let mut algorithm = 0; + let mut algorithm = 0; - if let Some(line) = lines.next() { - if !line.starts_with("Private-key-format: v1.") { - return Err(Error::from( - "expected private key format version (v1.x) specifier", - )); - } - }; + if let Some(line) = lines.next() { + if !line.starts_with("Private-key-format: v1.") { + return Err(Error::from( + "expected private key format version (v1.x) specifier", + )); + } + }; - if let Some(line) = lines.next() { - if !line.starts_with("Algorithm: ") { - return Err(Error::from("expected algorithm specifier")); - } + if let Some(line) = lines.next() { + if !line.starts_with("Algorithm: ") { + return Err(Error::from("expected algorithm specifier")); + } - // "Algorithm: 123 (NAMEXYZ)" - let mut parts = line.split(' '); - parts.next(); // "Algorithm:" - if let Some(alg) = parts.next() { - algorithm = alg.parse()?; - } else { - return Err(Error::from("expected algorithm identifier number")); - } - }; + // "Algorithm: 123 (NAMEXYZ)" + let mut parts = line.split(' '); + parts.next(); // "Algorithm:" + if let Some(alg) = parts.next() { + algorithm = alg.parse()?; + } else { + return Err(Error::from("expected algorithm identifier number")); + } + }; - match SecAlg::from_int(algorithm) { - SecAlg::RSAMD5 - | SecAlg::RSASHA1 - | SecAlg::RSASHA1_NSEC3_SHA1 - | SecAlg::RSASHA256 - | SecAlg::RSASHA512 => Ok(KeyData::Rsa(RsaKeyData::parse_lines( - algorithm, &mut lines, - )?)), - SecAlg::DSA | SecAlg::DSA_NSEC3_SHA1 => Ok(KeyData::Dsa(DsaKeyData::parse_lines( - algorithm, &mut lines, - )?)), - SecAlg::ECDSAP256SHA256 | SecAlg::ECDSAP384SHA384 | SecAlg::ED25519 | SecAlg::ED448 => { - Ok(KeyData::Ec(EcKeyData::parse_lines(algorithm, &mut lines)?)) - } + match SecAlg::from_int(algorithm) { + SecAlg::RSAMD5 + | SecAlg::RSASHA1 + | SecAlg::RSASHA1_NSEC3_SHA1 + | SecAlg::RSASHA256 + | SecAlg::RSASHA512 => Ok(KeyData::Rsa(RsaKeyData::parse_lines( + algorithm, &mut lines, + )?)), + SecAlg::DSA | SecAlg::DSA_NSEC3_SHA1 => Ok(KeyData::Dsa(DsaKeyData::parse_lines( + algorithm, &mut lines, + )?)), + SecAlg::ECDSAP256SHA256 | SecAlg::ECDSAP384SHA384 | SecAlg::ED25519 | SecAlg::ED448 => { + Ok(KeyData::Ec(EcKeyData::parse_lines(algorithm, &mut lines)?)) + } - /* might be a hmac algorithm */ - _ => match algorithm { - 157 /* HMAC_MD5 */ - | 158 /* HMAC_SHA1 */ - | 159 /* HMAC_SHA256 */ - | 161 /* HMAC_SHA1 */ - | 162 /* HMAC_SHA224 */ - | 163 /* HMAC_SHA256 */ - | 164 /* HMAC_SHA384 */ - | 165 /* HMAC_SHA512 */ - => Ok(KeyData::Hmac(HmacKeyData::parse_lines(algorithm, &mut lines)?)), - /* unknown algorithm number */ - _ => Err(Error::from("unsupported algorithm")), - }, + /* might be a hmac algorithm */ + _ => match algorithm { + 157 /* HMAC_MD5 */ + | 158 /* HMAC_SHA1 */ + | 159 /* HMAC_SHA256 */ + | 161 /* HMAC_SHA1 */ + | 162 /* HMAC_SHA224 */ + | 163 /* HMAC_SHA256 */ + | 164 /* HMAC_SHA384 */ + | 165 /* HMAC_SHA512 */ + => Ok(KeyData::Hmac(HmacKeyData::parse_lines(algorithm, &mut lines)?)), + /* unknown algorithm number */ + _ => Err(Error::from("unsupported algorithm")), + }, + } } } From 8f426c42ed30180cd57bd19814b7d88ab353a536 Mon Sep 17 00:00:00 2001 From: Jannik Peters Date: Tue, 17 Sep 2024 17:13:02 +0200 Subject: [PATCH 6/6] Add documentation to parsing/generating function --- src/utils/private_key_file_parser.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/private_key_file_parser.rs b/src/utils/private_key_file_parser.rs index 03b17b54..fdd6f3b8 100644 --- a/src/utils/private_key_file_parser.rs +++ b/src/utils/private_key_file_parser.rs @@ -133,6 +133,7 @@ pub enum KeyData { } impl KeyData { + /// Generate the Private-key-format text representation of KeyData pub fn gen_private_key_file_text(&self) -> Result { match &self { KeyData::Rsa(rsa) => Ok(format!( @@ -202,6 +203,7 @@ impl KeyData { } } + /// Parses lines of a private key file in bind's Private-key-format v1.x pub fn parse_lines(mut lines: Iter<&str>) -> Result { // File format in ABNF (no data validity encoded here)