Skip to content
Merged
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
5 changes: 4 additions & 1 deletion keetanetwork-account/src/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -922,7 +922,10 @@ macro_rules! impl_crypto_keypair {
.as_ref()
.ok_or(AccountError::InvalidConstruction)?;

Ok(private_key.decrypt(ciphertext.as_ref())?)
let ciphertext = ciphertext.as_ref();
let plaintext = private_key.decrypt(ciphertext)?;

Ok(plaintext)
}

fn supports_encryption(&self) -> bool {
Expand Down
35 changes: 35 additions & 0 deletions keetanetwork-asn1/src/der.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ impl AlgorithmIdentifier {
pub fn new_with_params(oid: &str, parameters: Any) -> Result<Self, Asn1Error> {
Ok(Self { algorithm: ObjectIdentifier::new(oid)?, parameters: Some(parameters) })
}

/// Decode the parameters as an OBJECT IDENTIFIER
///
/// Returns `None` when parameters are absent or not an OBJECT IDENTIFIER.
pub fn parameters_oid(&self) -> Option<ObjectIdentifier> {
let parameters = self.parameters.as_ref()?;
let decoded_oid = parameters.decode_as::<ObjectIdentifier>();

decoded_oid.ok()
}
}

impl TryFrom<spki::AlgorithmIdentifierOwned> for AlgorithmIdentifier {
Expand Down Expand Up @@ -229,6 +239,31 @@ mod tests {
},
}

#[test]
fn test_parameters_oid_decodes_oid_parameters() -> Result<(), Asn1Error> {
let curve_oid = ObjectIdentifier::new(oids::SECP256K1)?;
let oid_param = Any::encode_from(&curve_oid)?;

let alg_with_oid = AlgorithmIdentifier::new_with_params(oids::EC_PUBLIC_KEY, oid_param)?;
assert_eq!(alg_with_oid.parameters_oid(), Some(curve_oid));
Ok(())
}

#[test]
fn test_parameters_oid_rejects_non_oid_parameters() -> Result<(), Asn1Error> {
let null_param = Any::from_der(&[0x05, 0x00])?;
let alg_with_null = AlgorithmIdentifier::new_with_params(oids::RSA_ENCRYPTION, null_param)?;
assert_eq!(alg_with_null.parameters_oid(), None);
Ok(())
}

#[test]
fn test_parameters_oid_absent_parameters() -> Result<(), Asn1Error> {
let alg_without_params = AlgorithmIdentifier::new(oids::ED25519)?;
assert_eq!(alg_without_params.parameters_oid(), None);
Ok(())
}

#[test]
fn test_spki_conversions() -> Result<(), Asn1Error> {
// Test AlgorithmIdentifier round-trip
Expand Down
37 changes: 37 additions & 0 deletions keetanetwork-asn1/src/rasn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ impl AlgorithmIdentifier {
Ok(AlgorithmIdentifier { algorithm: oid, parameters: Some(parameters) })
}

/// Decode the parameters as an OBJECT IDENTIFIER
///
/// Returns `None` when parameters are absent or not an OBJECT IDENTIFIER.
pub fn parameters_oid(&self) -> Option<ObjectIdentifier> {
let parameters = self.parameters.as_ref()?;
let parameter_bytes = parameters.as_bytes();
let decoded_oid = rasn::der::decode::<ObjectIdentifier>(parameter_bytes);

decoded_oid.ok()
}

/// Decode an AlgorithmIdentifier from DER format
pub fn from_der(bytes: &[u8]) -> Result<Self, Asn1Error> {
Ok(rasn::der::decode::<Self>(bytes)?)
Expand Down Expand Up @@ -63,6 +74,7 @@ impl TryFrom<spki::AlgorithmIdentifierOwned> for AlgorithmIdentifier {
.map(|p| DerEncode::to_der(&p))
.transpose()?
.map(Any::new);

Ok(Self { algorithm: oid, parameters })
}
}
Expand All @@ -76,6 +88,7 @@ impl TryFrom<AlgorithmIdentifier> for spki::AlgorithmIdentifierOwned {
.parameters
.map(|p| DerDecode::from_der(p.as_bytes()))
.transpose()?;

Ok(Self { oid, parameters })
}
}
Expand Down Expand Up @@ -545,6 +558,30 @@ mod tests {
Ok(())
}

#[test]
fn test_parameters_oid_decodes_oid_parameters() -> Result<(), Asn1Error> {
let curve_oid = ObjectIdentifier::from_str(oids::SECP256K1)?;
let oid_param = Any::new(rasn::der::encode(&curve_oid)?);

let alg_with_oid = AlgorithmIdentifier::new_with_params(oids::EC_PUBLIC_KEY, oid_param)?;
assert_eq!(alg_with_oid.parameters_oid(), Some(curve_oid));
Ok(())
}

#[test]
fn test_parameters_oid_rejects_non_oid_parameters() -> Result<(), Asn1Error> {
let alg_with_null = AlgorithmIdentifier::new_with_params(oids::RSA_ENCRYPTION, create_null_any())?;
assert_eq!(alg_with_null.parameters_oid(), None);
Ok(())
}

#[test]
fn test_parameters_oid_absent_parameters() -> Result<(), Asn1Error> {
let alg_without_params = AlgorithmIdentifier::new(oids::ED25519)?;
assert_eq!(alg_without_params.parameters_oid(), None);
Ok(())
}

#[test]
fn test_spki_conversions() -> Result<(), Asn1Error> {
let alg_basic = AlgorithmIdentifier::new(oids::ED25519)?;
Expand Down
9 changes: 6 additions & 3 deletions keetanetwork-crypto/src/algorithms/aes_cbc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,12 @@ impl SymmetricEncryption for Aes256Cbc {
// Create cipher
let cipher = Aes256CbcEnc::new_from_slices(key, &iv_bytes)?;
// Encrypt with PKCS#7 padding
let ciphertext = cipher.encrypt_padded_vec_mut::<Pkcs7>(plaintext.as_ref());
let plaintext = plaintext.as_ref();
let ciphertext = cipher.encrypt_padded_vec_mut::<Pkcs7>(plaintext);

// Return IV + ciphertext
Ok(prepend_iv(&iv_bytes, &ciphertext))
let result = prepend_iv(&iv_bytes, &ciphertext);
Ok(result)
}

/// Decrypt data using AES-256-CBC.
Expand All @@ -64,7 +66,8 @@ impl SymmetricEncryption for Aes256Cbc {
ensure_key_size(key, 32)?;

// Extract IV and encrypted payload
let (iv, encrypted_data) = split_iv(ciphertext.as_ref())?;
let ciphertext = ciphertext.as_ref();
let (iv, encrypted_data) = split_iv(ciphertext)?;
// Create cipher
let cipher = Aes256CbcDec::new_from_slices(key, iv)?;

Expand Down
3 changes: 2 additions & 1 deletion keetanetwork-crypto/src/algorithms/aes_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,6 @@ pub(crate) fn split_iv(ciphertext: &[u8]) -> Result<(&[u8], &[u8]), CryptoError>
return Err(CryptoError::DecryptionFailed);
}

Ok(ciphertext.split_at(IV_SIZE))
let iv_and_payload = ciphertext.split_at(IV_SIZE);
Ok(iv_and_payload)
}
9 changes: 6 additions & 3 deletions keetanetwork-crypto/src/algorithms/aes_ctr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ impl Aes128CtrCipher {
// Create CTR cipher instance
let mut cipher = Aes128Ctr::new_from_slices(key, iv)?;
// CTR mode works in-place, so we need a mutable copy
let mut output = data.as_ref().to_vec();
let data = data.as_ref();
let mut output = data.to_vec();

cipher.apply_keystream(&mut output);

Expand Down Expand Up @@ -130,7 +131,8 @@ impl SymmetricEncryption for Aes128CtrCipher {
let ciphertext = self.encrypt_with_iv(key, iv_bytes, plaintext)?;

// Prepend IV to ciphertext
Ok(prepend_iv(&iv_bytes, &ciphertext))
let result = prepend_iv(&iv_bytes, &ciphertext);
Ok(result)
}

/// Decrypt data with IV extracted from the beginning
Expand All @@ -141,7 +143,8 @@ impl SymmetricEncryption for Aes128CtrCipher {
ensure_key_size(key, 16)?;

// Extract IV from the beginning
let (iv, encrypted_data) = split_iv(ciphertext.as_ref())?;
let ciphertext = ciphertext.as_ref();
let (iv, encrypted_data) = split_iv(ciphertext)?;

// Decrypt with the extracted IV
self.decrypt_with_iv(key, iv, encrypted_data)
Expand Down
31 changes: 27 additions & 4 deletions keetanetwork-x509/src/certificates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,31 @@ fn check_duplicate_extensions(extensions: &[Extension]) -> Result<(), Certificat
Ok(())
}

/// Verify an ECDSA certificate signature using the curve declared by the
/// issuer's SubjectPublicKeyInfo.
///
/// RFC 5480 Section 2.1.1 requires the algorithm parameters to be present and
/// restricts them to a named-curve OID. This implementation accepts the
/// secp256r1 and secp256k1 named curves.
fn verify_ecdsa_declared_curve(
issuer_public_key: &SubjectPublicKeyInfo,
signature_bytes: &[u8],
tbs_der: &[u8],
hash: HashAlgorithm,
) -> Result<bool, CertificateError> {
let Some(curve_oid) = issuer_public_key.algorithm.parameters_oid() else {
return Ok(false);
};

let curve_oid = curve_oid.to_string();
let public_key_bytes = issuer_public_key.subject_public_key.raw_bytes();
match curve_oid.as_str() {
oids::SECP256R1 => utils::try_verify_ecdsa_secp256r1(public_key_bytes, signature_bytes, tbs_der, hash),
oids::SECP256K1 => utils::try_verify_ecdsa_secp256k1(public_key_bytes, signature_bytes, tbs_der, hash),
_ => Ok(false),
}
}

impl Certificate {
/// Check if the certificate is valid at a specific time
pub fn is_valid_at(&self, time: DateTime<Utc>) -> Result<bool, CertificateError> {
Expand Down Expand Up @@ -1209,13 +1234,11 @@ impl Certificate {
oids::ED25519 => utils::verify_ed25519_signature(public_key_bytes, signature_bytes, &tbs_der),

oids::ECDSA_WITH_SHA3_256 => {
// For ECDSA, try both curves since the verification function handles curve detection
utils::verify_ecdsa_signature(public_key_bytes, signature_bytes, &tbs_der, HashAlgorithm::Sha3_256)
verify_ecdsa_declared_curve(issuer_public_key, signature_bytes, &tbs_der, HashAlgorithm::Sha3_256)
}

oids::ECDSA_WITH_SHA256 => {
// For ECDSA, try both curves since the verification function handles curve detection
utils::verify_ecdsa_signature(public_key_bytes, signature_bytes, &tbs_der, HashAlgorithm::Sha2_256)
verify_ecdsa_declared_curve(issuer_public_key, signature_bytes, &tbs_der, HashAlgorithm::Sha2_256)
}

oids::SHA256_WITH_RSA => Err(CertificateError::InvalidCertificate),
Expand Down
Loading
Loading