// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! The `crypto` crate abstracts over various crypto implementations.
//!
//! The ring implementation is the default and is used in production. The
//! BoringSSL implementation avoids needing to build ring in Chromium when
//! the enclave code is used with unit tests. The rustcrypto implementation
//! isn't fully complete, but can be used to compile to wasm, which we might
//! use in the future.
#![no_std]
#![allow(clippy::result_unit_err)]
extern crate alloc;
pub const NONCE_LEN: usize = 12;
pub const SHA1_OUTPUT_LEN: usize = 20;
pub const SHA256_OUTPUT_LEN: usize = 32;
/// The length of an uncompressed, X9.62 encoding of a P-256 point.
pub const P256_X962_LENGTH: usize = 65;
/// The length of a P-256 scalar value.
pub const P256_SCALAR_LENGTH: usize = 32;
#[cfg(feature = "rustcrypto")]
pub use crate::rustcrypto::{
aes_256_gcm_open_in_place, aes_256_gcm_seal_in_place, ecdsa_verify, hkdf_sha256,
p256_scalar_mult, rand_bytes, sha256, sha256_two_part, EcdsaKeyPair, P256Scalar,
};
#[cfg(feature = "ring")]
pub use crate::ringimpl::{
aes_128_gcm_open_in_place, aes_128_gcm_seal_in_place, aes_256_gcm_open_in_place,
aes_256_gcm_seal_in_place, ecdsa_verify, hkdf_sha256, hmac_sha256, p256_scalar_mult,
rand_bytes, rsa_verify, sha1_two_part, sha256, sha256_two_part, EcdsaKeyPair, P256Scalar,
RsaKeyPair,
};
#[cfg(feature = "bssl")]
pub use crate::bsslimpl::{
aes_128_gcm_open_in_place, aes_128_gcm_seal_in_place, aes_256_gcm_open_in_place,
aes_256_gcm_seal_in_place, ecdsa_verify, hkdf_sha256, hmac_sha256, p256_scalar_mult,
rand_bytes, rsa_verify, sha1_two_part, sha256, sha256_two_part, EcdsaKeyPair, P256Scalar,
RsaKeyPair,
};
#[cfg(feature = "rustcrypto")]
mod rustcrypto {
use crate::rustcrypto::ecdsa::signature::Verifier;
extern crate aes_gcm;
extern crate ecdsa;
extern crate hkdf;
extern crate pkcs8;
extern crate primeorder;
extern crate sha2;
use aes_gcm::{AeadInPlace, KeyInit};
use p256::ecdsa::signature::Signer;
use pkcs8::{DecodePrivateKey, EncodePrivateKey};
use primeorder::PrimeField;
use sha2::Digest;
use crate::{
NONCE_LEN, P256_SCALAR_LENGTH, P256_X962_LENGTH, SHA1_OUTPUT_LEN, SHA256_OUTPUT_LEN,
};
use primeorder::elliptic_curve::ops::{Mul, MulByGenerator};
use primeorder::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint};
use primeorder::Field;
use alloc::vec::Vec;
pub fn rand_bytes(output: &mut [u8]) {
panic!("unimplemented");
}
/// Perform the HKDF operation from https://datatracker.ietf.org/doc/html/rfc5869
pub fn hkdf_sha256(ikm: &[u8], salt: &[u8], info: &[u8], output: &mut [u8]) -> Result<(), ()> {
hkdf::Hkdf::<sha2::Sha256>::new(Some(salt), ikm).expand(info, output).map_err(|_| ())
}
pub fn aes_256_gcm_seal_in_place(
key: &[u8; 32],
nonce: &[u8; NONCE_LEN],
aad: &[u8],
plaintext: &mut Vec<u8>,
) {
aes_gcm::Aes256Gcm::new_from_slice(key.as_slice())
.unwrap()
.encrypt_in_place(nonce.into(), aad, plaintext)
.unwrap();
}
pub fn aes_256_gcm_open_in_place(
key: &[u8; 32],
nonce: &[u8; NONCE_LEN],
aad: &[u8],
mut ciphertext: Vec<u8>,
) -> Result<Vec<u8>, ()> {
aes_gcm::Aes256Gcm::new_from_slice(key.as_slice())
.unwrap()
.decrypt_in_place(nonce.into(), aad, &mut ciphertext)
.map_err(|_| ())?;
Ok(ciphertext)
}
pub fn sha256(input: &[u8]) -> [u8; SHA256_OUTPUT_LEN] {
let mut ctx = sha2::Sha256::new();
ctx.update(input);
ctx.finalize().into()
}
/// Compute the SHA-256 hash of the concatenation of two inputs.
pub fn sha256_two_part(input1: &[u8], input2: &[u8]) -> [u8; SHA256_OUTPUT_LEN] {
let mut ctx = sha2::Sha256::new();
ctx.update(input1);
ctx.update(input2);
ctx.finalize().into()
}
pub struct P256Scalar {
v: p256::Scalar,
}
impl P256Scalar {
pub fn generate() -> P256Scalar {
let mut ret = [0u8; P256_SCALAR_LENGTH];
// Warning: not very random.
ret[0] = 1;
P256Scalar { v: p256::Scalar::from_repr(ret.into()).unwrap() }
}
pub fn compute_public_key(&self) -> [u8; P256_X962_LENGTH] {
p256::ProjectivePoint::mul_by_generator(&self.v)
.to_encoded_point(false)
.as_bytes()
.try_into()
.unwrap()
}
pub fn bytes(&self) -> [u8; P256_SCALAR_LENGTH] {
self.v.to_repr().as_slice().try_into().unwrap()
}
}
impl TryFrom<&[u8]> for P256Scalar {
type Error = ();
fn try_from(bytes: &[u8]) -> Result<Self, ()> {
let array: [u8; P256_SCALAR_LENGTH] = bytes.try_into().map_err(|_| ())?;
(&array).try_into()
}
}
impl TryFrom<&[u8; P256_SCALAR_LENGTH]> for P256Scalar {
type Error = ();
fn try_from(bytes: &[u8; P256_SCALAR_LENGTH]) -> Result<Self, ()> {
let scalar = p256::Scalar::from_repr((*bytes).into());
if !bool::from(scalar.is_some()) {
return Err(());
}
let scalar = scalar.unwrap();
if scalar.is_zero_vartime() {
return Err(());
}
Ok(P256Scalar { v: scalar })
}
}
pub fn p256_scalar_mult(
scalar: &P256Scalar,
point: &[u8; P256_X962_LENGTH],
) -> Result<[u8; 32], ()> {
let point = p256::EncodedPoint::from_bytes(point).map_err(|_| ())?;
let affine_point = p256::AffinePoint::from_encoded_point(&point);
if !bool::from(affine_point.is_some()) {
// The peer's point is considered public input and so we don't need to work in
// constant time.
return Err(());
}
// unwrap: `is_some` checked just above.
let result = affine_point.unwrap().mul(scalar.v).to_encoded_point(false);
let x = result.x().ok_or(())?;
// unwrap: the length of a P256 field-element had better be 32 bytes.
Ok(x.as_slice().try_into().unwrap())
}
pub struct EcdsaKeyPair {
key_pair: p256::ecdsa::SigningKey,
}
impl EcdsaKeyPair {
pub fn from_pkcs8(pkcs8: &[u8]) -> Result<EcdsaKeyPair, ()> {
let key_pair: p256::ecdsa::SigningKey =
p256::ecdsa::SigningKey::from_pkcs8_der(pkcs8).map_err(|_| ())?;
Ok(EcdsaKeyPair { key_pair })
}
pub fn generate_pkcs8() -> Result<impl AsRef<[u8]>, ()> {
// WARNING: not actually a random scalar
let mut scalar = [0u8; P256_SCALAR_LENGTH];
scalar[0] = 42;
let non_zero_scalar = p256::NonZeroScalar::from_repr(scalar.into()).unwrap();
let key = p256::ecdsa::SigningKey::from(non_zero_scalar);
Ok(key.to_pkcs8_der().map_err(|_| ())?.to_bytes())
}
pub fn public_key(&self) -> impl AsRef<[u8]> + '_ {
p256::ecdsa::VerifyingKey::from(&self.key_pair).to_sec1_bytes()
}
pub fn sign(&self, signed_data: &[u8]) -> Result<impl AsRef<[u8]>, ()> {
let sig: ecdsa::Signature<p256::NistP256> = self.key_pair.sign(signed_data);
Ok(sig.to_der())
}
}
pub fn ecdsa_verify(pub_key: &[u8], signed_data: &[u8], signature: &[u8]) -> bool {
let signature = match ecdsa::der::Signature::from_bytes(signature) {
Ok(signature) => signature,
Err(_) => return false,
};
let key = match p256::ecdsa::VerifyingKey::from_sec1_bytes(pub_key) {
Ok(key) => key,
Err(_) => return false,
};
key.verify(signed_data, &signature).is_ok()
}
}
#[cfg(feature = "ring")]
mod ringimpl {
use crate::ringimpl::ring::signature::KeyPair;
extern crate prng;
extern crate ring;
extern crate untrusted;
use crate::{
NONCE_LEN, P256_SCALAR_LENGTH, P256_X962_LENGTH, SHA1_OUTPUT_LEN, SHA256_OUTPUT_LEN,
};
use alloc::vec;
use alloc::vec::Vec;
use ring::rand::SecureRandom;
use ring::signature::VerificationAlgorithm;
const PRNG: prng::Generator = prng::Generator(());
pub fn rand_bytes(output: &mut [u8]) {
// unwrap: the PRNG had better not fail otherwise we can't do much.
PRNG.fill(output).unwrap();
}
pub fn hkdf_sha256(ikm: &[u8], salt: &[u8], info: &[u8], output: &mut [u8]) -> Result<(), ()> {
ring::hkdf::hkdf(ring::hkdf::HKDF_SHA256, ikm, salt, info, output).map_err(|_| ())
}
pub fn aes_128_gcm_seal_in_place(
key: &[u8; 16],
nonce: &[u8; NONCE_LEN],
aad: &[u8],
plaintext: &mut Vec<u8>,
) {
let key = ring::aead::UnboundKey::new(&ring::aead::AES_128_GCM, key).unwrap();
let key = ring::aead::LessSafeKey::new(key);
key.seal_in_place_append_tag(
ring::aead::Nonce::assume_unique_for_key(*nonce),
ring::aead::Aad::from(aad),
plaintext,
)
.unwrap();
}
pub fn aes_128_gcm_open_in_place(
key: &[u8; 16],
nonce: &[u8; NONCE_LEN],
aad: &[u8],
mut ciphertext: Vec<u8>,
) -> Result<Vec<u8>, ()> {
let key = ring::aead::UnboundKey::new(&ring::aead::AES_128_GCM, key).unwrap();
let key = ring::aead::LessSafeKey::new(key);
let plaintext_len = key
.open_in_place(
ring::aead::Nonce::assume_unique_for_key(*nonce),
ring::aead::Aad::from(aad),
&mut ciphertext,
)
.map_err(|_| ())?
.len();
ciphertext.resize(plaintext_len, 0u8);
Ok(ciphertext)
}
pub fn aes_256_gcm_seal_in_place(
key: &[u8; 32],
nonce: &[u8; NONCE_LEN],
aad: &[u8],
plaintext: &mut Vec<u8>,
) {
let key = ring::aead::UnboundKey::new(&ring::aead::AES_256_GCM, key).unwrap();
let key = ring::aead::LessSafeKey::new(key);
key.seal_in_place_append_tag(
ring::aead::Nonce::assume_unique_for_key(*nonce),
ring::aead::Aad::from(aad),
plaintext,
)
.unwrap();
}
pub fn aes_256_gcm_open_in_place(
key: &[u8; 32],
nonce: &[u8; NONCE_LEN],
aad: &[u8],
mut ciphertext: Vec<u8>,
) -> Result<Vec<u8>, ()> {
let key = ring::aead::UnboundKey::new(&ring::aead::AES_256_GCM, key).unwrap();
let key = ring::aead::LessSafeKey::new(key);
let plaintext_len = key
.open_in_place(
ring::aead::Nonce::assume_unique_for_key(*nonce),
ring::aead::Aad::from(aad),
&mut ciphertext,
)
.map_err(|_| ())?
.len();
ciphertext.resize(plaintext_len, 0u8);
Ok(ciphertext)
}
/// Compute the SHA-1 hash of the concatenation of two inputs.
pub fn sha1_two_part(input1: &[u8], input2: &[u8]) -> [u8; SHA1_OUTPUT_LEN] {
let mut ctx = ring::digest::Context::new(&ring::digest::SHA1_FOR_LEGACY_USE_ONLY);
ctx.update(input1);
ctx.update(input2);
ctx.finish().as_ref().try_into().unwrap()
}
pub fn sha256(input: &[u8]) -> [u8; SHA256_OUTPUT_LEN] {
let mut ctx = ring::digest::Context::new(&ring::digest::SHA256);
ctx.update(input);
ctx.finish().as_ref().try_into().unwrap()
}
pub fn sha256_two_part(input1: &[u8], input2: &[u8]) -> [u8; SHA256_OUTPUT_LEN] {
let mut ctx = ring::digest::Context::new(&ring::digest::SHA256);
ctx.update(input1);
ctx.update(input2);
ctx.finish().as_ref().try_into().unwrap()
}
pub fn hmac_sha256(key: &[u8], msg: &[u8]) -> [u8; SHA256_OUTPUT_LEN] {
let key = ring::hmac::Key::new(ring::hmac::HMAC_SHA256, key);
// unwrap: HMAC-SHA256 has to produce output of the correct length.
ring::hmac::sign(&key, msg).as_ref().try_into().unwrap()
}
pub struct P256Scalar {
v: ring::agreement::EphemeralPrivateKey,
}
impl P256Scalar {
pub fn generate() -> P256Scalar {
P256Scalar {
v: ring::agreement::EphemeralPrivateKey::generate(
&ring::agreement::ECDH_P256,
&PRNG,
)
.unwrap(),
}
}
pub fn compute_public_key(&self) -> [u8; P256_X962_LENGTH] {
// unwrap: only returns an error if the input length is incorrect, but it isn't.
self.v.compute_public_key().unwrap().as_ref().try_into().unwrap()
}
pub fn bytes(&self) -> [u8; P256_SCALAR_LENGTH] {
self.v.bytes().as_ref().try_into().unwrap()
}
/// This exists only because ring insists on consuming the private key
/// during DH operations.
fn clone_scalar(&self) -> ring::agreement::EphemeralPrivateKey {
ring::agreement::EphemeralPrivateKey::from_bytes(
&ring::agreement::ECDH_P256,
&self.bytes(),
)
.unwrap()
}
}
impl TryFrom<&[u8]> for P256Scalar {
type Error = ();
fn try_from(bytes: &[u8]) -> Result<Self, ()> {
let array: [u8; P256_SCALAR_LENGTH] = bytes.try_into().map_err(|_| ())?;
(&array).try_into()
}
}
impl TryFrom<&[u8; P256_SCALAR_LENGTH]> for P256Scalar {
type Error = ();
fn try_from(bytes: &[u8; P256_SCALAR_LENGTH]) -> Result<Self, ()> {
let scalar = ring::agreement::EphemeralPrivateKey::from_bytes(
&ring::agreement::ECDH_P256,
bytes,
)
.map_err(|_| ())?;
Ok(P256Scalar { v: scalar })
}
}
pub fn p256_scalar_mult(
scalar: &P256Scalar,
point: &[u8; P256_X962_LENGTH],
) -> Result<[u8; 32], ()> {
// unwrap: only returns an error if the input length is incorrect, but it isn't.
ring::agreement::agree_ephemeral(
scalar.clone_scalar(),
&ring::agreement::UnparsedPublicKey::new(&ring::agreement::ECDH_P256, point),
(),
|key| Ok(key.try_into().unwrap()),
)
.map_err(|_| ())
}
pub struct EcdsaKeyPair {
key_pair: ring::signature::EcdsaKeyPair,
}
impl EcdsaKeyPair {
pub fn from_pkcs8(pkcs8: &[u8]) -> Result<EcdsaKeyPair, ()> {
let key_pair = ring::signature::EcdsaKeyPair::from_pkcs8(
&ring::signature::ECDSA_P256_SHA256_ASN1_SIGNING,
pkcs8,
&PRNG,
)
.map_err(|_| ())?;
Ok(EcdsaKeyPair { key_pair })
}
pub fn generate_pkcs8() -> impl AsRef<[u8]> {
ring::signature::EcdsaKeyPair::generate_pkcs8(
&ring::signature::ECDSA_P256_SHA256_ASN1_SIGNING,
&PRNG,
)
.unwrap()
}
pub fn public_key(&self) -> impl AsRef<[u8]> + '_ {
self.key_pair.public_key()
}
pub fn sign(&self, signed_data: &[u8]) -> Result<impl AsRef<[u8]>, ()> {
self.key_pair.sign(&PRNG, signed_data).map_err(|_| ())
}
}
pub fn ecdsa_verify(pub_key: &[u8], signed_data: &[u8], signature: &[u8]) -> bool {
ring::signature::ECDSA_P256_SHA256_ASN1
.verify(
untrusted::Input::from(pub_key),
untrusted::Input::from(signed_data),
untrusted::Input::from(signature),
)
.is_ok()
}
pub struct RsaKeyPair {
key_pair: ring::signature::RsaKeyPair,
}
impl RsaKeyPair {
pub fn from_pkcs8(pkcs8: &[u8]) -> Result<RsaKeyPair, ()> {
let key_pair = ring::signature::RsaKeyPair::from_pkcs8(pkcs8).map_err(|_| ())?;
Ok(RsaKeyPair { key_pair })
}
pub fn sign(&self, to_be_signed: &[u8]) -> Result<impl AsRef<[u8]>, ()> {
let mut signature = vec![0; self.key_pair.public_modulus_len()];
self.key_pair
.sign(&ring::signature::RSA_PKCS1_SHA256, &PRNG, to_be_signed, &mut signature)
.unwrap();
Ok(signature)
}
}
pub fn rsa_verify(pub_key: &[u8], signed_data: &[u8], signature: &[u8]) -> bool {
ring::signature::RSA_PKCS1_2048_8192_SHA256
.verify(
untrusted::Input::from(pub_key),
untrusted::Input::from(signed_data),
untrusted::Input::from(signature),
)
.is_ok()
}
}
// This implementation uses the bssl-crypto crate from the BoringSSL
// distribution. This is used for testing within Chromium.
#[cfg(feature = "bssl")]
mod bsslimpl {
#![allow(clippy::upper_case_acronyms)]
use crate::{
NONCE_LEN, P256_SCALAR_LENGTH, P256_X962_LENGTH, SHA1_OUTPUT_LEN, SHA256_OUTPUT_LEN,
};
use alloc::vec::Vec;
use bssl_crypto::aead::{Aead, Aes128Gcm, Aes256Gcm};
use bssl_crypto::ec::P256;
use bssl_crypto::hkdf::HkdfSha256;
use bssl_crypto::hmac::HmacSha256;
use bssl_crypto::{digest, ecdh, ecdsa, hkdf, rsa};
const GCM_TAG_LEN: usize = 16;
pub fn rand_bytes(output: &mut [u8]) {
bssl_crypto::rand_bytes(output)
}
pub fn aes_128_gcm_open_in_place(
key: &[u8; 16],
nonce: &[u8; NONCE_LEN],
aad: &[u8],
mut ciphertext: Vec<u8>,
) -> Result<Vec<u8>, ()> {
if ciphertext.len() < GCM_TAG_LEN {
return Err(());
}
let ciphertext_len = ciphertext.len() - GCM_TAG_LEN;
let (ciphertext_slice, tag) = ciphertext.split_at_mut(ciphertext_len);
let tag: &[u8; GCM_TAG_LEN] = &tag.try_into().unwrap();
let aead = Aes128Gcm::new(key);
aead.open_in_place(nonce, ciphertext_slice, tag, aad).map_err(|_| ())?;
ciphertext.resize(ciphertext_len, 0u8);
Ok(ciphertext)
}
pub fn aes_128_gcm_seal_in_place(
key: &[u8; 16],
nonce: &[u8; NONCE_LEN],
aad: &[u8],
plaintext: &mut Vec<u8>,
) {
let tag = Aes128Gcm::new(key).seal_in_place(nonce, plaintext, aad);
plaintext.extend_from_slice(&tag);
}
pub fn aes_256_gcm_open_in_place(
key: &[u8; 32],
nonce: &[u8; NONCE_LEN],
aad: &[u8],
mut ciphertext: Vec<u8>,
) -> Result<Vec<u8>, ()> {
if ciphertext.len() < GCM_TAG_LEN {
return Err(());
}
let ciphertext_len = ciphertext.len() - GCM_TAG_LEN;
let (ciphertext_slice, tag) = ciphertext.split_at_mut(ciphertext_len);
let tag: &[u8; GCM_TAG_LEN] = &tag.try_into().unwrap();
let aead = Aes256Gcm::new(key);
aead.open_in_place(nonce, ciphertext_slice, tag, aad).map_err(|_| ())?;
ciphertext.resize(ciphertext_len, 0u8);
Ok(ciphertext)
}
pub fn aes_256_gcm_seal_in_place(
key: &[u8; 32],
nonce: &[u8; NONCE_LEN],
aad: &[u8],
plaintext: &mut Vec<u8>,
) {
let tag = Aes256Gcm::new(key).seal_in_place(nonce, plaintext, aad);
plaintext.extend_from_slice(&tag);
}
pub fn hkdf_sha256(ikm: &[u8], salt: &[u8], info: &[u8], output: &mut [u8]) -> Result<(), ()> {
HkdfSha256::derive_into(ikm, hkdf::Salt::NonEmpty(salt), info, output).map_err(|_| ())
}
pub fn hmac_sha256(key: &[u8], msg: &[u8]) -> [u8; SHA256_OUTPUT_LEN] {
HmacSha256::mac(key, msg)
}
pub fn sha1_two_part(input1: &[u8], input2: &[u8]) -> [u8; SHA1_OUTPUT_LEN] {
let mut ctx = digest::InsecureSha1::new();
ctx.update(input1);
ctx.update(input2);
ctx.digest()
}
pub fn sha256(input: &[u8]) -> [u8; SHA256_OUTPUT_LEN] {
digest::Sha256::hash(input)
}
pub fn sha256_two_part(input1: &[u8], input2: &[u8]) -> [u8; SHA256_OUTPUT_LEN] {
let mut ctx = digest::Sha256::new();
ctx.update(input1);
ctx.update(input2);
ctx.digest()
}
pub struct P256Scalar(ecdh::PrivateKey<P256>);
impl P256Scalar {
pub fn generate() -> Self {
Self(ecdh::PrivateKey::generate())
}
pub fn compute_public_key(&self) -> [u8; P256_X962_LENGTH] {
self.0.to_x962_uncompressed().as_ref().try_into().unwrap()
}
pub fn bytes(&self) -> [u8; P256_SCALAR_LENGTH] {
self.0.to_big_endian().as_ref().try_into().unwrap()
}
}
impl TryFrom<&[u8]> for P256Scalar {
type Error = ();
fn try_from(bytes: &[u8]) -> Result<Self, ()> {
Ok(Self(ecdh::PrivateKey::from_big_endian(bytes).ok_or(())?))
}
}
impl TryFrom<&[u8; P256_SCALAR_LENGTH]> for P256Scalar {
type Error = ();
fn try_from(bytes: &[u8; P256_SCALAR_LENGTH]) -> Result<Self, ()> {
Ok(Self(ecdh::PrivateKey::from_big_endian(bytes).ok_or(())?))
}
}
pub fn p256_scalar_mult(
scalar: &P256Scalar,
encoded_point: &[u8; P256_X962_LENGTH],
) -> Result<[u8; 32], ()> {
let pub_key = ecdh::PublicKey::from_x962_uncompressed(encoded_point).ok_or(())?;
Ok(scalar.0.compute_shared_key(&pub_key).try_into().unwrap())
}
pub struct EcdsaKeyPair(ecdsa::PrivateKey<P256>);
impl EcdsaKeyPair {
pub fn from_pkcs8(pkcs8: &[u8]) -> Result<EcdsaKeyPair, ()> {
Ok(Self(ecdsa::PrivateKey::from_der_private_key_info(pkcs8).ok_or(())?))
}
pub fn generate_pkcs8() -> impl AsRef<[u8]> {
ecdsa::PrivateKey::<P256>::generate().to_der_private_key_info()
}
pub fn public_key(&self) -> impl AsRef<[u8]> + '_ {
self.0.to_x962_uncompressed()
}
pub fn sign(&self, signed_data: &[u8]) -> Result<impl AsRef<[u8]>, ()> {
Ok(self.0.sign(signed_data))
}
}
pub fn ecdsa_verify(pub_key: &[u8], signed_data: &[u8], signature: &[u8]) -> bool {
let Some(pub_key) = ecdsa::PublicKey::<P256>::from_x962_uncompressed(pub_key) else {
return false;
};
pub_key.verify(signed_data, signature).is_ok()
}
pub struct RsaKeyPair(rsa::PrivateKey);
impl RsaKeyPair {
pub fn from_pkcs8(pkcs8: &[u8]) -> Result<RsaKeyPair, ()> {
Ok(Self(rsa::PrivateKey::from_der_private_key_info(pkcs8).ok_or(())?))
}
pub fn sign(&self, signed_data: &[u8]) -> Result<impl AsRef<[u8]>, ()> {
Ok(self.0.sign_pkcs1::<digest::Sha256>(signed_data))
}
}
pub fn rsa_verify(pub_key: &[u8], signed_data: &[u8], signature: &[u8]) -> bool {
let Some(pub_key) = rsa::PublicKey::from_der_rsa_public_key(pub_key) else {
return false;
};
pub_key.verify_pkcs1::<digest::Sha256>(signed_data, signature).is_ok()
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_rand_bytes() {
let mut buf = [0u8; 16];
rand_bytes(&mut buf);
}
#[test]
fn test_aes_256_gcm() {
let key = [1u8; 32];
let nonce = [2u8; 12];
let aad = [3u8; 16];
let mut plaintext = Vec::new();
plaintext.resize(50, 4u8);
let mut ciphertext = plaintext.clone();
aes_256_gcm_seal_in_place(&key, &nonce, &aad, &mut ciphertext);
let plaintext2 = aes_256_gcm_open_in_place(&key, &nonce, &aad, ciphertext).unwrap();
assert_eq!(plaintext, plaintext2);
}
#[test]
fn test_ecdh() {
let priv1 = P256Scalar::generate();
let pub1 = priv1.compute_public_key();
let priv2 = P256Scalar::generate();
let pub2 = priv2.compute_public_key();
let shared1 = p256_scalar_mult(&priv1, &pub2).unwrap();
let shared2 = p256_scalar_mult(&priv2, &pub1).unwrap();
assert_eq!(shared1, shared2);
let priv3: P256Scalar = (&priv1.bytes()).try_into().unwrap();
let pub3 = priv3.compute_public_key();
let shared3 = p256_scalar_mult(&priv2, &pub3).unwrap();
assert_eq!(shared1, shared3);
}
#[test]
fn test_ecdsa() {
let pkcs8 = EcdsaKeyPair::generate_pkcs8();
let priv_key = EcdsaKeyPair::from_pkcs8(pkcs8.as_ref()).unwrap();
let pub_key = priv_key.public_key();
let signed_message = [42u8; 20];
let signature = priv_key.sign(&signed_message).unwrap();
assert!(ecdsa_verify(pub_key.as_ref(), &signed_message, signature.as_ref()));
}
}
}