ecies_25519 0.2.1

Cross-platform Elliptic Curve Integrated Encryption Scheme (ECIES) using X25519, AES-256-GCM, and HKDF-SHA256.
Documentation
use rand_core::{CryptoRng, RngCore, TryRngCore};
use aes_gcm::aead::{self, generic_array::GenericArray, Aead};
use aes_gcm::{Aes256Gcm, KeyInit};
use hkdf::Hkdf;
use sha2::Sha256;

use super::AesKey;
use super::Error;
use super::AES_IV_LENGTH;

pub(crate) fn hkdf_sha256(master: &[u8], hkdf_info: &[u8]) -> AesKey {
  let h = Hkdf::<Sha256>::new(None, master);
  let mut out = [0u8; 32];
  h.expand(hkdf_info, &mut out)
    .expect("ecies-ed25519: unexpected error in rust hkdf_sha256");

  out
}

pub(crate) fn aes_encrypt<R: CryptoRng + RngCore>(
  key: &AesKey,
  msg: &[u8],
  rng: &mut R,
) -> Result<Vec<u8>, Error> {
  let key = GenericArray::from_slice(key);
  let aead = Aes256Gcm::new(key);

  let mut nonce = [0u8; AES_IV_LENGTH];
  rng
    .try_fill_bytes(&mut nonce)
    .map_err(|_| Error::EncryptionFailedRng)?;
  let nonce = GenericArray::from_slice(&nonce);

  let ciphertext = aead
    .encrypt(nonce, msg)
    .map_err(|_| Error::EncryptionFailed)?;

  let mut output = Vec::with_capacity(AES_IV_LENGTH + ciphertext.len());

  output.extend(nonce);
  output.extend(ciphertext);

  Ok(output)
}

pub(crate) fn aes_decrypt(key: &AesKey, ciphertext: &[u8]) -> Result<Vec<u8>, aead::Error> {
  let key = GenericArray::from_slice(key);
  let aead = Aes256Gcm::new(key);

  let nonce = GenericArray::from_slice(&ciphertext[..AES_IV_LENGTH]);
  let encrypted = &ciphertext[AES_IV_LENGTH..];

  let decrypted = aead.decrypt(nonce, encrypted)?;

  Ok(decrypted)
}