use crate::errors::{BottleError, Result};
use const_oid::{db::rfc5912, ObjectIdentifier};
use der::{Decode, Encode};
use pkcs8::{AlgorithmIdentifierRef, PrivateKeyInfo};
use spki::{AlgorithmIdentifier, SubjectPublicKeyInfo};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum KeyType {
EcdsaP256,
EcdsaP384,
EcdsaP521,
Ed25519,
X25519,
Rsa,
#[cfg(feature = "ml-kem")]
MlKem768,
#[cfg(feature = "ml-kem")]
MlKem1024,
#[cfg(feature = "post-quantum")]
MlDsa44,
#[cfg(feature = "post-quantum")]
MlDsa65,
#[cfg(feature = "post-quantum")]
MlDsa87,
#[cfg(feature = "post-quantum")]
SlhDsa128s,
#[cfg(feature = "post-quantum")]
SlhDsa128f,
#[cfg(feature = "post-quantum")]
SlhDsa192s,
#[cfg(feature = "post-quantum")]
SlhDsa192f,
#[cfg(feature = "post-quantum")]
SlhDsa256s,
#[cfg(feature = "post-quantum")]
SlhDsa256f,
#[cfg(feature = "post-quantum")]
SlhDsaSha2_128s,
#[cfg(feature = "post-quantum")]
SlhDsaSha2_128f,
#[cfg(feature = "post-quantum")]
SlhDsaSha2_192s,
#[cfg(feature = "post-quantum")]
SlhDsaSha2_192f,
#[cfg(feature = "post-quantum")]
SlhDsaSha2_256s,
#[cfg(feature = "post-quantum")]
SlhDsaSha2_256f,
}
impl KeyType {
fn oid(&self) -> ObjectIdentifier {
match self {
KeyType::EcdsaP256 => rfc5912::ID_EC_PUBLIC_KEY, KeyType::EcdsaP384 => rfc5912::ID_EC_PUBLIC_KEY, KeyType::EcdsaP521 => rfc5912::ID_EC_PUBLIC_KEY, KeyType::Ed25519 => ObjectIdentifier::new("1.3.101.112").expect("Invalid Ed25519 OID"), KeyType::X25519 => ObjectIdentifier::new("1.3.101.110").expect("Invalid X25519 OID"), KeyType::Rsa => rfc5912::RSA_ENCRYPTION, #[cfg(feature = "ml-kem")]
KeyType::MlKem768 | KeyType::MlKem1024 => {
ObjectIdentifier::new("1.3.6.1.4.1.2.267.7.6.5").expect("Invalid ML-KEM OID")
}
#[cfg(feature = "post-quantum")]
KeyType::MlDsa44 | KeyType::MlDsa65 | KeyType::MlDsa87 => {
ObjectIdentifier::new("1.3.6.1.4.1.2.267.7.4.4").expect("Invalid ML-DSA OID")
}
#[cfg(feature = "post-quantum")]
KeyType::SlhDsa128s | KeyType::SlhDsa128f | KeyType::SlhDsa192s | KeyType::SlhDsa192f | KeyType::SlhDsa256s | KeyType::SlhDsa256f
| KeyType::SlhDsaSha2_128s | KeyType::SlhDsaSha2_128f | KeyType::SlhDsaSha2_192s | KeyType::SlhDsaSha2_192f | KeyType::SlhDsaSha2_256s | KeyType::SlhDsaSha2_256f => {
ObjectIdentifier::new("1.3.6.1.4.1.2.267.1.16.7").expect("Invalid SLH-DSA OID")
}
}
}
#[allow(dead_code)]
fn curve_oid(&self) -> Option<&'static [u32]> {
match self {
KeyType::EcdsaP256 => Some(&[1, 2, 840, 10045, 3, 1, 7]), KeyType::EcdsaP384 => Some(&[1, 3, 132, 0, 34]), KeyType::EcdsaP521 => Some(&[1, 3, 132, 0, 35]), _ => None,
}
}
}
pub fn marshal_pkix_public_key(public_key_bytes: &[u8]) -> Result<Vec<u8>> {
let key_type = detect_key_type_from_public_key(public_key_bytes)?;
marshal_pkix_public_key_with_type(public_key_bytes, key_type)
}
pub fn marshal_pkix_public_key_with_type(
public_key_bytes: &[u8],
key_type: KeyType,
) -> Result<Vec<u8>> {
match key_type {
KeyType::EcdsaP256 | KeyType::EcdsaP384 | KeyType::EcdsaP521 => {
marshal_ecdsa_pkix(public_key_bytes, key_type)
}
KeyType::Ed25519 => marshal_ed25519_pkix(public_key_bytes),
KeyType::X25519 => marshal_x25519_pkix(public_key_bytes),
KeyType::Rsa => marshal_rsa_pkix(public_key_bytes),
#[cfg(feature = "ml-kem")]
KeyType::MlKem768 | KeyType::MlKem1024 => marshal_mlkem_pkix(public_key_bytes, key_type),
#[cfg(feature = "post-quantum")]
KeyType::MlDsa44 | KeyType::MlDsa65 | KeyType::MlDsa87 => {
marshal_mldsa_pkix(public_key_bytes, key_type)
}
#[cfg(feature = "post-quantum")]
KeyType::SlhDsa128s | KeyType::SlhDsa128f | KeyType::SlhDsa192s | KeyType::SlhDsa192f | KeyType::SlhDsa256s | KeyType::SlhDsa256f
| KeyType::SlhDsaSha2_128s | KeyType::SlhDsaSha2_128f | KeyType::SlhDsaSha2_192s | KeyType::SlhDsaSha2_192f | KeyType::SlhDsaSha2_256s | KeyType::SlhDsaSha2_256f => {
marshal_slhdsa_pkix(public_key_bytes, key_type)
}
}
}
pub fn marshal_pkix_public_key_pem(public_key_bytes: &[u8]) -> Result<String> {
let der = marshal_pkix_public_key(public_key_bytes)?;
let pem = pem::encode(&pem::Pem::new("PUBLIC KEY", der));
Ok(pem)
}
pub fn parse_pkix_public_key(der_bytes: &[u8]) -> Result<Vec<u8>> {
use der::asn1::AnyRef;
use der::asn1::BitString;
let spki: SubjectPublicKeyInfo<AnyRef, BitString> = SubjectPublicKeyInfo::from_der(der_bytes)
.map_err(|e| {
BottleError::Deserialization(format!("Failed to parse PKIX public key: {}", e))
})?;
Ok(spki.subject_public_key.raw_bytes().to_vec())
}
pub fn parse_pkix_public_key_pem(pem_str: &str) -> Result<Vec<u8>> {
let pem = pem::parse(pem_str)
.map_err(|e| BottleError::Deserialization(format!("Failed to parse PEM: {}", e)))?;
parse_pkix_public_key(pem.contents())
}
pub fn marshal_pkcs8_private_key(private_key_bytes: &[u8], key_type: KeyType) -> Result<Vec<u8>> {
match key_type {
KeyType::EcdsaP256 | KeyType::EcdsaP384 | KeyType::EcdsaP521 => {
marshal_ecdsa_pkcs8(private_key_bytes, key_type)
}
KeyType::Ed25519 => marshal_ed25519_pkcs8(private_key_bytes),
KeyType::X25519 => marshal_x25519_pkcs8(private_key_bytes),
KeyType::Rsa => marshal_rsa_pkcs8(private_key_bytes),
#[cfg(feature = "ml-kem")]
KeyType::MlKem768 | KeyType::MlKem1024 => marshal_mlkem_pkcs8(private_key_bytes, key_type),
#[cfg(feature = "post-quantum")]
KeyType::MlDsa44 | KeyType::MlDsa65 | KeyType::MlDsa87 => {
marshal_mldsa_pkcs8(private_key_bytes, key_type)
}
#[cfg(feature = "post-quantum")]
KeyType::SlhDsa128s | KeyType::SlhDsa128f | KeyType::SlhDsa192s | KeyType::SlhDsa192f | KeyType::SlhDsa256s | KeyType::SlhDsa256f
| KeyType::SlhDsaSha2_128s | KeyType::SlhDsaSha2_128f | KeyType::SlhDsaSha2_192s | KeyType::SlhDsaSha2_192f | KeyType::SlhDsaSha2_256s | KeyType::SlhDsaSha2_256f => {
marshal_slhdsa_pkcs8(private_key_bytes, key_type)
}
}
}
pub fn marshal_pkcs8_private_key_pem(
private_key_bytes: &[u8],
key_type: KeyType,
) -> Result<String> {
let der = marshal_pkcs8_private_key(private_key_bytes, key_type)?;
let pem = pem::encode(&pem::Pem::new("PRIVATE KEY", der));
Ok(pem)
}
pub fn parse_pkcs8_private_key(der_bytes: &[u8], key_type: KeyType) -> Result<Vec<u8>> {
match key_type {
KeyType::EcdsaP256 => {
use p256::ecdsa::SigningKey;
use p256::pkcs8::DecodePrivateKey;
let signing_key = SigningKey::from_pkcs8_der(der_bytes).map_err(|e| {
BottleError::Deserialization(format!("Failed to parse P-256 PKCS#8: {}", e))
})?;
Ok(signing_key.to_bytes().to_vec())
}
KeyType::Ed25519 => {
use ed25519_dalek::pkcs8::DecodePrivateKey;
use ed25519_dalek::SigningKey;
let signing_key = SigningKey::from_pkcs8_der(der_bytes).map_err(|e| {
BottleError::Deserialization(format!("Failed to parse Ed25519 PKCS#8: {}", e))
})?;
Ok(signing_key.to_bytes().to_vec())
}
KeyType::X25519 => {
let pkcs8 = PrivateKeyInfo::from_der(der_bytes).map_err(|e| {
BottleError::Deserialization(format!("Failed to parse PKCS#8 private key: {}", e))
})?;
Ok(pkcs8.private_key.to_vec())
}
KeyType::Rsa => {
Err(BottleError::Deserialization(
"RSA PKCS#8 deserialization not yet implemented. Use RsaKey directly.".to_string(),
))
}
_ => {
let pkcs8 = PrivateKeyInfo::from_der(der_bytes).map_err(|e| {
BottleError::Deserialization(format!("Failed to parse PKCS#8 private key: {}", e))
})?;
Ok(pkcs8.private_key.to_vec())
}
}
}
pub fn parse_pkcs8_private_key_pem(pem_str: &str, key_type: KeyType) -> Result<Vec<u8>> {
let pem = pem::parse(pem_str)
.map_err(|e| BottleError::Deserialization(format!("Failed to parse PEM: {}", e)))?;
parse_pkcs8_private_key(pem.contents(), key_type)
}
fn marshal_ecdsa_pkix(public_key_bytes: &[u8], key_type: KeyType) -> Result<Vec<u8>> {
match key_type {
KeyType::EcdsaP256 => {
use p256::pkcs8::EncodePublicKey;
let pub_key = p256::PublicKey::from_sec1_bytes(public_key_bytes).map_err(|e| {
BottleError::Serialization(format!("Failed to create P-256 public key: {}", e))
})?;
pub_key
.to_public_key_der()
.map(|doc| doc.as_bytes().to_vec())
.map_err(|e| {
BottleError::Serialization(format!("Failed to encode P-256 PKIX: {}", e))
})
}
_ => Err(BottleError::UnsupportedAlgorithm),
}
}
fn marshal_ecdsa_pkcs8(private_key_bytes: &[u8], key_type: KeyType) -> Result<Vec<u8>> {
match key_type {
KeyType::EcdsaP256 => {
use p256::ecdsa::SigningKey;
use p256::pkcs8::EncodePrivateKey;
let signing_key = SigningKey::from_bytes(private_key_bytes.into()).map_err(|e| {
BottleError::Serialization(format!("Invalid P-256 private key: {}", e))
})?;
signing_key
.to_pkcs8_der()
.map(|doc| doc.as_bytes().to_vec())
.map_err(|e| {
BottleError::Serialization(format!("Failed to encode P-256 PKCS#8: {}", e))
})
}
_ => Err(BottleError::UnsupportedAlgorithm),
}
}
fn marshal_ed25519_pkix(public_key_bytes: &[u8]) -> Result<Vec<u8>> {
use ed25519_dalek::pkcs8::EncodePublicKey;
use ed25519_dalek::VerifyingKey;
let verifying_key = VerifyingKey::from_bytes(public_key_bytes.try_into().map_err(|_| {
BottleError::Serialization("Invalid Ed25519 public key length".to_string())
})?)
.map_err(|e| BottleError::Serialization(format!("Invalid Ed25519 public key: {}", e)))?;
verifying_key
.to_public_key_der()
.map(|doc| doc.as_bytes().to_vec())
.map_err(|e| BottleError::Serialization(format!("Failed to encode Ed25519 PKIX: {}", e)))
}
fn marshal_ed25519_pkcs8(private_key_bytes: &[u8]) -> Result<Vec<u8>> {
use ed25519_dalek::pkcs8::EncodePrivateKey;
use ed25519_dalek::SigningKey;
let signing_key = SigningKey::from_bytes(private_key_bytes.try_into().map_err(|_| {
BottleError::Serialization("Invalid Ed25519 private key length".to_string())
})?);
signing_key
.to_pkcs8_der()
.map(|doc| doc.as_bytes().to_vec())
.map_err(|e| BottleError::Serialization(format!("Failed to encode Ed25519 PKCS#8: {}", e)))
}
fn marshal_x25519_pkix(public_key_bytes: &[u8]) -> Result<Vec<u8>> {
if public_key_bytes.len() != 32 {
return Err(BottleError::Serialization(
"Invalid X25519 public key length".to_string(),
));
}
use der::asn1::OctetString;
let key_octets = OctetString::new(public_key_bytes).map_err(|e| {
BottleError::Serialization(format!("Failed to create X25519 octet string: {}", e))
})?;
use der::asn1::AnyRef;
let algorithm = AlgorithmIdentifier {
oid: KeyType::X25519.oid(),
parameters: None::<AnyRef>,
};
let spki = SubjectPublicKeyInfo {
algorithm,
subject_public_key: key_octets,
};
spki.to_der()
.map_err(|e| BottleError::Serialization(format!("Failed to encode X25519 PKIX: {}", e)))
}
fn marshal_x25519_pkcs8(private_key_bytes: &[u8]) -> Result<Vec<u8>> {
if private_key_bytes.len() != 32 {
return Err(BottleError::Serialization(
"Invalid X25519 private key length".to_string(),
));
}
let algorithm = AlgorithmIdentifierRef {
oid: KeyType::X25519.oid(),
parameters: None,
};
let pkcs8 = PrivateKeyInfo::new(algorithm, private_key_bytes);
pkcs8
.to_der()
.map_err(|e| BottleError::Serialization(format!("Failed to encode X25519 PKCS#8: {}", e)))
}
fn marshal_rsa_pkix(_public_key_bytes: &[u8]) -> Result<Vec<u8>> {
Err(BottleError::Serialization(
"RSA PKIX serialization requires RsaPublicKey reference. Use PKCS#8 serialization or provide RsaPublicKey directly.".to_string()
))
}
fn marshal_rsa_pkcs8(_private_key_bytes: &[u8]) -> Result<Vec<u8>> {
Err(BottleError::Serialization(
"RSA PKCS#8 serialization requires RsaPrivateKey reference. Use RsaKey directly or provide RsaPrivateKey.".to_string()
))
}
#[cfg(feature = "ml-kem")]
fn marshal_mlkem_pkix(public_key_bytes: &[u8], key_type: KeyType) -> Result<Vec<u8>> {
use der::asn1::BitString;
let key_bits = BitString::from_bytes(public_key_bytes).map_err(|e| {
BottleError::Serialization(format!("Failed to create ML-KEM bit string: {}", e))
})?;
use der::asn1::AnyRef;
let algorithm = AlgorithmIdentifier {
oid: key_type.oid(),
parameters: Some(AnyRef::NULL),
};
let spki = SubjectPublicKeyInfo {
algorithm,
subject_public_key: key_bits,
};
spki.to_der()
.map_err(|e| BottleError::Serialization(format!("Failed to encode ML-KEM PKIX: {}", e)))
}
#[cfg(feature = "ml-kem")]
fn marshal_mlkem_pkcs8(private_key_bytes: &[u8], key_type: KeyType) -> Result<Vec<u8>> {
use der::asn1::OctetString;
let _key_octets = OctetString::new(private_key_bytes).map_err(|e| {
BottleError::Serialization(format!("Failed to create ML-KEM octet string: {}", e))
})?;
use der::asn1::AnyRef;
let algorithm = AlgorithmIdentifierRef {
oid: key_type.oid(),
parameters: Some(AnyRef::NULL),
};
let pkcs8 = PrivateKeyInfo::new(algorithm, private_key_bytes);
pkcs8
.to_der()
.map_err(|e| BottleError::Serialization(format!("Failed to encode ML-KEM PKCS#8: {}", e)))
}
#[cfg(feature = "post-quantum")]
fn marshal_mldsa_pkix(public_key_bytes: &[u8], key_type: KeyType) -> Result<Vec<u8>> {
use der::asn1::BitString;
let key_bits = BitString::from_bytes(public_key_bytes).map_err(|e| {
BottleError::Serialization(format!("Failed to create ML-DSA bit string: {}", e))
})?;
use der::asn1::AnyRef;
let algorithm = AlgorithmIdentifier {
oid: key_type.oid(),
parameters: Some(AnyRef::NULL),
};
let spki = SubjectPublicKeyInfo {
algorithm,
subject_public_key: key_bits,
};
spki.to_der()
.map_err(|e| BottleError::Serialization(format!("Failed to encode ML-DSA PKIX: {}", e)))
}
#[cfg(feature = "post-quantum")]
fn marshal_mldsa_pkcs8(private_key_bytes: &[u8], key_type: KeyType) -> Result<Vec<u8>> {
use der::asn1::OctetString;
let _key_octets = OctetString::new(private_key_bytes).map_err(|e| {
BottleError::Serialization(format!("Failed to create ML-DSA octet string: {}", e))
})?;
use der::asn1::AnyRef;
let algorithm = AlgorithmIdentifierRef {
oid: key_type.oid(),
parameters: Some(AnyRef::NULL),
};
let pkcs8 = PrivateKeyInfo::new(algorithm, private_key_bytes);
pkcs8
.to_der()
.map_err(|e| BottleError::Serialization(format!("Failed to encode ML-DSA PKCS#8: {}", e)))
}
#[cfg(feature = "post-quantum")]
fn marshal_slhdsa_pkix(public_key_bytes: &[u8], key_type: KeyType) -> Result<Vec<u8>> {
use der::asn1::BitString;
let key_bits = BitString::from_bytes(public_key_bytes).map_err(|e| {
BottleError::Serialization(format!("Failed to create SLH-DSA bit string: {}", e))
})?;
use der::asn1::AnyRef;
let algorithm = AlgorithmIdentifier {
oid: key_type.oid(),
parameters: Some(AnyRef::NULL),
};
let spki = SubjectPublicKeyInfo {
algorithm,
subject_public_key: key_bits,
};
spki.to_der()
.map_err(|e| BottleError::Serialization(format!("Failed to encode SLH-DSA PKIX: {}", e)))
}
#[cfg(feature = "post-quantum")]
fn marshal_slhdsa_pkcs8(private_key_bytes: &[u8], key_type: KeyType) -> Result<Vec<u8>> {
use der::asn1::OctetString;
let _key_octets = OctetString::new(private_key_bytes).map_err(|e| {
BottleError::Serialization(format!("Failed to create SLH-DSA octet string: {}", e))
})?;
use der::asn1::AnyRef;
let algorithm = AlgorithmIdentifierRef {
oid: key_type.oid(),
parameters: Some(AnyRef::NULL),
};
let pkcs8 = PrivateKeyInfo::new(algorithm, private_key_bytes);
pkcs8
.to_der()
.map_err(|e| BottleError::Serialization(format!("Failed to encode SLH-DSA PKCS#8: {}", e)))
}
fn detect_key_type_from_public_key(public_key_bytes: &[u8]) -> Result<KeyType> {
match public_key_bytes.len() {
32 => {
#[cfg(feature = "post-quantum")]
{
Ok(KeyType::Ed25519)
}
#[cfg(not(feature = "post-quantum"))]
{
Ok(KeyType::Ed25519)
}
}
65 => {
if public_key_bytes[0] == 0x04 {
Ok(KeyType::EcdsaP256)
} else {
Err(BottleError::InvalidKeyType)
}
}
97 => {
if public_key_bytes[0] == 0x04 {
Ok(KeyType::EcdsaP384)
} else {
Err(BottleError::InvalidKeyType)
}
}
133 => {
if public_key_bytes[0] == 0x04 {
Ok(KeyType::EcdsaP521)
} else {
Err(BottleError::InvalidKeyType)
}
}
1184 => {
#[cfg(feature = "ml-kem")]
{
Ok(KeyType::MlKem768)
}
#[cfg(not(feature = "ml-kem"))]
{
Err(BottleError::InvalidKeyType)
}
}
1568 => {
#[cfg(feature = "ml-kem")]
{
Ok(KeyType::MlKem1024)
}
#[cfg(not(feature = "ml-kem"))]
{
Err(BottleError::InvalidKeyType)
}
}
1312 => {
#[cfg(feature = "post-quantum")]
{
Ok(KeyType::MlDsa44)
}
#[cfg(not(feature = "post-quantum"))]
{
Err(BottleError::InvalidKeyType)
}
}
1952 => {
#[cfg(feature = "post-quantum")]
{
Ok(KeyType::MlDsa65)
}
#[cfg(not(feature = "post-quantum"))]
{
Err(BottleError::InvalidKeyType)
}
}
2592 => {
#[cfg(feature = "post-quantum")]
{
Ok(KeyType::MlDsa87)
}
#[cfg(not(feature = "post-quantum"))]
{
Err(BottleError::InvalidKeyType)
}
}
48 => {
#[cfg(feature = "post-quantum")]
{
Ok(KeyType::SlhDsa192s)
}
#[cfg(not(feature = "post-quantum"))]
{
Err(BottleError::InvalidKeyType)
}
}
64 => {
#[cfg(feature = "post-quantum")]
{
Ok(KeyType::SlhDsa256s)
}
#[cfg(not(feature = "post-quantum"))]
{
Err(BottleError::InvalidKeyType)
}
}
_ => Err(BottleError::InvalidKeyType),
}
}