Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ jobs:
steps:
- uses: actions/checkout@v5
- uses: Swatinem/rust-cache@v2
- if: ${{ runner.os == 'Windows' }}
uses: ilammy/setup-nasm@v1
- name: Install Rust
run: |
rustup set profile minimal
Expand Down
9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ keywords = ["Cryptography"]
description = "Shadowsocks Crypto"
repository = "https://github.com/shadowsocks/shadowsocks-crypto"
documentation = "https://docs.rs/shadowsocks-crypto"
rust-version = "1.63"
rust-version = "1.71"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
Expand All @@ -30,7 +30,10 @@ v1-aead-extra = [
v2 = ["aes", "aes-gcm", "blake3", "chacha20poly1305", "bytes", "cfg-if"]
v2-extra = ["v2", "chacha20poly1305/reduced-round"]

ring = ["ring-compat"]
# Swap the pure-Rust AEAD/HKDF implementations for the AWS-LC-backed `aws-lc-rs`
# crate for higher throughput. Platforms not supported by aws-lc-rs (e.g. WASM,
# exotic targets) must leave this feature off and use the pure-Rust fallback.
faster-crypto = ["dep:aws-lc-rs"]

[dependencies]
cfg-if = { version = "1.0", optional = true }
Expand All @@ -39,7 +42,7 @@ aes-gcm = { version = "0.10", optional = true }
aes-gcm-siv = { version = "0.11", optional = true }
ccm = { version = "0.5", optional = true }
chacha20poly1305 = { version = "0.10", optional = true }
ring-compat = { version = "0.8", default-features = false, features = ["aead", "alloc"], optional = true }
aws-lc-rs = { version = "1.16", optional = true }
md-5 = { version = "0.10", optional = true }
hkdf = { version = "0.12", optional = true }
sha1 = { version = "0.10", optional = true }
Expand Down
207 changes: 108 additions & 99 deletions src/v1/aeadcipher/aes_gcm.rs
Original file line number Diff line number Diff line change
@@ -1,142 +1,151 @@
use cfg_if::cfg_if;

cfg_if! {
if #[cfg(feature = "ring")] {
use std::convert::{AsMut, AsRef};
if #[cfg(feature = "faster-crypto")] {
use aws_lc_rs::aead::{Aad, Algorithm, LessSafeKey, Nonce, UnboundKey, AES_128_GCM, AES_256_GCM};

pub use ring_compat::aead::{Aes128Gcm as CryptoAes128Gcm, Aes256Gcm as CryptoAes256Gcm};
use ring_compat::{
aead::{AeadCore, AeadInPlace, Buffer, Error as AeadError, KeySizeUser, KeyInit},
generic_array::{typenum::Unsigned, GenericArray},
};

type Key<B> = GenericArray<u8, <B as KeySizeUser>::KeySize>;
type Nonce<NonceSize> = GenericArray<u8, NonceSize>;
struct AeadKey(LessSafeKey);

struct SliceBuffer<'a>(&'a mut [u8]);
impl AeadKey {
fn new(algorithm: &'static Algorithm, key: &[u8]) -> AeadKey {
let unbound = UnboundKey::new(algorithm, key).expect("AEAD key");
AeadKey(LessSafeKey::new(unbound))
}

impl AsRef<[u8]> for SliceBuffer<'_> {
fn as_ref(&self) -> &[u8] {
self.0
fn encrypt(&self, nonce: &[u8], plaintext_in_ciphertext_out: &mut [u8]) {
let nonce = Nonce::try_assume_unique_for_key(nonce).expect("AEAD nonce");
let tag_len = self.0.algorithm().tag_len();
let (plaintext, out_tag) =
plaintext_in_ciphertext_out.split_at_mut(plaintext_in_ciphertext_out.len() - tag_len);
let tag = self
.0
.seal_in_place_separate_tag(nonce, Aad::empty(), plaintext)
.expect("AEAD encrypt");
out_tag.copy_from_slice(tag.as_ref());
}
}

impl AsMut<[u8]> for SliceBuffer<'_> {
fn as_mut(&mut self) -> &mut [u8] {
self.0
fn decrypt(&self, nonce: &[u8], ciphertext_in_plaintext_out: &mut [u8]) -> bool {
let nonce = Nonce::try_assume_unique_for_key(nonce).expect("AEAD nonce");
self.0.open_in_place(nonce, Aad::empty(), ciphertext_in_plaintext_out).is_ok()
}
}

impl Buffer for SliceBuffer<'_> {
fn extend_from_slice(&mut self, _other: &[u8]) -> Result<(), AeadError> {
unimplemented!("not used in decrypt_in_place")
}
macro_rules! aead_cipher {
($name:ident, $algorithm:ident) => {
pub struct $name(AeadKey);

impl $name {
pub fn new(key: &[u8]) -> $name {
$name(AeadKey::new(&$algorithm, key))
}

pub fn key_size() -> usize {
$algorithm.key_len()
}

pub fn nonce_size() -> usize {
$algorithm.nonce_len()
}

pub fn tag_size() -> usize {
$algorithm.tag_len()
}

pub fn encrypt(&self, nonce: &[u8], plaintext_in_ciphertext_out: &mut [u8]) {
self.0.encrypt(nonce, plaintext_in_ciphertext_out)
}

fn truncate(&mut self, _len: usize) {}
pub fn decrypt(&self, nonce: &[u8], ciphertext_in_plaintext_out: &mut [u8]) -> bool {
self.0.decrypt(nonce, ciphertext_in_plaintext_out)
}
}
};
}

aead_cipher!(Aes128Gcm, AES_128_GCM);
aead_cipher!(Aes256Gcm, AES_256_GCM);
} else {
use aes_gcm::{
aead::{generic_array::typenum::Unsigned, AeadCore, AeadInPlace, KeySizeUser, KeyInit},
Key,
Nonce,
Tag,
};
pub use aes_gcm::{Aes128Gcm as CryptoAes128Gcm, Aes256Gcm as CryptoAes256Gcm};
}
}
use aes_gcm::{Aes128Gcm as CryptoAes128Gcm, Aes256Gcm as CryptoAes256Gcm};

pub struct Aes128Gcm(Box<CryptoAes128Gcm>);

impl Aes128Gcm {
pub fn new(key: &[u8]) -> Aes128Gcm {
let key = Key::<CryptoAes128Gcm>::from_slice(key);
Aes128Gcm(Box::new(CryptoAes128Gcm::new(key)))
}
pub struct Aes128Gcm(Box<CryptoAes128Gcm>);

pub fn key_size() -> usize {
<CryptoAes128Gcm as KeySizeUser>::KeySize::to_usize()
}

pub fn nonce_size() -> usize {
<CryptoAes128Gcm as AeadCore>::NonceSize::to_usize()
}
impl Aes128Gcm {
pub fn new(key: &[u8]) -> Aes128Gcm {
let key = Key::<CryptoAes128Gcm>::from_slice(key);
Aes128Gcm(Box::new(CryptoAes128Gcm::new(key)))
}

pub fn tag_size() -> usize {
<CryptoAes128Gcm as AeadCore>::TagSize::to_usize()
}
pub fn key_size() -> usize {
<CryptoAes128Gcm as KeySizeUser>::KeySize::to_usize()
}

pub fn encrypt(&self, nonce: &[u8], plaintext_in_ciphertext_out: &mut [u8]) {
let nonce = Nonce::from_slice(nonce);
let (plaintext, out_tag) =
plaintext_in_ciphertext_out.split_at_mut(plaintext_in_ciphertext_out.len() - Self::tag_size());
let tag = self
.0
.encrypt_in_place_detached(nonce, &[], plaintext)
.expect("AES_128_GCM encrypt");
out_tag.copy_from_slice(tag.as_slice())
}
pub fn nonce_size() -> usize {
<CryptoAes128Gcm as AeadCore>::NonceSize::to_usize()
}

pub fn decrypt(&self, nonce: &[u8], ciphertext_in_plaintext_out: &mut [u8]) -> bool {
let nonce = Nonce::from_slice(nonce);
pub fn tag_size() -> usize {
<CryptoAes128Gcm as AeadCore>::TagSize::to_usize()
}

cfg_if! {
if #[cfg(feature = "ring")] {
// ring-compat marked decrypt_in_place_detached as unimplemented.
// But AES_128_GCM actually expects tag in the back. So it is safe to use `decrypt_in_place`.
pub fn encrypt(&self, nonce: &[u8], plaintext_in_ciphertext_out: &mut [u8]) {
let nonce = Nonce::from_slice(nonce);
let (plaintext, out_tag) =
plaintext_in_ciphertext_out.split_at_mut(plaintext_in_ciphertext_out.len() - Self::tag_size());
let tag = self
.0
.encrypt_in_place_detached(nonce, &[], plaintext)
.expect("AES_128_GCM encrypt");
out_tag.copy_from_slice(tag.as_slice())
}

let mut buffer = SliceBuffer(ciphertext_in_plaintext_out);
self.0.decrypt_in_place(nonce, &[], &mut buffer).is_ok()
} else {
pub fn decrypt(&self, nonce: &[u8], ciphertext_in_plaintext_out: &mut [u8]) -> bool {
let nonce = Nonce::from_slice(nonce);
let (ciphertext, in_tag) =
ciphertext_in_plaintext_out.split_at_mut(ciphertext_in_plaintext_out.len() - Self::tag_size());
let in_tag = Tag::from_slice(in_tag);
self.0.decrypt_in_place_detached(nonce, &[], ciphertext, in_tag).is_ok()
}
}
}
}

pub struct Aes256Gcm(Box<CryptoAes256Gcm>);

impl Aes256Gcm {
pub fn new(key: &[u8]) -> Aes256Gcm {
let key = Key::<CryptoAes256Gcm>::from_slice(key);
Aes256Gcm(Box::new(CryptoAes256Gcm::new(key)))
}
pub struct Aes256Gcm(Box<CryptoAes256Gcm>);

pub fn key_size() -> usize {
<CryptoAes256Gcm as KeySizeUser>::KeySize::to_usize()
}
impl Aes256Gcm {
pub fn new(key: &[u8]) -> Aes256Gcm {
let key = Key::<CryptoAes256Gcm>::from_slice(key);
Aes256Gcm(Box::new(CryptoAes256Gcm::new(key)))
}

pub fn nonce_size() -> usize {
<CryptoAes256Gcm as AeadCore>::NonceSize::to_usize()
}
pub fn key_size() -> usize {
<CryptoAes256Gcm as KeySizeUser>::KeySize::to_usize()
}

pub fn tag_size() -> usize {
<CryptoAes256Gcm as AeadCore>::TagSize::to_usize()
}
pub fn nonce_size() -> usize {
<CryptoAes256Gcm as AeadCore>::NonceSize::to_usize()
}

pub fn encrypt(&self, nonce: &[u8], plaintext_in_ciphertext_out: &mut [u8]) {
let nonce = Nonce::from_slice(nonce);
let (plaintext, out_tag) =
plaintext_in_ciphertext_out.split_at_mut(plaintext_in_ciphertext_out.len() - Self::tag_size());
let tag = self
.0
.encrypt_in_place_detached(nonce, &[], plaintext)
.expect("AES_256_GCM encrypt");
out_tag.copy_from_slice(tag.as_slice())
}
pub fn tag_size() -> usize {
<CryptoAes256Gcm as AeadCore>::TagSize::to_usize()
}

pub fn decrypt(&self, nonce: &[u8], ciphertext_in_plaintext_out: &mut [u8]) -> bool {
let nonce = Nonce::from_slice(nonce);
cfg_if! {
if #[cfg(feature = "ring")] {
// ring-compat marked decrypt_in_place_detached as unimplemented.
// But AES_256_GCM actually expects tag in the back. So it is safe to use `decrypt_in_place`.
pub fn encrypt(&self, nonce: &[u8], plaintext_in_ciphertext_out: &mut [u8]) {
let nonce = Nonce::from_slice(nonce);
let (plaintext, out_tag) =
plaintext_in_ciphertext_out.split_at_mut(plaintext_in_ciphertext_out.len() - Self::tag_size());
let tag = self
.0
.encrypt_in_place_detached(nonce, &[], plaintext)
.expect("AES_256_GCM encrypt");
out_tag.copy_from_slice(tag.as_slice())
}

let mut buffer = SliceBuffer(ciphertext_in_plaintext_out);
self.0.decrypt_in_place(nonce, &[], &mut buffer).is_ok()
} else {
pub fn decrypt(&self, nonce: &[u8], ciphertext_in_plaintext_out: &mut [u8]) -> bool {
let nonce = Nonce::from_slice(nonce);
let (ciphertext, in_tag) =
ciphertext_in_plaintext_out.split_at_mut(ciphertext_in_plaintext_out.len() - Self::tag_size());
let in_tag = Tag::from_slice(in_tag);
Expand Down
Loading
Loading