Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion rs/crypto/utils/threshold_sig_der/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ rust_library(
# Keep sorted.
"//rs/crypto/internal/crypto_lib/types",
"//rs/types/types",
"@crate_index//:base64",
"@crate_index//:pem",
"@crate_index//:simple_asn1",
"@crate_index//:thiserror",
],
Expand Down
2 changes: 1 addition & 1 deletion rs/crypto/utils/threshold_sig_der/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ edition.workspace = true
documentation.workspace = true

[dependencies]
base64 = { workspace = true }
ic-crypto-internal-types = { path = "../../internal/crypto_lib/types/" }
ic-types = { path = "../../../types/types" }
pem = { workspace = true }
simple_asn1 = { workspace = true }
thiserror = { workspace = true }

Expand Down
45 changes: 12 additions & 33 deletions rs/crypto/utils/threshold_sig_der/src/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ pub enum KeyConversionError {
/// The DER encoding is invalid or decoding failed.
#[error("Invalid DER encoding: {0}")]
InvalidDer(String),
/// The base64 encoding is invalid.
#[error("Invalid base64 encoding: {0}")]
InvalidBase64(String),
/// Failed to encode the key to DER format.
#[error("Failed to encode key to DER: {0}")]
DerEncoding(String),
Expand All @@ -39,31 +36,16 @@ pub fn parse_threshold_sig_key_from_pem_file(
pem_file: &Path,
) -> Result<ThresholdSigPublicKey, KeyConversionError> {
let buf = std::fs::read(pem_file).map_err(|e| KeyConversionError::IoError(e.to_string()))?;
let s = String::from_utf8_lossy(&buf);
let lines: Vec<_> = s.trim_end().lines().collect();
let n = lines.len();
let pem = pem::parse(&buf).map_err(|e| KeyConversionError::InvalidPem(format!("{e:?}")))?;

if n < 3 {
return Err(KeyConversionError::InvalidPem(
"input file is too short".to_string(),
));
if pem.tag() != "PUBLIC KEY" {
return Err(KeyConversionError::InvalidPem(format!(
"expected 'PUBLIC KEY' tag, got '{}'",
pem.tag()
)));
}

if !lines[0].starts_with("-----BEGIN PUBLIC KEY-----") {
return Err(KeyConversionError::InvalidPem(
"PEM file doesn't start with 'BEGIN PUBLIC KEY' block".to_string(),
));
}
if !lines[n - 1].starts_with("-----END PUBLIC KEY-----") {
return Err(KeyConversionError::InvalidPem(
"PEM file doesn't end with 'END PUBLIC KEY' block".to_string(),
));
}

let decoded = base64::decode(lines[1..n - 1].join(""))
.map_err(|err| KeyConversionError::InvalidBase64(err.to_string()))?;

parse_threshold_sig_key_from_der(&decoded)
parse_threshold_sig_key_from_der(pem.contents())
}

/// Parse a DER format threshold signature public key from bytes.
Expand Down Expand Up @@ -105,12 +87,9 @@ pub fn threshold_sig_public_key_to_pem(

/// Encodes DER-encoded public key bytes into PEM format.
pub fn public_key_der_to_pem(der_bytes: &[u8]) -> Vec<u8> {
let mut pem = vec![];
pem.extend_from_slice(b"-----BEGIN PUBLIC KEY-----\n");
for chunk in base64::encode(der_bytes).as_bytes().chunks(64) {
pem.extend_from_slice(chunk);
pem.extend_from_slice(b"\n");
}
pem.extend_from_slice(b"-----END PUBLIC KEY-----\n");
pem
pem::encode_config(
&pem::Pem::new("PUBLIC KEY", der_bytes),
pem::EncodeConfig::new().set_line_ending(pem::LineEnding::LF),
)
.into_bytes()
}
8 changes: 5 additions & 3 deletions rs/crypto/utils/threshold_sig_der/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,14 +173,16 @@ fn test_pem_missing_end_header() {
}

#[test]
fn test_pem_too_short() {
fn test_pem_empty_content() {
use std::io::Write;

// Valid PEM structure but with no content between the headers.
// The pem crate parses this successfully, then DER parsing fails on empty content.
let bad_pem = "-----BEGIN PUBLIC KEY-----\n-----END PUBLIC KEY-----\n";
let mut tmpfile = tempfile::NamedTempFile::new().unwrap();
tmpfile.write_all(bad_pem.as_bytes()).unwrap();
let result = parse_threshold_sig_key_from_pem_file(tmpfile.path());
assert!(matches!(result, Err(KeyConversionError::InvalidPem(_))));
assert!(matches!(result, Err(KeyConversionError::InvalidDer(_))));
}

#[test]
Expand All @@ -193,7 +195,7 @@ fn test_pem_invalid_base64() {
let mut tmpfile = tempfile::NamedTempFile::new().unwrap();
tmpfile.write_all(bad_pem.as_bytes()).unwrap();
let result = parse_threshold_sig_key_from_pem_file(tmpfile.path());
assert!(matches!(result, Err(KeyConversionError::InvalidBase64(_))));
assert!(matches!(result, Err(KeyConversionError::InvalidPem(_))));
}

#[test]
Expand Down
Loading