use native_ossl::pkey::{
DeriveCtx, KeygenCtx, PkeyDecryptCtx, PkeyEncryptCtx, SignInit, Signer, Verifier,
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
ed25519_sign_verify()?;
ecdh_derive()?;
rsa_oaep()?;
Ok(())
}
fn ed25519_sign_verify() -> Result<(), Box<dyn std::error::Error>> {
let message = b"sign this message";
let mut kgen = KeygenCtx::new(c"ED25519")?;
let priv_key = kgen.generate()?;
let init = SignInit::default();
let mut signer = Signer::new(&priv_key, &init)?;
let sig = signer.sign_oneshot(message)?;
println!(
"Ed25519 signature ({} bytes): {}",
sig.len(),
hex::encode(&sig)
);
let pub_key = native_ossl::pkey::Pkey::<native_ossl::pkey::Public>::from(priv_key.clone());
let mut verifier = Verifier::new(&pub_key, &init)?;
verifier.verify_oneshot(message, &sig)?;
println!("Ed25519 verify: OK");
let mut bad = message.to_vec();
bad[0] ^= 1;
let mut verifier2 = Verifier::new(&pub_key, &init)?;
assert!(!verifier2.verify_oneshot(&bad, &sig)?);
println!("Ed25519 tampered message rejected: OK");
let pem = priv_key.to_pem()?;
let reloaded = native_ossl::pkey::Pkey::<native_ossl::pkey::Private>::from_pem(&pem)?;
assert!(priv_key.public_eq(&reloaded));
println!("Ed25519 PEM round-trip: OK");
Ok(())
}
fn ecdh_derive() -> Result<(), Box<dyn std::error::Error>> {
let mut kgen = KeygenCtx::new(c"EC")?;
let params = native_ossl::params::ParamBuilder::new()?
.push_utf8_string(c"group", c"P-256")?
.build()?;
kgen.set_params(¶ms)?;
let alice_priv = kgen.generate()?;
let bob_priv = kgen.generate()?;
let alice_pub = native_ossl::pkey::Pkey::<native_ossl::pkey::Public>::from(alice_priv.clone());
let bob_pub = native_ossl::pkey::Pkey::<native_ossl::pkey::Public>::from(bob_priv.clone());
let mut alice_ctx = DeriveCtx::new(&alice_priv)?;
alice_ctx.set_peer(&bob_pub)?;
let shared_len = alice_ctx.derive_len()?;
let mut alice_shared = vec![0u8; shared_len];
alice_ctx.derive(&mut alice_shared)?;
let mut bob_ctx = DeriveCtx::new(&bob_priv)?;
bob_ctx.set_peer(&alice_pub)?;
let mut bob_shared = vec![0u8; shared_len];
bob_ctx.derive(&mut bob_shared)?;
assert_eq!(alice_shared, bob_shared, "ECDH shared secrets must match");
println!(
"ECDH P-256 shared secret ({} bytes): {}",
alice_shared.len(),
hex::encode(&alice_shared)
);
Ok(())
}
fn rsa_oaep() -> Result<(), Box<dyn std::error::Error>> {
let mut kgen = KeygenCtx::new(c"RSA")?;
let params = native_ossl::params::ParamBuilder::new()?
.push_uint(c"bits", 2048)?
.build()?;
kgen.set_params(¶ms)?;
let priv_key = kgen.generate()?;
let pub_key = native_ossl::pkey::Pkey::<native_ossl::pkey::Public>::from(priv_key.clone());
println!("RSA key size: {} bits", priv_key.bits());
let oaep_params = native_ossl::params::ParamBuilder::new()?
.push_utf8_string(c"pad-mode", c"oaep")?
.push_utf8_string(c"digest", c"SHA2-256")?
.build()?;
let plaintext = b"wrap a 32-byte symmetric key here";
let mut enc_ctx = PkeyEncryptCtx::new(&pub_key, Some(&oaep_params))?;
let ct_len = enc_ctx.encrypt_len(plaintext.len())?;
let mut ciphertext = vec![0u8; ct_len];
let written = enc_ctx.encrypt(plaintext, &mut ciphertext)?;
ciphertext.truncate(written);
println!("RSA-OAEP ciphertext: {} bytes", ciphertext.len());
let mut dec_ctx = PkeyDecryptCtx::new(&priv_key, Some(&oaep_params))?;
let mut recovered = vec![0u8; ciphertext.len()];
let n = dec_ctx.decrypt(&ciphertext, &mut recovered)?;
recovered.truncate(n);
assert_eq!(&recovered, plaintext);
println!("RSA-OAEP round-trip: OK");
Ok(())
}