use core::str::FromStr;
use der::{SecretDocument, asn1::OctetStringRef};
#[cfg(feature = "pkcs8")]
use crate::ALGORITHM_OID;
use crate::{PublicKey, ScalarValue};
use elliptic_curve::{Error, Generate, array::typenum::Unsigned, zeroize::Zeroizing};
#[cfg(feature = "pkcs8")]
use pkcs8::{
AssociatedOid, DecodePrivateKey, EncodePrivateKey, ObjectIdentifier,
spki::{AlgorithmIdentifier, AssociatedAlgorithmIdentifier},
};
#[cfg(feature = "arithmetic")]
use crate::{BignP256, FieldBytes, NonZeroScalar, Result, elliptic_curve::rand_core::TryCryptoRng};
#[cfg(feature = "arithmetic")]
#[derive(Copy, Clone, Debug)]
pub struct SecretKey {
inner: ScalarValue,
}
impl SecretKey {
const MIN_SIZE: usize = 24;
pub fn as_scalar_primitive(&self) -> &ScalarValue {
&self.inner
}
#[cfg(feature = "arithmetic")]
pub fn to_nonzero_scalar(&self) -> NonZeroScalar {
(*self).into()
}
#[cfg(feature = "arithmetic")]
pub fn public_key(&self) -> PublicKey {
PublicKey::from_secret_scalar(&self.to_nonzero_scalar())
}
pub fn from_bytes(bytes: &FieldBytes) -> Result<Self> {
let inner = ScalarValue::from_bytes(bytes).into_option().ok_or(Error)?;
if inner.is_zero().into() {
return Err(Error);
}
Ok(Self { inner })
}
pub fn from_slice(slice: &[u8]) -> Result<Self> {
if slice.len() == <BignP256 as elliptic_curve::Curve>::FieldBytesSize::USIZE {
Self::from_bytes(&FieldBytes::try_from(slice).map_err(|_| Error)?)
} else if (Self::MIN_SIZE..<BignP256 as elliptic_curve::Curve>::FieldBytesSize::USIZE)
.contains(&slice.len())
{
let mut bytes = Zeroizing::new(FieldBytes::default());
let offset = <BignP256 as elliptic_curve::Curve>::FieldBytesSize::USIZE
.saturating_sub(slice.len());
bytes[offset..].copy_from_slice(slice);
Self::from_bytes(&bytes)
} else {
Err(Error)
}
}
pub fn to_bytes(&self) -> FieldBytes {
self.inner.to_bytes()
}
}
#[cfg(feature = "pkcs8")]
impl AssociatedAlgorithmIdentifier for SecretKey {
type Params = ObjectIdentifier;
const ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> = AlgorithmIdentifier {
oid: ALGORITHM_OID,
parameters: Some(BignP256::OID),
};
}
impl From<SecretKey> for NonZeroScalar {
fn from(secret_key: SecretKey) -> NonZeroScalar {
secret_key.to_nonzero_scalar()
}
}
#[cfg(feature = "arithmetic")]
impl From<NonZeroScalar> for SecretKey {
fn from(scalar: NonZeroScalar) -> SecretKey {
SecretKey::from(&scalar)
}
}
#[cfg(feature = "arithmetic")]
impl From<&NonZeroScalar> for SecretKey {
fn from(scalar: &NonZeroScalar) -> SecretKey {
SecretKey {
inner: scalar.into(),
}
}
}
impl Generate for SecretKey {
fn try_generate_from_rng<R: TryCryptoRng + ?Sized>(
rng: &mut R,
) -> core::result::Result<Self, R::Error> {
Ok(Self {
inner: ScalarValue::try_generate_from_rng(rng)?,
})
}
}
#[cfg(feature = "pkcs8")]
impl TryFrom<pkcs8::PrivateKeyInfoRef<'_>> for SecretKey {
type Error = pkcs8::Error;
fn try_from(private_key_info: pkcs8::PrivateKeyInfoRef<'_>) -> pkcs8::Result<Self> {
private_key_info
.algorithm
.assert_oids(ALGORITHM_OID, BignP256::OID)?;
Self::from_slice(private_key_info.private_key.as_bytes())
.map_err(|_| pkcs8::KeyError::Invalid.into())
}
}
#[cfg(feature = "pem")]
impl FromStr for SecretKey {
type Err = Error;
fn from_str(s: &str) -> core::result::Result<Self, Error> {
Self::from_pkcs8_pem(s).map_err(|_| Error)
}
}
#[cfg(feature = "pkcs8")]
impl EncodePrivateKey for SecretKey {
fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
let algorithm_identifier = pkcs8::AlgorithmIdentifierRef {
oid: ALGORITHM_OID,
parameters: Some((&BignP256::OID).into()),
};
let ec_private_key = self.to_bytes();
let pkcs8_key = pkcs8::PrivateKeyInfoRef::new(
algorithm_identifier,
OctetStringRef::new(&ec_private_key)?,
);
Ok(SecretDocument::encode_msg(&pkcs8_key)?)
}
}