Skip to content

Commit

Permalink
fix: Allow using from_encrypted_pem for unencrypted pems if an empty …
Browse files Browse the repository at this point in the history
…password is given

Signed-off-by: Gerald Pinder <[email protected]>
  • Loading branch information
gmpinder committed Aug 24, 2024
1 parent 48857ff commit 300910f
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 113 deletions.
79 changes: 32 additions & 47 deletions src/crypto/signing_key/ecdsa/ec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ where
let ec_seckey = SecretKey::<C>::from_sec1_der(pkcs8.private_key)?;
Self::from_private_key(ec_seckey)
}
PRIVATE_KEY_PEM_LABEL if password.is_empty() => Self::from_pem(private_key),
tag => Err(SigstoreError::PrivateKeyDecryptError(format!(
"Unsupported pem tag {tag}"
))),
Expand Down Expand Up @@ -366,6 +367,8 @@ where
mod tests {
use std::fs;

use rstest::rstest;

use crate::crypto::{
signing_key::{tests::MESSAGE, KeyPair, Signer},
verification_key::CosignVerificationKey,
Expand All @@ -392,24 +395,16 @@ mod tests {

/// This test will try to read an encrypted ecdsa
/// private key file, which is generated by `sigstore`.
#[test]
fn ecdsa_from_encrypted_pem() {
let content = fs::read("tests/data/keys/ecdsa_encrypted_private.key")
.expect("read tests/data/keys/ecdsa_encrypted_private.key failed.");
let key = EcdsaKeys::<p256::NistP256>::from_encrypted_pem(&content, PASSWORD);
assert!(
key.is_ok(),
"can not create EcdsaKeys from encrypted PEM file"
);
}

/// This test will try to read an encrypted ecdsa
/// private key file, which is generated by `sigstore`.
#[test]
fn ecdsa_from_encrypted_pem_cosign_empty_password() {
let content = fs::read("tests/data/keys/cosign_generated_encrypted_empty_private.key")
.expect("read tests/data/keys/cosign_generated_encrypted_empty_private.key failed.");
let key = EcdsaKeys::<p256::NistP256>::from_encrypted_pem(&content, EMPTY_PASSWORD);
#[rstest]
#[case("tests/data/keys/ecdsa_encrypted_private.key", PASSWORD)]
#[case::empty_password(
"tests/data/keys/cosign_generated_encrypted_empty_private.key",
EMPTY_PASSWORD
)]
#[case::empty_password_unencrypted("tests/data/keys/ecdsa_private.key", EMPTY_PASSWORD)]
fn ecdsa_from_encrypted_pem(#[case] keypath: &str, #[case] password: &[u8]) {
let content = fs::read(keypath).expect("read key failed.");
let key = EcdsaKeys::<p256::NistP256>::from_encrypted_pem(&content, password);
assert!(
key.is_ok(),
"can not create EcdsaKeys from encrypted PEM file"
Expand All @@ -418,27 +413,29 @@ mod tests {

/// This test will try to encrypt a ecdsa keypair and
/// return the pem-encoded contents.
#[test]
fn ecdsa_to_encrypted_pem() {
#[rstest]
#[case(PASSWORD)]
#[case::empty_password(EMPTY_PASSWORD)]
fn ecdsa_to_encrypted_pem(#[case] password: &[u8]) {
let key =
EcdsaKeys::<p256::NistP256>::new().expect("create ecdsa keys with P256 curve failed.");
let key = key.private_key_to_encrypted_pem(PASSWORD);
let key = key.private_key_to_encrypted_pem(password);
assert!(
key.is_ok(),
"can not export private key in encrypted PEM format."
);
}

/// This test will try to encrypt a ecdsa keypair and
/// return the pem-encoded contents.
/// This test will ensure that an unencrypted
/// keypair will fail to read if a non-empty
/// password is given.
#[test]
fn ecdsa_to_encrypted_pem_empty_password() {
let key =
EcdsaKeys::<p256::NistP256>::new().expect("create ecdsa keys with P256 curve failed.");
let key = key.private_key_to_encrypted_pem(EMPTY_PASSWORD);
fn ecdsa_error_unencrypted_pem_password() {
let content = fs::read("tests/data/keys/ecdsa_private.key").expect("read key failed.");
let key = EcdsaKeys::<p256::NistP256>::from_encrypted_pem(&content, PASSWORD);
assert!(
key.is_ok(),
"can not export private key in encrypted PEM format."
key.is_err_and(|e| e.to_string().contains("Unsupported pem tag")),
"read unencrypted key with password"
);
}

Expand All @@ -459,28 +456,16 @@ mod tests {
/// This test will generate a EcdsaKeys, encode the private key
/// it into pem, and decode a new key from the generated pem-encoded
/// private key.
#[test]
fn ecdsa_to_and_from_encrypted_pem() {
let key =
EcdsaKeys::<p256::NistP256>::new().expect("create ecdsa keys with P256 curve failed.");
let key = key
.private_key_to_encrypted_pem(PASSWORD)
.expect("export private key to PEM format failed.");
let key = EcdsaKeys::<p256::NistP256>::from_encrypted_pem(key.as_bytes(), PASSWORD);
assert!(key.is_ok(), "can not create EcdsaKeys from PEM string.");
}

/// This test will generate a EcdsaKeys, encode the private key
/// it into pem, and decode a new key from the generated pem-encoded
/// private key.
#[test]
fn ecdsa_to_and_from_encrypted_pem_empty_password() {
#[rstest]
#[case(PASSWORD)]
#[case::empty_password(EMPTY_PASSWORD)]
fn ecdsa_to_and_from_encrypted_pem(#[case] password: &[u8]) {
let key =
EcdsaKeys::<p256::NistP256>::new().expect("create ecdsa keys with P256 curve failed.");
let key = key
.private_key_to_encrypted_pem(EMPTY_PASSWORD)
.private_key_to_encrypted_pem(password)
.expect("export private key to PEM format failed.");
let key = EcdsaKeys::<p256::NistP256>::from_encrypted_pem(key.as_bytes(), EMPTY_PASSWORD);
let key = EcdsaKeys::<p256::NistP256>::from_encrypted_pem(key.as_bytes(), password);
assert!(key.is_ok(), "can not create EcdsaKeys from PEM string.");
}

Expand Down
60 changes: 28 additions & 32 deletions src/crypto/signing_key/ed25519.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ impl Ed25519Keys {
})?;
Self::from_key_pair_bytes(key_pair_bytes)
}
PRIVATE_KEY_PEM_LABEL if password.is_empty() => Self::from_pem(encrypted_pem),
tag => Err(SigstoreError::PrivateKeyDecryptError(format!(
"Unsupported pem tag {tag}"
))),
Expand Down Expand Up @@ -279,6 +280,8 @@ impl Signer for Ed25519Signer {
mod tests {
use std::fs;

use rstest::rstest;

use crate::crypto::{
signing_key::{tests::MESSAGE, KeyPair, Signer},
verification_key::CosignVerificationKey,
Expand All @@ -305,11 +308,12 @@ mod tests {

/// This test will try to read an encrypted ed25519
/// private key file, which is generated by `sigstore`.
#[test]
fn ed25519_from_encrypted_pem() {
let content = fs::read("tests/data/keys/ed25519_encrypted_private.key")
.expect("read tests/data/keys/ed25519_encrypted_private.key failed.");
let key = Ed25519Keys::from_encrypted_pem(&content, PASSWORD);
#[rstest]
#[case("tests/data/keys/ed25519_encrypted_private.key", PASSWORD)]
#[case::empty_password("tests/data/keys/ed25519_private.key", EMPTY_PASSWORD)]
fn ed25519_from_encrypted_pem(#[case] keypath: &str, #[case] password: &[u8]) {
let content = fs::read(keypath).expect("read key failed.");
let key = Ed25519Keys::from_encrypted_pem(&content, password);
assert!(
key.is_ok(),
"can not create Ed25519Keys from encrypted PEM file"
Expand All @@ -318,25 +322,28 @@ mod tests {

/// This test will try to encrypt a ed25519 keypair and
/// return the pem-encoded contents.
#[test]
fn ed25519_to_encrypted_pem() {
#[rstest]
#[case(PASSWORD)]
#[case::empty_password(EMPTY_PASSWORD)]
fn ed25519_to_encrypted_pem(#[case] password: &[u8]) {
let key = Ed25519Keys::new().expect("create Ed25519 keys failed.");
let key = key.private_key_to_encrypted_pem(PASSWORD);
let key = key.private_key_to_encrypted_pem(password);
assert!(
key.is_ok(),
"can not export private key in encrypted PEM format."
);
}

/// This test will try to encrypt a ed25519 keypair and
/// return the pem-encoded contents.
/// This test will ensure that an unencrypted
/// keypair will fail to read if a non-empty
/// password is given.
#[test]
fn ed25519_to_encrypted_pem_empty_password() {
let key = Ed25519Keys::new().expect("create Ed25519 keys failed.");
let key = key.private_key_to_encrypted_pem(EMPTY_PASSWORD);
fn ed25519_error_unencrypted_pem_password() {
let content = fs::read("tests/data/keys/ed25519_private.key").expect("read key failed.");
let key = Ed25519Keys::from_encrypted_pem(&content, PASSWORD);
assert!(
key.is_ok(),
"can not export private key in encrypted PEM format."
key.is_err_and(|e| e.to_string().contains("Unsupported pem tag")),
"read unencrypted key with password"
);
}

Expand All @@ -356,26 +363,15 @@ mod tests {
/// This test will generate a Ed25519Keys, encode the private key
/// into pem, and decode a new key from the generated pem-encoded
/// private key.
#[test]
fn ed25519_to_and_from_encrypted_pem() {
let key = Ed25519Keys::new().expect("create ed25519 keys failed.");
let key = key
.private_key_to_encrypted_pem(PASSWORD)
.expect("export private key to PEM format failed.");
let key = Ed25519Keys::from_encrypted_pem(key.as_bytes(), PASSWORD);
assert!(key.is_ok(), "can not create Ed25519Keys from PEM string.");
}

/// This test will generate a Ed25519Keys, encode the private key
/// into pem, and decode a new key from the generated pem-encoded
/// private key.
#[test]
fn ed25519_to_and_from_encrypted_pem_empty_password() {
#[rstest]
#[case(PASSWORD)]
#[case::empty_password(EMPTY_PASSWORD)]
fn ed25519_to_and_from_encrypted_pem(#[case] password: &[u8]) {
let key = Ed25519Keys::new().expect("create ed25519 keys failed.");
let key = key
.private_key_to_encrypted_pem(EMPTY_PASSWORD)
.private_key_to_encrypted_pem(password)
.expect("export private key to PEM format failed.");
let key = Ed25519Keys::from_encrypted_pem(key.as_bytes(), EMPTY_PASSWORD);
let key = Ed25519Keys::from_encrypted_pem(key.as_bytes(), password);
assert!(key.is_ok(), "can not create Ed25519Keys from PEM string.");
}

Expand Down
65 changes: 31 additions & 34 deletions src/crypto/signing_key/rsa/keypair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ impl RSAKeys {
})?;
Ok(Self::from(private_key))
}

RSA_PRIVATE_KEY_PEM_LABEL | PRIVATE_KEY_PEM_LABEL if password.is_empty() => {
Self::from_pem(encrypted_pem)
}
tag => Err(SigstoreError::PrivateKeyDecryptError(format!(
"Unsupported pem tag {tag}"
))),
Expand Down Expand Up @@ -267,6 +269,8 @@ impl KeyPair for RSAKeys {
mod tests {
use std::fs;

use rstest::rstest;

use crate::crypto::{
signing_key::{
rsa::{DigestAlgorithm, PaddingScheme, RSASigner},
Expand Down Expand Up @@ -298,11 +302,13 @@ mod tests {

/// This test will try to read an encrypted rsa
/// private key file, which is generated by `sigstore`.
#[test]
fn rsa_from_encrypted_pem() {
let content = fs::read("tests/data/keys/rsa_encrypted_private.key")
.expect("read tests/data/keys/rsa_encrypted_private.key failed.");
let key = RSAKeys::from_encrypted_pem(&content, PASSWORD);
#[rstest]
#[case("tests/data/keys/rsa_encrypted_private.key", PASSWORD)]
#[case("tests/data/keys/rsa_private.key", EMPTY_PASSWORD)]
fn rsa_from_encrypted_pem(#[case] keypath: &str, #[case] password: &[u8]) {
let content =
fs::read(keypath).expect("read tests/data/keys/rsa_encrypted_private.key failed.");
let key = RSAKeys::from_encrypted_pem(&content, password);
assert!(
key.is_ok(),
"can not create RSAKeys from encrypted PEM file"
Expand All @@ -312,26 +318,28 @@ mod tests {
/// This test will try to encrypt a rsa keypair and
/// return the pem-encoded contents. The bit size
/// of the rsa key is [`KEY_SIZE`].
#[test]
fn rsa_to_encrypted_pem() {
#[rstest]
#[case(PASSWORD)]
#[case::empty_password(PASSWORD)]
fn rsa_to_encrypted_pem(#[case] password: &[u8]) {
let key = RSAKeys::new(KEY_SIZE).expect("create rsa keys failed.");
let key = key.private_key_to_encrypted_pem(PASSWORD);
let key = key.private_key_to_encrypted_pem(password);
assert!(
key.is_ok(),
"can not export private key in encrypted PEM format."
);
}

/// This test will try to encrypt a rsa keypair and
/// return the pem-encoded contents. The bit size
/// of the rsa key is [`KEY_SIZE`].
/// This test will ensure that an unencrypted
/// keypair will fail to read if a non-empty
/// password is given.
#[test]
fn rsa_to_encrypted_pem_empty_password() {
let key = RSAKeys::new(KEY_SIZE).expect("create rsa keys failed.");
let key = key.private_key_to_encrypted_pem(EMPTY_PASSWORD);
fn rsa_error_unencrypted_pem_password() {
let content = fs::read("tests/data/keys/rsa_private.key").expect("read key failed.");
let key = RSAKeys::from_encrypted_pem(&content, PASSWORD);
assert!(
key.is_ok(),
"can not export private key in encrypted PEM format."
key.is_err_and(|e| e.to_string().contains("Unsupported pem tag")),
"read unencrypted key with password"
);
}

Expand All @@ -351,26 +359,15 @@ mod tests {
/// This test will generate a RSAKeys, encode the private key
/// it into pem, and decode a new key from the generated pem-encoded
/// private key.
#[test]
fn rsa_to_and_from_encrypted_pem() {
let key = RSAKeys::new(KEY_SIZE).expect("create rsa keys failed.");
let key = key
.private_key_to_encrypted_pem(PASSWORD)
.expect("export private key to PEM format failed.");
let key = RSAKeys::from_encrypted_pem(key.as_bytes(), PASSWORD);
assert!(key.is_ok(), "can not create RSAKeys from PEM string.");
}

/// This test will generate a RSAKeys, encode the private key
/// it into pem, and decode a new key from the generated pem-encoded
/// private key.
#[test]
fn rsa_to_and_from_encrypted_pem_empty_password() {
#[rstest]
#[case(PASSWORD)]
#[case::empty_password(EMPTY_PASSWORD)]
fn rsa_to_and_from_encrypted_pem(#[case] password: &[u8]) {
let key = RSAKeys::new(KEY_SIZE).expect("create rsa keys failed.");
let key = key
.private_key_to_encrypted_pem(EMPTY_PASSWORD)
.private_key_to_encrypted_pem(password)
.expect("export private key to PEM format failed.");
let key = RSAKeys::from_encrypted_pem(key.as_bytes(), EMPTY_PASSWORD);
let key = RSAKeys::from_encrypted_pem(key.as_bytes(), password);
assert!(key.is_ok(), "can not create RSAKeys from PEM string.");
}

Expand Down

0 comments on commit 300910f

Please sign in to comment.