use auths_core::api::runtime::export_key_openssh_pub;
use auths_core::crypto::signer::encrypt_keypair;
use auths_core::storage::keychain::{IdentityDID, KeyAlias, KeyRole, KeyStorage};
use auths_core::storage::memory::{MEMORY_KEYCHAIN, MemoryKeychainHandle};
use pkcs8::der::Encode;
use pkcs8::der::asn1::ObjectIdentifier;
use pkcs8::{AlgorithmIdentifierRef, PrivateKeyInfo};
use ring::signature::{Ed25519KeyPair, KeyPair};
const OID_ED25519: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.101.112");
fn fresh_keychain() -> Box<dyn KeyStorage + Send + Sync> {
MEMORY_KEYCHAIN.lock().unwrap().clear_all().ok();
Box::new(MemoryKeychainHandle)
}
fn create_ring_compatible_pkcs8() -> (Vec<u8>, Vec<u8>) {
let rng = ring::rand::SystemRandom::new();
let pkcs8_doc = Ed25519KeyPair::generate_pkcs8(&rng).expect("Failed to generate key");
let pkcs8_bytes = pkcs8_doc.as_ref().to_vec();
let keypair = Ed25519KeyPair::from_pkcs8(&pkcs8_bytes).expect("Failed to parse generated key");
let pubkey = keypair.public_key().as_ref().to_vec();
(pkcs8_bytes, pubkey)
}
fn create_non_ring_pkcs8() -> (Vec<u8>, Vec<u8>) {
let rng = ring::rand::SystemRandom::new();
let pkcs8_doc = Ed25519KeyPair::generate_pkcs8(&rng).expect("Failed to generate key");
let keypair =
Ed25519KeyPair::from_pkcs8(pkcs8_doc.as_ref()).expect("Failed to parse generated key");
let pubkey = keypair.public_key().as_ref().to_vec();
let pk_info =
PrivateKeyInfo::try_from(pkcs8_doc.as_ref()).expect("Failed to parse as PrivateKeyInfo");
let wrapped_seed = pk_info.private_key;
assert_eq!(wrapped_seed.len(), 34, "Expected wrapped seed");
let seed = &wrapped_seed[2..];
let non_ring_pkcs8 = PrivateKeyInfo {
algorithm: AlgorithmIdentifierRef {
oid: OID_ED25519,
parameters: None,
},
private_key: seed, public_key: None,
}
.to_der()
.expect("Failed to encode PKCS#8");
assert!(
Ed25519KeyPair::from_pkcs8(&non_ring_pkcs8).is_err(),
"Ring should NOT be able to parse this format"
);
(non_ring_pkcs8, pubkey)
}
#[test]
fn test_export_ring_compatible_key() {
let keychain = fresh_keychain();
let alias = "test-ring-key";
let passphrase = "Test-P@ss12345";
let identity_did = IdentityDID::new_unchecked("did:keri:test123");
let (pkcs8_bytes, _expected_pubkey) = create_ring_compatible_pkcs8();
let encrypted = encrypt_keypair(&pkcs8_bytes, passphrase).expect("Failed to encrypt");
keychain
.store_key(
&KeyAlias::new_unchecked(alias),
&identity_did,
KeyRole::Primary,
&encrypted,
)
.expect("Failed to store key");
let result = export_key_openssh_pub(alias, passphrase, keychain.as_ref());
assert!(result.is_ok(), "Export failed: {:?}", result.err());
let openssh_pubkey = result.unwrap();
assert!(
openssh_pubkey.starts_with("ssh-ed25519 "),
"Should be ssh-ed25519 format"
);
assert!(
openssh_pubkey.contains(alias),
"Should contain alias as comment"
);
assert!(openssh_pubkey.len() > 50, "Should have reasonable length");
}
#[test]
fn test_export_non_ring_compatible_key() {
let keychain = fresh_keychain();
let alias = "test-nonring-key";
let passphrase = "Test-P@ss12345";
let identity_did = IdentityDID::new_unchecked("did:keri:test456");
let (pkcs8_bytes, _) = create_non_ring_pkcs8();
let encrypted = encrypt_keypair(&pkcs8_bytes, passphrase).expect("Failed to encrypt");
keychain
.store_key(
&KeyAlias::new_unchecked(alias),
&identity_did,
KeyRole::Primary,
&encrypted,
)
.expect("Failed to store key");
let result = export_key_openssh_pub(alias, passphrase, keychain.as_ref());
assert!(
result.is_ok(),
"Export should succeed via fallback: {:?}",
result.err()
);
let openssh_pubkey = result.unwrap();
assert!(
openssh_pubkey.starts_with("ssh-ed25519 "),
"Should be ssh-ed25519 format"
);
assert!(
openssh_pubkey.contains(alias),
"Should contain alias as comment"
);
}
#[test]
fn test_export_with_wrong_passphrase() {
let keychain = fresh_keychain();
let alias = "test-wrong-pass";
let passphrase = "Corr3ct-P@sswd!";
let wrong_passphrase = "Wr0ng-P@ssword!";
let identity_did = IdentityDID::new_unchecked("did:keri:test789");
let (pkcs8_bytes, _) = create_ring_compatible_pkcs8();
let encrypted = encrypt_keypair(&pkcs8_bytes, passphrase).expect("Failed to encrypt");
keychain
.store_key(
&KeyAlias::new_unchecked(alias),
&identity_did,
KeyRole::Primary,
&encrypted,
)
.expect("Failed to store key");
let result = export_key_openssh_pub(alias, wrong_passphrase, keychain.as_ref());
assert!(result.is_err(), "Should fail with wrong passphrase");
}
#[test]
fn test_export_nonexistent_key() {
let keychain = fresh_keychain();
let result = export_key_openssh_pub("nonexistent-alias", "any-pass", keychain.as_ref());
assert!(result.is_err(), "Should fail for nonexistent key");
}