use crate::{Result, SodiumError};
use std::convert::TryFrom;
pub const PUBLICKEYBYTES: usize =
libsodium_sys::crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES as usize;
pub const SECRETKEYBYTES: usize =
libsodium_sys::crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES as usize;
pub const NONCEBYTES: usize =
libsodium_sys::crypto_box_curve25519xsalsa20poly1305_NONCEBYTES as usize;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Nonce([u8; NONCEBYTES]);
impl Nonce {
pub fn generate() -> Self {
let mut bytes = [0u8; NONCEBYTES];
crate::random::fill_bytes(&mut bytes);
Self(bytes)
}
pub fn from_bytes(bytes: [u8; NONCEBYTES]) -> Self {
Self(bytes)
}
pub fn try_from_slice(bytes: &[u8]) -> Result<Self> {
if bytes.len() != NONCEBYTES {
return Err(SodiumError::InvalidNonce(format!(
"nonce must be exactly {NONCEBYTES} bytes"
)));
}
let mut nonce_bytes = [0u8; NONCEBYTES];
nonce_bytes.copy_from_slice(bytes);
Ok(Self(nonce_bytes))
}
pub fn as_bytes(&self) -> &[u8; NONCEBYTES] {
&self.0
}
pub fn as_bytes_mut(&mut self) -> &mut [u8; NONCEBYTES] {
&mut self.0
}
pub const fn from_bytes_exact(bytes: [u8; NONCEBYTES]) -> Self {
Self(bytes)
}
}
impl AsRef<[u8]> for Nonce {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsRef<Nonce> for Nonce {
fn as_ref(&self) -> &Nonce {
self
}
}
impl TryFrom<&[u8]> for Nonce {
type Error = SodiumError;
fn try_from(slice: &[u8]) -> std::result::Result<Self, Self::Error> {
Self::try_from_slice(slice)
}
}
impl From<[u8; NONCEBYTES]> for Nonce {
fn from(bytes: [u8; NONCEBYTES]) -> Self {
Self(bytes)
}
}
impl From<Nonce> for [u8; NONCEBYTES] {
fn from(nonce: Nonce) -> [u8; NONCEBYTES] {
nonce.0
}
}
pub const MACBYTES: usize = libsodium_sys::crypto_box_curve25519xsalsa20poly1305_MACBYTES as usize;
pub const SEEDBYTES: usize =
libsodium_sys::crypto_box_curve25519xsalsa20poly1305_SEEDBYTES as usize;
pub const BEFORENMBYTES: usize =
libsodium_sys::crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES as usize;
pub const ZEROBYTES: usize =
libsodium_sys::crypto_box_curve25519xsalsa20poly1305_ZEROBYTES as usize;
pub const BOXZEROBYTES: usize =
libsodium_sys::crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES as usize;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PublicKey([u8; PUBLICKEYBYTES]);
impl PublicKey {
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
if bytes.len() != PUBLICKEYBYTES {
return Err(SodiumError::InvalidInput(format!(
"public key must be exactly {PUBLICKEYBYTES} bytes"
)));
}
let mut pk = [0u8; PUBLICKEYBYTES];
pk.copy_from_slice(bytes);
Ok(Self(pk))
}
pub fn as_bytes(&self) -> &[u8; PUBLICKEYBYTES] {
&self.0
}
pub const fn from_bytes_exact(bytes: [u8; PUBLICKEYBYTES]) -> Self {
Self(bytes)
}
}
impl AsRef<PublicKey> for PublicKey {
fn as_ref(&self) -> &PublicKey {
self
}
}
impl AsRef<[u8]> for PublicKey {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl TryFrom<&[u8]> for PublicKey {
type Error = SodiumError;
fn try_from(slice: &[u8]) -> std::result::Result<Self, Self::Error> {
Self::from_bytes(slice)
}
}
impl From<[u8; PUBLICKEYBYTES]> for PublicKey {
fn from(bytes: [u8; PUBLICKEYBYTES]) -> Self {
Self(bytes)
}
}
impl From<PublicKey> for [u8; PUBLICKEYBYTES] {
fn from(key: PublicKey) -> [u8; PUBLICKEYBYTES] {
key.0
}
}
#[derive(Debug, Clone, Eq, PartialEq, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
pub struct SecretKey([u8; SECRETKEYBYTES]);
impl SecretKey {
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
if bytes.len() != SECRETKEYBYTES {
return Err(SodiumError::InvalidInput(format!(
"secret key must be exactly {SECRETKEYBYTES} bytes"
)));
}
let mut sk = [0u8; SECRETKEYBYTES];
sk.copy_from_slice(bytes);
Ok(Self(sk))
}
pub fn as_bytes(&self) -> &[u8; SECRETKEYBYTES] {
&self.0
}
pub const fn from_bytes_exact(bytes: [u8; SECRETKEYBYTES]) -> Self {
Self(bytes)
}
}
impl AsRef<SecretKey> for SecretKey {
fn as_ref(&self) -> &SecretKey {
self
}
}
impl AsRef<[u8]> for SecretKey {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl TryFrom<&[u8]> for SecretKey {
type Error = SodiumError;
fn try_from(slice: &[u8]) -> std::result::Result<Self, Self::Error> {
Self::from_bytes(slice)
}
}
impl From<[u8; SECRETKEYBYTES]> for SecretKey {
fn from(bytes: [u8; SECRETKEYBYTES]) -> Self {
Self(bytes)
}
}
impl From<SecretKey> for [u8; SECRETKEYBYTES] {
fn from(key: SecretKey) -> [u8; SECRETKEYBYTES] {
key.0
}
}
#[derive(Debug, Clone, Eq, PartialEq, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
pub struct PrecomputedKey([u8; BEFORENMBYTES]);
impl PrecomputedKey {
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
if bytes.len() != BEFORENMBYTES {
return Err(SodiumError::InvalidInput(format!(
"precomputed key must be exactly {BEFORENMBYTES} bytes"
)));
}
let mut k = [0u8; BEFORENMBYTES];
k.copy_from_slice(bytes);
Ok(Self(k))
}
pub fn as_bytes(&self) -> &[u8; BEFORENMBYTES] {
&self.0
}
pub const fn from_bytes_exact(bytes: [u8; BEFORENMBYTES]) -> Self {
Self(bytes)
}
}
impl AsRef<PrecomputedKey> for PrecomputedKey {
fn as_ref(&self) -> &PrecomputedKey {
self
}
}
impl AsRef<[u8]> for PrecomputedKey {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl TryFrom<&[u8]> for PrecomputedKey {
type Error = SodiumError;
fn try_from(slice: &[u8]) -> std::result::Result<Self, Self::Error> {
Self::from_bytes(slice)
}
}
impl From<[u8; BEFORENMBYTES]> for PrecomputedKey {
fn from(bytes: [u8; BEFORENMBYTES]) -> Self {
Self(bytes)
}
}
impl From<PrecomputedKey> for [u8; BEFORENMBYTES] {
fn from(key: PrecomputedKey) -> [u8; BEFORENMBYTES] {
key.0
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct KeyPair {
pub public_key: PublicKey,
pub secret_key: SecretKey,
}
impl KeyPair {
pub fn new(public_key: PublicKey, secret_key: SecretKey) -> Self {
Self {
public_key,
secret_key,
}
}
pub fn generate() -> Result<Self> {
let mut pk = [0u8; PUBLICKEYBYTES];
let mut sk = [0u8; SECRETKEYBYTES];
let result = unsafe {
libsodium_sys::crypto_box_curve25519xsalsa20poly1305_keypair(
pk.as_mut_ptr(),
sk.as_mut_ptr(),
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"keypair generation failed".into(),
));
}
Ok(Self {
public_key: PublicKey(pk),
secret_key: SecretKey(sk),
})
}
pub fn from_seed(seed: &[u8]) -> Result<Self> {
if seed.len() != SEEDBYTES {
return Err(SodiumError::InvalidInput(format!(
"seed must be exactly {SEEDBYTES} bytes"
)));
}
let mut pk = [0u8; PUBLICKEYBYTES];
let mut sk = [0u8; SECRETKEYBYTES];
unsafe {
let ret = libsodium_sys::crypto_box_curve25519xsalsa20poly1305_seed_keypair(
pk.as_mut_ptr(),
sk.as_mut_ptr(),
seed.as_ptr(),
);
if ret != 0 {
return Err(SodiumError::OperationError(
"key generation from seed failed".into(),
));
}
}
Ok(Self {
public_key: PublicKey(pk),
secret_key: SecretKey(sk),
})
}
pub fn encrypt(
&self,
message: &[u8],
nonce: impl AsRef<Nonce>,
recipient_public_key: impl AsRef<PublicKey>,
) -> Result<Vec<u8>> {
encrypt(message, nonce, recipient_public_key, &self.secret_key)
}
pub fn decrypt(
&self,
ciphertext: &[u8],
nonce: impl AsRef<Nonce>,
sender_public_key: impl AsRef<PublicKey>,
) -> Result<Vec<u8>> {
decrypt(ciphertext, nonce, sender_public_key, &self.secret_key)
}
}
impl From<(PublicKey, SecretKey)> for KeyPair {
fn from((public_key, secret_key): (PublicKey, SecretKey)) -> Self {
Self {
public_key,
secret_key,
}
}
}
impl From<KeyPair> for (PublicKey, SecretKey) {
fn from(val: KeyPair) -> Self {
(val.public_key, val.secret_key)
}
}
pub fn beforenm(public_key: &PublicKey, secret_key: &SecretKey) -> Result<PrecomputedKey> {
let mut k = [0u8; BEFORENMBYTES];
let result = unsafe {
libsodium_sys::crypto_box_curve25519xsalsa20poly1305_beforenm(
k.as_mut_ptr(),
public_key.as_bytes().as_ptr(),
secret_key.as_bytes().as_ptr(),
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"precomputed key generation failed".into(),
));
}
Ok(PrecomputedKey(k))
}
pub fn encrypt_afternm(
message: &[u8],
nonce: &Nonce,
precomputed_key: &PrecomputedKey,
) -> Result<Vec<u8>> {
let ciphertext_len = message.len() + MACBYTES;
let mut ciphertext = vec![0u8; ciphertext_len];
let result = unsafe {
libsodium_sys::crypto_box_easy_afternm(
ciphertext.as_mut_ptr(),
message.as_ptr(),
message.len() as u64,
nonce.as_bytes().as_ptr(),
precomputed_key.as_bytes().as_ptr(),
)
};
if result != 0 {
return Err(SodiumError::EncryptionError(
"XSalsa20-Poly1305 encryption with precomputed key failed".into(),
));
}
Ok(ciphertext)
}
pub fn decrypt_afternm(
ciphertext: &[u8],
nonce: &Nonce,
precomputed_key: &PrecomputedKey,
) -> Result<Vec<u8>> {
if ciphertext.len() < MACBYTES {
return Err(SodiumError::InvalidInput(format!(
"ciphertext must be at least {MACBYTES} bytes"
)));
}
let mut message = vec![0u8; ciphertext.len() - MACBYTES];
let result = unsafe {
libsodium_sys::crypto_box_open_easy_afternm(
message.as_mut_ptr(),
ciphertext.as_ptr(),
ciphertext.len() as u64,
nonce.as_bytes().as_ptr(),
precomputed_key.as_bytes().as_ptr(),
)
};
if result != 0 {
return Err(SodiumError::DecryptionError(
"XSalsa20-Poly1305 authentication with precomputed key failed".into(),
));
}
Ok(message)
}
pub fn encrypt(
message: &[u8],
nonce: impl AsRef<Nonce>,
recipient_pk: impl AsRef<PublicKey>,
sender_sk: impl AsRef<SecretKey>,
) -> Result<Vec<u8>> {
let nonce = nonce.as_ref();
let recipient_pk = recipient_pk.as_ref();
let sender_sk = sender_sk.as_ref();
let ciphertext_len = message.len() + MACBYTES;
let mut ciphertext = vec![0u8; ciphertext_len];
let result = unsafe {
libsodium_sys::crypto_box_easy(
ciphertext.as_mut_ptr(),
message.as_ptr(),
message.len() as u64,
nonce.as_bytes().as_ptr(),
recipient_pk.as_bytes().as_ptr(),
sender_sk.as_bytes().as_ptr(),
)
};
if result != 0 {
return Err(SodiumError::EncryptionError(
"XSalsa20-Poly1305 encryption failed".into(),
));
}
Ok(ciphertext)
}
pub fn decrypt(
ciphertext: &[u8],
nonce: impl AsRef<Nonce>,
sender_pk: impl AsRef<PublicKey>,
recipient_sk: impl AsRef<SecretKey>,
) -> Result<Vec<u8>> {
if ciphertext.len() < MACBYTES {
return Err(SodiumError::InvalidInput(format!(
"ciphertext must be at least {MACBYTES} bytes"
)));
}
let nonce = nonce.as_ref();
let sender_pk = sender_pk.as_ref();
let recipient_sk = recipient_sk.as_ref();
let mut message = vec![0u8; ciphertext.len() - MACBYTES];
let result = unsafe {
libsodium_sys::crypto_box_open_easy(
message.as_mut_ptr(),
ciphertext.as_ptr(),
ciphertext.len() as u64,
nonce.as_bytes().as_ptr(),
sender_pk.as_bytes().as_ptr(),
recipient_sk.as_bytes().as_ptr(),
)
};
if result != 0 {
return Err(SodiumError::DecryptionError(
"XSalsa20-Poly1305 authentication failed".into(),
));
}
Ok(message)
}
pub fn encrypt_nacl(
padded_message: &[u8],
nonce: &Nonce,
recipient_pk: &PublicKey,
sender_sk: &SecretKey,
) -> Result<Vec<u8>> {
if padded_message.len() < ZEROBYTES {
return Err(SodiumError::InvalidInput(format!(
"padded message must be at least {ZEROBYTES} bytes"
)));
}
if padded_message.iter().take(ZEROBYTES).any(|&byte| byte != 0) {
return Err(SodiumError::InvalidInput(format!(
"first {ZEROBYTES} bytes of padded message must be zero"
)));
}
let mut ciphertext = vec![0u8; padded_message.len()];
let result = unsafe {
libsodium_sys::crypto_box_curve25519xsalsa20poly1305(
ciphertext.as_mut_ptr(),
padded_message.as_ptr(),
padded_message.len() as u64,
nonce.as_bytes().as_ptr(),
recipient_pk.as_bytes().as_ptr(),
sender_sk.as_bytes().as_ptr(),
)
};
if result != 0 {
return Err(SodiumError::OperationError("encryption failed".into()));
}
Ok(ciphertext)
}
pub fn decrypt_nacl(
padded_ciphertext: &[u8],
nonce: &Nonce,
sender_pk: &PublicKey,
recipient_sk: &SecretKey,
) -> Result<Vec<u8>> {
if padded_ciphertext.len() < BOXZEROBYTES {
return Err(SodiumError::InvalidInput(format!(
"padded ciphertext must be at least {BOXZEROBYTES} bytes"
)));
}
if padded_ciphertext
.iter()
.take(BOXZEROBYTES)
.any(|&byte| byte != 0)
{
return Err(SodiumError::InvalidInput(format!(
"first {BOXZEROBYTES} bytes of padded ciphertext must be zero"
)));
}
let mut message = vec![0u8; padded_ciphertext.len()];
let result = unsafe {
libsodium_sys::crypto_box_curve25519xsalsa20poly1305_open(
message.as_mut_ptr(),
padded_ciphertext.as_ptr(),
padded_ciphertext.len() as u64,
nonce.as_bytes().as_ptr(),
sender_pk.as_bytes().as_ptr(),
recipient_sk.as_bytes().as_ptr(),
)
};
if result != 0 {
return Err(SodiumError::OperationError("decryption failed".into()));
}
Ok(message)
}
pub fn encrypt_nacl_afternm(
padded_message: &[u8],
nonce: &Nonce,
precomputed_key: &PrecomputedKey,
) -> Result<Vec<u8>> {
if padded_message.len() < ZEROBYTES {
return Err(SodiumError::InvalidInput(format!(
"padded message must be at least {ZEROBYTES} bytes"
)));
}
if padded_message.iter().take(ZEROBYTES).any(|&byte| byte != 0) {
return Err(SodiumError::InvalidInput(format!(
"first {ZEROBYTES} bytes of padded message must be zero"
)));
}
let mut ciphertext = vec![0u8; padded_message.len()];
let result = unsafe {
libsodium_sys::crypto_box_curve25519xsalsa20poly1305_afternm(
ciphertext.as_mut_ptr(),
padded_message.as_ptr(),
padded_message.len() as u64,
nonce.as_bytes().as_ptr(),
precomputed_key.as_bytes().as_ptr(),
)
};
if result != 0 {
return Err(SodiumError::OperationError("encryption failed".into()));
}
Ok(ciphertext)
}
pub fn decrypt_nacl_afternm(
padded_ciphertext: &[u8],
nonce: &Nonce,
precomputed_key: &PrecomputedKey,
) -> Result<Vec<u8>> {
if padded_ciphertext.len() < BOXZEROBYTES {
return Err(SodiumError::InvalidInput(format!(
"padded ciphertext must be at least {BOXZEROBYTES} bytes"
)));
}
if padded_ciphertext
.iter()
.take(BOXZEROBYTES)
.any(|&byte| byte != 0)
{
return Err(SodiumError::InvalidInput(format!(
"first {BOXZEROBYTES} bytes of padded ciphertext must be zero"
)));
}
let mut message = vec![0u8; padded_ciphertext.len()];
let result = unsafe {
libsodium_sys::crypto_box_curve25519xsalsa20poly1305_open_afternm(
message.as_mut_ptr(),
padded_ciphertext.as_ptr(),
padded_ciphertext.len() as u64,
nonce.as_bytes().as_ptr(),
precomputed_key.as_bytes().as_ptr(),
)
};
if result != 0 {
return Err(SodiumError::OperationError("decryption failed".into()));
}
Ok(message)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_keypair_generation() {
let keypair = KeyPair::generate().unwrap();
assert_eq!(keypair.public_key.as_bytes().len(), PUBLICKEYBYTES);
assert_eq!(keypair.secret_key.as_bytes().len(), SECRETKEYBYTES);
}
#[test]
fn test_seed_keypair() {
let mut seed = [0u8; SEEDBYTES];
crate::random::fill_bytes(&mut seed);
let keypair1 = KeyPair::from_seed(&seed).unwrap();
let keypair2 = KeyPair::from_seed(&seed).unwrap();
assert_eq!(
keypair1.public_key.as_bytes(),
keypair2.public_key.as_bytes()
);
assert_eq!(
keypair1.secret_key.as_bytes(),
keypair2.secret_key.as_bytes()
);
}
#[test]
fn test_encrypt_decrypt() {
let alice = KeyPair::generate().unwrap();
let bob = KeyPair::generate().unwrap();
let nonce = Nonce::generate();
let message = b"Hello, XSalsa20-Poly1305!";
let ciphertext = encrypt(message, &nonce, &bob.public_key, &alice.secret_key).unwrap();
let decrypted = decrypt(&ciphertext, &nonce, &alice.public_key, &bob.secret_key).unwrap();
assert_eq!(decrypted, message);
}
#[test]
fn test_beforenm_afternm() {
let alice = KeyPair::generate().unwrap();
let bob = KeyPair::generate().unwrap();
let nonce = Nonce::generate();
let message = b"Hello, precomputed key!";
let alice_precomputed = beforenm(&bob.public_key, &alice.secret_key).unwrap();
let bob_precomputed = beforenm(&alice.public_key, &bob.secret_key).unwrap();
let ciphertext = encrypt_afternm(message, &nonce, &alice_precomputed).unwrap();
let decrypted = decrypt_afternm(&ciphertext, &nonce, &bob_precomputed).unwrap();
assert_eq!(decrypted, message);
}
#[test]
fn test_nacl_compatibility() {
let alice = KeyPair::generate().unwrap();
let bob = KeyPair::generate().unwrap();
let nonce = Nonce::generate();
let message = b"Hello, NaCl!";
let mut padded_message = vec![0u8; ZEROBYTES + message.len()];
padded_message[ZEROBYTES..].copy_from_slice(message);
let padded_ciphertext =
encrypt_nacl(&padded_message, &nonce, &bob.public_key, &alice.secret_key).unwrap();
let decrypted_padded = decrypt_nacl(
&padded_ciphertext,
&nonce,
&alice.public_key,
&bob.secret_key,
)
.unwrap();
assert_eq!(decrypted_padded, padded_message);
}
#[test]
fn test_nacl_compatibility_afternm() {
let alice = KeyPair::generate().unwrap();
let bob = KeyPair::generate().unwrap();
let nonce = Nonce::generate();
let message = b"Hello, NaCl afternm!";
let mut padded_message = vec![0u8; ZEROBYTES + message.len()];
padded_message[ZEROBYTES..].copy_from_slice(message);
let alice_precomputed = beforenm(&bob.public_key, &alice.secret_key).unwrap();
let bob_precomputed = beforenm(&alice.public_key, &bob.secret_key).unwrap();
let padded_ciphertext =
encrypt_nacl_afternm(&padded_message, &nonce, &alice_precomputed).unwrap();
let decrypted_padded =
decrypt_nacl_afternm(&padded_ciphertext, &nonce, &bob_precomputed).unwrap();
assert_eq!(decrypted_padded, padded_message);
}
#[test]
fn test_nonce_traits() {
let bytes = [0x42; NONCEBYTES];
let nonce = Nonce::try_from(&bytes[..]).unwrap();
assert_eq!(nonce.as_bytes(), &bytes);
let invalid_bytes = [0x42; NONCEBYTES - 1];
assert!(Nonce::try_from(&invalid_bytes[..]).is_err());
let bytes = [0x43; NONCEBYTES];
let nonce2 = Nonce::from(bytes);
assert_eq!(nonce2.as_bytes(), &bytes);
let extracted: [u8; NONCEBYTES] = nonce2.into();
assert_eq!(extracted, bytes);
let nonce3 = Nonce::generate();
let slice_ref: &[u8] = nonce3.as_ref();
assert_eq!(slice_ref.len(), NONCEBYTES);
}
#[test]
fn test_publickey_traits() {
let bytes = [0x42; PUBLICKEYBYTES];
let key = PublicKey::try_from(&bytes[..]).unwrap();
assert_eq!(key.as_bytes(), &bytes);
let invalid_bytes = [0x42; PUBLICKEYBYTES - 1];
assert!(PublicKey::try_from(&invalid_bytes[..]).is_err());
let bytes = [0x43; PUBLICKEYBYTES];
let key2 = PublicKey::from(bytes);
assert_eq!(key2.as_bytes(), &bytes);
let extracted: [u8; PUBLICKEYBYTES] = key2.into();
assert_eq!(extracted, bytes);
let keypair = KeyPair::generate().unwrap();
let slice_ref: &[u8] = keypair.public_key.as_ref();
assert_eq!(slice_ref.len(), PUBLICKEYBYTES);
}
#[test]
fn test_secretkey_traits() {
let bytes = [0x42; SECRETKEYBYTES];
let key = SecretKey::try_from(&bytes[..]).unwrap();
assert_eq!(key.as_bytes(), &bytes);
let invalid_bytes = [0x42; SECRETKEYBYTES - 1];
assert!(SecretKey::try_from(&invalid_bytes[..]).is_err());
let bytes = [0x43; SECRETKEYBYTES];
let key2 = SecretKey::from(bytes);
assert_eq!(key2.as_bytes(), &bytes);
let extracted: [u8; SECRETKEYBYTES] = key2.into();
assert_eq!(extracted, bytes);
let keypair = KeyPair::generate().unwrap();
let slice_ref: &[u8] = keypair.secret_key.as_ref();
assert_eq!(slice_ref.len(), SECRETKEYBYTES);
}
#[test]
fn test_precomputedkey_traits() {
let alice = KeyPair::generate().unwrap();
let bob = KeyPair::generate().unwrap();
let precomputed = beforenm(&bob.public_key, &alice.secret_key).unwrap();
let slice_ref: &[u8] = precomputed.as_ref();
assert_eq!(slice_ref.len(), BEFORENMBYTES);
let bytes_ref = precomputed.as_bytes();
let key = PrecomputedKey::try_from(&bytes_ref[..]).unwrap();
assert_eq!(key.as_bytes(), bytes_ref);
let invalid_bytes = [0x42; BEFORENMBYTES - 1];
assert!(PrecomputedKey::try_from(&invalid_bytes[..]).is_err());
let mut bytes = [0u8; BEFORENMBYTES];
bytes.copy_from_slice(bytes_ref);
let key2 = PrecomputedKey::from(bytes);
assert_eq!(key2.as_bytes(), &bytes);
let extracted: [u8; BEFORENMBYTES] = key2.into();
assert_eq!(extracted, bytes);
}
}