mirror of
https://github.com/logos-blockchain/sponges.git
synced 2026-07-30 02:23:31 +00:00
ascon: replace implementation with ascon-core (#49)
Originally from https://github.com/sebastinas/ascon-aead/tree/main/ascon-core
This commit is contained in:
parent
d8f8fbca7d
commit
3e38152a4a
13
.github/workflows/ascon.yml
vendored
13
.github/workflows/ascon.yml
vendored
@ -23,18 +23,6 @@ jobs:
|
||||
with:
|
||||
msrv: 1.60.0
|
||||
|
||||
benches:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: RustCrypto/actions/cargo-cache@master
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly-2023-02-01
|
||||
override: true
|
||||
- run: cargo build --benches
|
||||
|
||||
build:
|
||||
needs: set-msrv
|
||||
runs-on: ubuntu-latest
|
||||
@ -56,7 +44,6 @@ jobs:
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- run: cargo build --no-default-features --target ${{ matrix.target }}
|
||||
- run: cargo build --no-default-features --target ${{ matrix.target }} --features aead
|
||||
|
||||
minimal-versions:
|
||||
uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master
|
||||
|
||||
61
Cargo.lock
generated
61
Cargo.lock
generated
@ -2,65 +2,6 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aead"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c192eb8f11fc081b0fe4259ba5af04217d4e0faddd02417310a927911abd7c8"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ascon"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"hex-literal",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex-literal"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
version = "0.3.0-pre"
|
||||
|
||||
@ -1,34 +1,22 @@
|
||||
[package]
|
||||
name = "ascon"
|
||||
version = "0.2.0"
|
||||
description = """
|
||||
Pure Rust implementation of Ascon, a family of authenticated encryption and
|
||||
hashing algorithms designed to be lightweight and easy to implement
|
||||
"""
|
||||
authors = ["quininer kel <quininer@live.com>", "RustCrypto Developers"]
|
||||
version = "0.3.0-pre"
|
||||
description = "Pure rust implementation of the Ascon permutation"
|
||||
authors = [
|
||||
"Sebastian Ramacher <sebastian.ramacher@ait.ac.at>",
|
||||
"RustCrypto Developers",
|
||||
]
|
||||
license = "Apache-2.0 OR MIT"
|
||||
documentation = "https://docs.rs/ascon"
|
||||
repository = "https://github.com/RustCrypto/sponges/tree/master/ascon"
|
||||
keywords = ["aead", "crypto", "sponge"]
|
||||
keywords = ["Ascon", "crypto", "permutation"]
|
||||
categories = ["cryptography", "no-std"]
|
||||
readme = "README.md"
|
||||
edition = "2021"
|
||||
rust-version = "1.60"
|
||||
|
||||
[dependencies]
|
||||
aead = { version = "0.5", optional = true, default-features = false }
|
||||
subtle = { version = "2", optional = true, default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
hex-literal = "0.3"
|
||||
|
||||
[features]
|
||||
default = ["aead"]
|
||||
alloc = ["aead?/alloc"]
|
||||
std = ["aead?/std"]
|
||||
|
||||
aead = ["dep:aead", "alloc", "subtle"] # TODO(tarcieri): remove alloc dependency
|
||||
no_unroll = [] # Do not unroll loops for binary size reduction
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# [RustCrypto]: Ascon
|
||||
# [RustCrypto]: Ascon permutation
|
||||
|
||||
[![crate][crate-image]][crate-link]
|
||||
[![Docs][docs-image]][docs-link]
|
||||
@ -7,8 +7,9 @@
|
||||
![Rust Version][rustc-image]
|
||||
[![Project Chat][chat-image]][chat-link]
|
||||
|
||||
Pure Rust implementation of [Ascon], a family of authenticated encryption and
|
||||
hashing algorithms designed to be lightweight and easy to implement.
|
||||
Pure Rust implementation of the permutation of [Ascon], a family of
|
||||
authenticated encryption and hashing algorithms designed to be lightweight and
|
||||
easy to implement.
|
||||
|
||||
[Documentation][docs-link]
|
||||
|
||||
@ -18,7 +19,7 @@ Ascon is a family of lightweight algorithms built on a core permutation
|
||||
algorithm. These algorithms include:
|
||||
|
||||
- [x] Authenticated Encryption with Associated Data (AEAD)
|
||||
- [ ] Hash functions (HASH) and extendible-output functions (XOF)
|
||||
- [x] Hash functions (HASH) and extendible-output functions (XOF)
|
||||
- [ ] Pseudo-random functions (PRF) and message authentication codes (MAC)
|
||||
|
||||
Ascon has been selected as [new standard for lightweight cryptography] in the
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
|
||||
use ascon::aead::{AeadInPlace, AsconAead, KeyInit};
|
||||
use hex_literal::hex;
|
||||
use test::Bencher;
|
||||
|
||||
const KEY: [u8; 16] = [4; 16];
|
||||
const NONCE: [u8; 16] = [8; 16];
|
||||
const AAD: [u8; 16] = [3; 16];
|
||||
const PLAINTEXT: [u8; 1025] = [99; 1025];
|
||||
const CIPHERTEXT: [u8; 1025] = hex!("d5cfb4650e0a82b9e86455de588cfd48c4bf240301efbf1981a57f00c5ec31eb9127d5da85fab0bf3c927b9d875cd1f71e0945f0281a0774e890bae1f98d488580c63e19972d73b6df19773503b0d8f0317727b8ab7681e15237eec369e8ea2915695d6f03f44541ea28f737574d5c7277361636a0a246fbc8c4b6764ef642b9a15211e11f5059fd08d173e4a9fff4cc55389d04a392f0a12dbccce67439e967bb8f39887b078cd68eaa8fd5a3c3b7299536c50a35b7871ab612417fdd1313527cbc82591d20417f2ef3dc37dc1298b32677daac9e79cf28307ceb886aea42694322ac5e978405fff9df6b9da86c83661fa165c957b830dc85350bb41ae6a6f9ee7c713d47b6277b54c64d0ceefd41945f23cfe4f0ba0ea39ba7c829c308ab53591817adeda57c4d3858fff07f57ec4100905e8568e855f61d558f7cffc3dc47b4e2ab9b90053caef52bede5871c5839b33f48e8822dbca59e96532ded31e809d901de72d65d5112ac067f7135670445bdec7e9f41287190dd091f906d6d98b6d15aa3fa475a2b4f99bc0e12e9c057140308a73572dcabad2e2140103028c075e9da74d92177aaea9d1899dace0ee8f4a9edfa9c4abc39a4b6239a2ea3841af1f90af884d10ef43b48924cba8474cb0162c8204acab4647cb680e9b64fb77e4d2195f63dd45e6c21b637db5c15101a72259e5af940ce9e5c0fe0b4affefef059e2982c32dfe50c0aefee368f15867ab2ff205fa855a895a0379b5678f432e25fd884771873acf6ee72fe4cf02c13843239b3114ff9e9bfebc44618bffc731b272cf08e627e47d759f1ae63191892d493a19fb54b96dd2132e416612d4e4a3bb536d0470c151c998a9ce5a44883ac395efa23e201734ef6a9e7ac1dade6a48fff8445e39a38157b5263cf9e52abf8cb604be9f1ed7ebe85e10933ab1f3e0546c17bbf7561233d7f95b32f8086fdbb06948af5a0ffd06dd47e89c3f4a419cc5b34300a76a542aeeae716563204f68f880cac077855297150fd217d6185116d542b64ce1319712760bf61fa1b6b52c8e9feb609bf6caa07b41ed80c7423cbe5390ec9851a2629d9d307971515c0a1696b0d4d52deabab089b945326b1e62a6ec5eb2cf2c58a5af0404962923a0c8605b6d55fd899765b193db8f5a849ecab478e187476779cf5f08d38a812b14ddff6143870ce5d9afc3e2eda91624067654940856421f5a8e0f6e3eefe888bd11c9364a29e2821374a1550e888359230ba863424746c793708e0ba23be967fa527ee5989eaf48541928e594ea846d67d5a6a56aa886be7b693d662cd675c786687c0d870bf19ab7564fb0b87bc6bba157ae04f482fca52858f8ff600141efa24f2f29cb0b8e222d88d9bfd9e60818474e55cc2b11e70125a956934c75cad4207f83b372c501ca7864bf8d387d0f81ec1cc872ff180");
|
||||
const TAG: [u8; 16] = hex!("17d7dca595adcaaddf537a1e9fd02854");
|
||||
|
||||
#[bench]
|
||||
fn ascon_decrypt_bench(b: &mut Bencher) {
|
||||
let aead = AsconAead::<12, 8>::new(&KEY.into());
|
||||
b.bytes = CIPHERTEXT.len() as u64;
|
||||
b.iter(|| {
|
||||
let mut buffer = CIPHERTEXT;
|
||||
aead.decrypt_in_place_detached(&NONCE.into(), &AAD, &mut buffer, &TAG.into())
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn ascon_encrypt_bench(b: &mut Bencher) {
|
||||
let aead = AsconAead::<12, 8>::new(&KEY.into());
|
||||
b.bytes = PLAINTEXT.len() as u64;
|
||||
b.iter(|| {
|
||||
let mut buffer = PLAINTEXT;
|
||||
aead.encrypt_in_place_detached(&NONCE.into(), &AAD, &mut buffer)
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
@ -1,219 +0,0 @@
|
||||
//! Authenticated Encryption with Associated Data.
|
||||
|
||||
pub use ::aead::{self, AeadCore, AeadInPlace, Error, KeyInit, KeySizeUser};
|
||||
|
||||
use crate::{Ascon, KEY_SIZE, RATE, S_SIZE};
|
||||
use ::aead::consts::{U0, U16};
|
||||
|
||||
// TODO(tarcieri): remove hard dependency on alloc
|
||||
use alloc::vec;
|
||||
|
||||
/// Ascon AEAD key.
|
||||
pub type Key = ::aead::generic_array::GenericArray<u8, U16>;
|
||||
|
||||
/// Ascon AEAD nonce.
|
||||
pub type Nonce = ::aead::generic_array::GenericArray<u8, U16>;
|
||||
|
||||
/// Ascon AEAD authentication tag.
|
||||
pub type Tag = ::aead::generic_array::GenericArray<u8, U16>;
|
||||
|
||||
/// Ascon AEAD encryption.
|
||||
#[derive(Clone)]
|
||||
pub struct AsconAead<const A: usize = 12, const B: usize = 8> {
|
||||
key: Key,
|
||||
}
|
||||
|
||||
impl<const A: usize, const B: usize> KeySizeUser for AsconAead<A, B> {
|
||||
type KeySize = U16;
|
||||
}
|
||||
|
||||
impl<const A: usize, const B: usize> KeyInit for AsconAead<A, B> {
|
||||
fn new(key_bytes: &Key) -> Self {
|
||||
Self { key: *key_bytes }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const A: usize, const B: usize> AeadCore for AsconAead<A, B> {
|
||||
type NonceSize = U16;
|
||||
type TagSize = U16;
|
||||
type CiphertextOverhead = U0;
|
||||
}
|
||||
|
||||
impl<const A: usize, const B: usize> AeadInPlace for AsconAead<A, B> {
|
||||
fn encrypt_in_place_detached(
|
||||
&self,
|
||||
nonce: &Nonce,
|
||||
aad: &[u8],
|
||||
buffer: &mut [u8],
|
||||
) -> Result<Tag, Error> {
|
||||
let s = aad.len() / RATE + 1;
|
||||
let t = buffer.len() / RATE + 1;
|
||||
let l = buffer.len() % RATE;
|
||||
|
||||
let mut aa = vec![0; s * RATE];
|
||||
let mut mm = vec![0; t * RATE];
|
||||
|
||||
// pad aad
|
||||
aa[..aad.len()].copy_from_slice(aad);
|
||||
aa[aad.len()] = 0x80;
|
||||
|
||||
// pad message
|
||||
mm[..buffer.len()].copy_from_slice(buffer);
|
||||
mm[buffer.len()] = 0x80;
|
||||
|
||||
// init
|
||||
let mut ss = Ascon::<A, B>::new(self.key.as_ref(), nonce.as_ref());
|
||||
|
||||
// aad
|
||||
if !aad.is_empty() {
|
||||
process_aad(&mut ss, &aa, s);
|
||||
}
|
||||
|
||||
ss.state[S_SIZE - 1] ^= 1;
|
||||
|
||||
// plaintext
|
||||
for i in 0..(t - 1) {
|
||||
for j in 0..RATE {
|
||||
ss.state[j] ^= mm[i * RATE + j];
|
||||
}
|
||||
buffer[(i * RATE)..(i * RATE + RATE)].copy_from_slice(&ss.state[..RATE]);
|
||||
ss.permutation(12 - B, B);
|
||||
}
|
||||
|
||||
for j in 0..RATE {
|
||||
ss.state[j] ^= mm[(t - 1) * RATE + j];
|
||||
}
|
||||
|
||||
for j in 0..l {
|
||||
buffer[(t - 1) * RATE + j] = ss.state[j];
|
||||
}
|
||||
|
||||
// tag
|
||||
let mut tag = Tag::default();
|
||||
tag.copy_from_slice(&ss.finalize()[S_SIZE - KEY_SIZE..]);
|
||||
|
||||
Ok(tag)
|
||||
}
|
||||
|
||||
fn decrypt_in_place_detached(
|
||||
&self,
|
||||
nonce: &Nonce,
|
||||
aad: &[u8],
|
||||
buffer: &mut [u8],
|
||||
tag: &Tag,
|
||||
) -> Result<(), Error> {
|
||||
let s = aad.len() / RATE + 1;
|
||||
let t = buffer.len() / RATE + 1;
|
||||
let l = buffer.len() % RATE;
|
||||
|
||||
let mut aa = vec![0; s * RATE];
|
||||
let mut mm = vec![0; t * RATE];
|
||||
|
||||
// pad aad
|
||||
aa[..aad.len()].copy_from_slice(aad);
|
||||
aa[aad.len()] = 0x80;
|
||||
|
||||
// init
|
||||
let mut ss = Ascon::<A, B>::new(self.key.as_ref(), nonce.as_ref());
|
||||
|
||||
// aad
|
||||
if !aad.is_empty() {
|
||||
process_aad(&mut ss, &aa, s);
|
||||
}
|
||||
|
||||
ss.state[S_SIZE - 1] ^= 1;
|
||||
|
||||
// ciphertext
|
||||
for i in 0..(t - 1) {
|
||||
for j in 0..RATE {
|
||||
mm[i * RATE + j] = ss.state[j] ^ buffer[i * RATE + j];
|
||||
}
|
||||
ss.state[..RATE].copy_from_slice(&buffer[(i * RATE)..(i * RATE + RATE)]);
|
||||
ss.permutation(12 - B, B);
|
||||
}
|
||||
|
||||
for j in 0..l {
|
||||
mm[(t - 1) * RATE + j] = ss.state[j] ^ buffer[(t - 1) * RATE + j];
|
||||
}
|
||||
|
||||
for j in 0..l {
|
||||
ss.state[j] = buffer[(t - 1) * RATE + j];
|
||||
}
|
||||
|
||||
ss.state[l] ^= 0x80;
|
||||
|
||||
// finalization
|
||||
let expected_tag = ss.finalize();
|
||||
|
||||
use subtle::ConstantTimeEq;
|
||||
if expected_tag[S_SIZE - KEY_SIZE..]
|
||||
.ct_eq(tag.as_slice())
|
||||
.into()
|
||||
{
|
||||
buffer.copy_from_slice(&mm[..buffer.len()]);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn process_aad<const A: usize, const B: usize>(ss: &mut Ascon<A, B>, aa: &[u8], s: usize) {
|
||||
for i in 0..s {
|
||||
for j in 0..RATE {
|
||||
ss.state[j] ^= aa[i * RATE + j];
|
||||
}
|
||||
|
||||
ss.permutation(12 - B, B);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{AeadInPlace, AsconAead, KeyInit};
|
||||
use hex_literal::hex;
|
||||
|
||||
#[test]
|
||||
fn round_trip() {
|
||||
let key = [0; 16];
|
||||
let nonce = [0; 16];
|
||||
let aad = [0; 16];
|
||||
let message = [0; 64];
|
||||
|
||||
let aead = AsconAead::<12, 8>::new(&key.into());
|
||||
let mut buffer = message;
|
||||
|
||||
let tag = aead
|
||||
.encrypt_in_place_detached(&nonce.into(), &aad, &mut buffer)
|
||||
.unwrap();
|
||||
aead.decrypt_in_place_detached(&nonce.into(), &aad, &mut buffer, &tag)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(message, buffer);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vectors() {
|
||||
const KEY: [u8; 16] = [0; 16];
|
||||
const NONCE: [u8; 16] = [0; 16];
|
||||
const AAD: &[u8; 5] = b"ASCON";
|
||||
const PLAINTEXT: &[u8; 5] = b"ascon";
|
||||
const CIPHERTEXT: &[u8; 5] = &hex!("4c8c428949");
|
||||
const TAG: &[u8; 16] = &hex!("65fd17b6d30cd876a05a8efcecad993a");
|
||||
|
||||
let aead = AsconAead::<12, 8>::new(&KEY.into());
|
||||
let mut buffer = *PLAINTEXT;
|
||||
|
||||
let tag = aead
|
||||
.encrypt_in_place_detached(&NONCE.into(), AAD, &mut buffer)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(CIPHERTEXT, &buffer);
|
||||
assert_eq!(TAG, tag.as_slice());
|
||||
|
||||
aead.decrypt_in_place_detached(&NONCE.into(), AAD, &mut buffer, &tag)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(PLAINTEXT, &buffer);
|
||||
}
|
||||
}
|
||||
515
ascon/src/lib.rs
515
ascon/src/lib.rs
@ -1,3 +1,6 @@
|
||||
// Copyright 2021-2022 Sebastian Ramacher
|
||||
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
#![no_std]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![doc = include_str!("../README.md")]
|
||||
@ -6,161 +9,405 @@
|
||||
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
|
||||
)]
|
||||
#![forbid(unsafe_code)]
|
||||
#![warn(
|
||||
clippy::mod_module_files,
|
||||
clippy::unwrap_used,
|
||||
missing_docs,
|
||||
rust_2018_idioms,
|
||||
unused_lifetimes,
|
||||
unused_qualifications
|
||||
)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
use core::mem::size_of;
|
||||
|
||||
#[cfg(feature = "aead")]
|
||||
pub mod aead;
|
||||
|
||||
#[cfg(feature = "aead")]
|
||||
pub use crate::aead::AsconAead;
|
||||
|
||||
use core::convert::TryInto;
|
||||
|
||||
/// Size of an Ascon key in bytes.
|
||||
const KEY_SIZE: usize = 16;
|
||||
|
||||
/// Size of an Ascon nonce in bytes.
|
||||
const NONCE_SIZE: usize = 16;
|
||||
|
||||
/// State size in bits.
|
||||
const S_BITS: usize = 320;
|
||||
|
||||
/// State size.
|
||||
const S_SIZE: usize = S_BITS / 8;
|
||||
|
||||
/// Rate: Sᵣ.
|
||||
const RATE: usize = 128 / 8;
|
||||
|
||||
/// Ascon permutation key.
|
||||
pub type Key = [u8; KEY_SIZE];
|
||||
|
||||
/// Ascon nonce.
|
||||
pub type Nonce = [u8; NONCE_SIZE];
|
||||
|
||||
/// Ascon permutation state.
|
||||
type State = [u8; S_SIZE];
|
||||
|
||||
/// Ascon(a,b) permutation.
|
||||
pub struct Ascon<const A: usize = 12, const B: usize = 8> {
|
||||
key: Key,
|
||||
state: State,
|
||||
/// Produce mask for padding.
|
||||
#[inline(always)]
|
||||
pub const fn pad(n: usize) -> u64 {
|
||||
0x80_u64 << (56 - 8 * n)
|
||||
}
|
||||
|
||||
impl<const A: usize, const B: usize> Ascon<A, B> {
|
||||
/// Initialize Ascon permutation.
|
||||
pub fn new(key: &Key, nonce: &Nonce) -> Self {
|
||||
let mut state = [0; S_SIZE];
|
||||
state[0] = KEY_SIZE as u8 * 8;
|
||||
state[1] = RATE as u8 * 8;
|
||||
state[2] = A as u8;
|
||||
state[3] = B as u8;
|
||||
/// Compute round constant
|
||||
#[inline(always)]
|
||||
const fn round_constant(round: u64) -> u64 {
|
||||
((0xfu64 - round) << 4) | round
|
||||
}
|
||||
|
||||
let pos = S_SIZE - KEY_SIZE - NONCE_SIZE;
|
||||
let (k, n) = state[pos..].split_at_mut(KEY_SIZE);
|
||||
k.copy_from_slice(key);
|
||||
n.copy_from_slice(nonce);
|
||||
/// The state of Ascon's permutation.
|
||||
///
|
||||
/// The permutation operates on a state of 320 bits represented as 5 64 bit words.
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct State {
|
||||
x: [u64; 5],
|
||||
}
|
||||
|
||||
permutation(&mut state, 12 - A, A);
|
||||
/// Ascon's round function
|
||||
const fn round(x: [u64; 5], c: u64) -> [u64; 5] {
|
||||
// S-box layer
|
||||
let x0 = x[0] ^ x[4];
|
||||
let x2 = x[2] ^ x[1] ^ c; // with round constant
|
||||
let x4 = x[4] ^ x[3];
|
||||
|
||||
for (i, &b) in key.iter().enumerate() {
|
||||
state[pos + KEY_SIZE + i] ^= b;
|
||||
let tx0 = x0 ^ (!x[1] & x2);
|
||||
let tx1 = x[1] ^ (!x2 & x[3]);
|
||||
let tx2 = x2 ^ (!x[3] & x4);
|
||||
let tx3 = x[3] ^ (!x4 & x0);
|
||||
let tx4 = x4 ^ (!x0 & x[1]);
|
||||
let tx1 = tx1 ^ tx0;
|
||||
let tx3 = tx3 ^ tx2;
|
||||
let tx0 = tx0 ^ tx4;
|
||||
|
||||
// linear layer
|
||||
let x0 = tx0 ^ tx0.rotate_right(9);
|
||||
let x1 = tx1 ^ tx1.rotate_right(22);
|
||||
let x2 = tx2 ^ tx2.rotate_right(5);
|
||||
let x3 = tx3 ^ tx3.rotate_right(7);
|
||||
let x4 = tx4 ^ tx4.rotate_right(34);
|
||||
[
|
||||
tx0 ^ x0.rotate_right(19),
|
||||
tx1 ^ x1.rotate_right(39),
|
||||
!(tx2 ^ x2.rotate_right(1)),
|
||||
tx3 ^ x3.rotate_right(10),
|
||||
tx4 ^ x4.rotate_right(7),
|
||||
]
|
||||
}
|
||||
|
||||
impl State {
|
||||
/// Instantiate new state from the given values.
|
||||
pub fn new(x0: u64, x1: u64, x2: u64, x3: u64, x4: u64) -> Self {
|
||||
State {
|
||||
x: [x0, x1, x2, x3, x4],
|
||||
}
|
||||
|
||||
Self { key: *key, state }
|
||||
}
|
||||
|
||||
/// Perform Ascon permutation on internal state.
|
||||
pub fn permutation(&mut self, start: usize, rounds: usize) {
|
||||
permutation(&mut self.state, start, rounds);
|
||||
#[cfg(not(feature = "no_unroll"))]
|
||||
/// Perform permutation with 12 rounds.
|
||||
pub fn permute_12(&mut self) {
|
||||
// We could in theory iter().fold() over an array of round constants,
|
||||
// but the compiler produces better results when optimizing this chain
|
||||
// of round function calls.
|
||||
self.x = round(
|
||||
round(
|
||||
round(
|
||||
round(
|
||||
round(
|
||||
round(
|
||||
round(
|
||||
round(
|
||||
round(round(round(round(self.x, 0xf0), 0xe1), 0xd2), 0xc3),
|
||||
0xb4,
|
||||
),
|
||||
0xa5,
|
||||
),
|
||||
0x96,
|
||||
),
|
||||
0x87,
|
||||
),
|
||||
0x78,
|
||||
),
|
||||
0x69,
|
||||
),
|
||||
0x5a,
|
||||
),
|
||||
0x4b,
|
||||
);
|
||||
}
|
||||
|
||||
/// Finalize Ascon permutation.
|
||||
#[cfg(feature = "no_unroll")]
|
||||
/// Perform permutation with 12 rounds.
|
||||
pub fn permute_12(&mut self) {
|
||||
self.x = [
|
||||
0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b,
|
||||
]
|
||||
.into_iter()
|
||||
.fold(self.x, round);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_unroll"))]
|
||||
/// Perform permutation with 8 rounds.
|
||||
pub fn permute_8(&mut self) {
|
||||
self.x = round(
|
||||
round(
|
||||
round(
|
||||
round(
|
||||
round(round(round(round(self.x, 0xb4), 0xa5), 0x96), 0x87),
|
||||
0x78,
|
||||
),
|
||||
0x69,
|
||||
),
|
||||
0x5a,
|
||||
),
|
||||
0x4b,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_unroll")]
|
||||
/// Perform permutation with 8 rounds.
|
||||
pub fn permute_8(&mut self) {
|
||||
self.x = [0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b]
|
||||
.into_iter()
|
||||
.fold(self.x, round);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_unroll"))]
|
||||
/// Perform permutation with 6 rounds.
|
||||
pub fn permute_6(&mut self) {
|
||||
self.x = round(
|
||||
round(
|
||||
round(round(round(round(self.x, 0x96), 0x87), 0x78), 0x69),
|
||||
0x5a,
|
||||
),
|
||||
0x4b,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_unroll")]
|
||||
/// Perform permutation with 6 rounds.
|
||||
pub fn permute_6(&mut self) {
|
||||
self.x = [0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b]
|
||||
.into_iter()
|
||||
.fold(self.x, round);
|
||||
}
|
||||
|
||||
/// Perform permutation with 1 round
|
||||
pub fn permute_1(&mut self) {
|
||||
self.x = round(self.x, 0x4b);
|
||||
}
|
||||
|
||||
/// Perform a given number (up to 12) of permutations
|
||||
///
|
||||
/// Panics (in debug mode) if `rounds` is larger than 12.
|
||||
pub fn permute_n(&mut self, rounds: usize) {
|
||||
debug_assert!(rounds <= 12);
|
||||
|
||||
let start = 12 - rounds;
|
||||
self.x = (start..12).fold(self.x, |x, round_index| {
|
||||
round(x, round_constant(round_index as u64))
|
||||
});
|
||||
}
|
||||
|
||||
/// Convert state to bytes.
|
||||
pub fn as_bytes(&self) -> [u8; 40] {
|
||||
let mut bytes = [0u8; size_of::<u64>() * 5];
|
||||
for (dst, src) in bytes
|
||||
.chunks_exact_mut(size_of::<u64>())
|
||||
.zip(self.x.into_iter())
|
||||
{
|
||||
dst.copy_from_slice(&u64::to_be_bytes(src));
|
||||
}
|
||||
bytes
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::Index<usize> for State {
|
||||
type Output = u64;
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn finalize(self) -> [u8; S_SIZE] {
|
||||
let mut s = self.state;
|
||||
|
||||
for (i, &b) in self.key.iter().enumerate() {
|
||||
s[RATE + i] ^= b;
|
||||
}
|
||||
|
||||
permutation(&mut s, 12 - A, A);
|
||||
|
||||
for (i, &b) in self.key.iter().enumerate() {
|
||||
s[S_SIZE - KEY_SIZE + i] ^= b;
|
||||
}
|
||||
|
||||
s
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
&self.x[index]
|
||||
}
|
||||
}
|
||||
|
||||
/// Ascon permutation.
|
||||
// TODO(tarcieri): change `s` to `&mut [u8; 40]`
|
||||
fn permutation(s: &mut [u8], start: usize, rounds: usize) {
|
||||
let mut x = [0; 5];
|
||||
let mut t = [0; 5];
|
||||
impl core::ops::IndexMut<usize> for State {
|
||||
#[inline(always)]
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
&mut self.x[index]
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(s.len(), 8 * x.len());
|
||||
impl TryFrom<&[u64]> for State {
|
||||
type Error = ();
|
||||
|
||||
// TODO(tarcieri): use `array_chunks` to eliminate `unwrap`
|
||||
#[allow(clippy::unwrap_used)]
|
||||
s.chunks_exact(8)
|
||||
.map(|c| c.try_into().unwrap())
|
||||
.map(u64::from_be_bytes)
|
||||
.zip(x.iter_mut())
|
||||
.for_each(|(inp, out)| *out = inp);
|
||||
fn try_from(value: &[u64]) -> Result<Self, Self::Error> {
|
||||
match value.len() {
|
||||
5 => Ok(Self::new(value[0], value[1], value[2], value[3], value[4])),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i in start as u64..(start + rounds) as u64 {
|
||||
x[2] ^= ((0xfu64 - i) << 4) | i;
|
||||
impl From<&[u64; 5]> for State {
|
||||
fn from(value: &[u64; 5]) -> Self {
|
||||
Self { x: *value }
|
||||
}
|
||||
}
|
||||
|
||||
x[0] ^= x[4];
|
||||
x[4] ^= x[3];
|
||||
x[2] ^= x[1];
|
||||
t[0] = x[0];
|
||||
t[1] = x[1];
|
||||
t[2] = x[2];
|
||||
t[3] = x[3];
|
||||
t[4] = x[4];
|
||||
t[0] = !t[0];
|
||||
t[1] = !t[1];
|
||||
t[2] = !t[2];
|
||||
t[3] = !t[3];
|
||||
t[4] = !t[4];
|
||||
t[0] &= x[1];
|
||||
t[1] &= x[2];
|
||||
t[2] &= x[3];
|
||||
t[3] &= x[4];
|
||||
t[4] &= x[0];
|
||||
x[0] ^= t[1];
|
||||
x[1] ^= t[2];
|
||||
x[2] ^= t[3];
|
||||
x[3] ^= t[4];
|
||||
x[4] ^= t[0];
|
||||
x[1] ^= x[0];
|
||||
x[0] ^= x[4];
|
||||
x[3] ^= x[2];
|
||||
x[2] = !x[2];
|
||||
impl TryFrom<&[u8]> for State {
|
||||
type Error = ();
|
||||
|
||||
x[0] ^= x[0].rotate_right(19) ^ x[0].rotate_right(28);
|
||||
x[1] ^= x[1].rotate_right(61) ^ x[1].rotate_right(39);
|
||||
x[2] ^= x[2].rotate_right(1) ^ x[2].rotate_right(6);
|
||||
x[3] ^= x[3].rotate_right(10) ^ x[3].rotate_right(17);
|
||||
x[4] ^= x[4].rotate_right(7) ^ x[4].rotate_right(41);
|
||||
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
|
||||
if value.len() != core::mem::size_of::<u64>() * 5 {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let mut state = Self::default();
|
||||
for (src, dst) in value
|
||||
.chunks_exact(core::mem::size_of::<u64>())
|
||||
.zip(state.x.iter_mut())
|
||||
{
|
||||
*dst = u64::from_be_bytes(src.try_into().unwrap());
|
||||
}
|
||||
Ok(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8; size_of::<u64>() * 5]> for State {
|
||||
fn from(value: &[u8; size_of::<u64>() * 5]) -> Self {
|
||||
let mut state = Self::default();
|
||||
for (src, dst) in value
|
||||
.chunks_exact(core::mem::size_of::<u64>())
|
||||
.zip(state.x.iter_mut())
|
||||
{
|
||||
*dst = u64::from_be_bytes(src.try_into().unwrap());
|
||||
}
|
||||
state
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u64]> for State {
|
||||
fn as_ref(&self) -> &[u64] {
|
||||
&self.x
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn pad_0to7() {
|
||||
assert_eq!(pad(0), 0x8000000000000000);
|
||||
assert_eq!(pad(1), 0x80000000000000);
|
||||
assert_eq!(pad(2), 0x800000000000);
|
||||
assert_eq!(pad(3), 0x8000000000);
|
||||
assert_eq!(pad(4), 0x80000000);
|
||||
assert_eq!(pad(5), 0x800000);
|
||||
assert_eq!(pad(6), 0x8000);
|
||||
assert_eq!(pad(7), 0x80);
|
||||
}
|
||||
|
||||
x.into_iter()
|
||||
.map(u64::to_be_bytes)
|
||||
// TODO: replace with `array_chunks_mut` on stabilization
|
||||
.zip(s.chunks_exact_mut(8))
|
||||
.for_each(|(inp, out)| out.copy_from_slice(&inp))
|
||||
#[test]
|
||||
fn round_constants() {
|
||||
assert_eq!(round_constant(0), 0xf0);
|
||||
assert_eq!(round_constant(1), 0xe1);
|
||||
assert_eq!(round_constant(2), 0xd2);
|
||||
assert_eq!(round_constant(3), 0xc3);
|
||||
assert_eq!(round_constant(4), 0xb4);
|
||||
assert_eq!(round_constant(5), 0xa5);
|
||||
assert_eq!(round_constant(6), 0x96);
|
||||
assert_eq!(round_constant(7), 0x87);
|
||||
assert_eq!(round_constant(8), 0x78);
|
||||
assert_eq!(round_constant(9), 0x69);
|
||||
assert_eq!(round_constant(10), 0x5a);
|
||||
assert_eq!(round_constant(11), 0x4b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_round() {
|
||||
let state = round(
|
||||
[
|
||||
0x0123456789abcdef,
|
||||
0x23456789abcdef01,
|
||||
0x456789abcdef0123,
|
||||
0x6789abcdef012345,
|
||||
0x89abcde01234567f,
|
||||
],
|
||||
0x1f,
|
||||
);
|
||||
assert_eq!(
|
||||
state,
|
||||
[
|
||||
0x3c1748c9be2892ce,
|
||||
0x5eafb305cd26164f,
|
||||
0xf9470254bb3a4213,
|
||||
0xf0428daf0c5d3948,
|
||||
0x281375af0b294899
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn state_permute_12() {
|
||||
let mut state = State::new(
|
||||
0x0123456789abcdef,
|
||||
0xef0123456789abcd,
|
||||
0xcdef0123456789ab,
|
||||
0xabcdef0123456789,
|
||||
0x89abcdef01234567,
|
||||
);
|
||||
state.permute_12();
|
||||
assert_eq!(state[0], 0x206416dfc624bb14);
|
||||
assert_eq!(state[1], 0x1b0c47a601058aab);
|
||||
assert_eq!(state[2], 0x8934cfc93814cddd);
|
||||
assert_eq!(state[3], 0xa9738d287a748e4b);
|
||||
assert_eq!(state[4], 0xddd934f058afc7e1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn state_permute_6() {
|
||||
let mut state = State::new(
|
||||
0x0123456789abcdef,
|
||||
0xef0123456789abcd,
|
||||
0xcdef0123456789ab,
|
||||
0xabcdef0123456789,
|
||||
0x89abcdef01234567,
|
||||
);
|
||||
state.permute_6();
|
||||
assert_eq!(state[0], 0xc27b505c635eb07f);
|
||||
assert_eq!(state[1], 0xd388f5d2a72046fa);
|
||||
assert_eq!(state[2], 0x9e415c204d7b15e7);
|
||||
assert_eq!(state[3], 0xce0d71450fe44581);
|
||||
assert_eq!(state[4], 0xdd7c5fef57befe48);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn state_permute_8() {
|
||||
let mut state = State::new(
|
||||
0x0123456789abcdef,
|
||||
0xef0123456789abcd,
|
||||
0xcdef0123456789ab,
|
||||
0xabcdef0123456789,
|
||||
0x89abcdef01234567,
|
||||
);
|
||||
state.permute_8();
|
||||
assert_eq!(state[0], 0x67ed228272f46eee);
|
||||
assert_eq!(state[1], 0x80bc0b097aad7944);
|
||||
assert_eq!(state[2], 0x2fa599382c6db215);
|
||||
assert_eq!(state[3], 0x368133fae2f7667a);
|
||||
assert_eq!(state[4], 0x28cefb195a7c651c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn state_permute_n() {
|
||||
let mut state = State::new(
|
||||
0x0123456789abcdef,
|
||||
0xef0123456789abcd,
|
||||
0xcdef0123456789ab,
|
||||
0xabcdef0123456789,
|
||||
0x89abcdef01234567,
|
||||
);
|
||||
let mut state2 = state;
|
||||
|
||||
state.permute_6();
|
||||
state2.permute_n(6);
|
||||
assert_eq!(state.x, state2.x);
|
||||
|
||||
state.permute_8();
|
||||
state2.permute_n(8);
|
||||
assert_eq!(state.x, state2.x);
|
||||
|
||||
state.permute_12();
|
||||
state2.permute_n(12);
|
||||
assert_eq!(state.x, state2.x);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn state_convert_bytes() {
|
||||
let state = State::new(
|
||||
0x0123456789abcdef,
|
||||
0xef0123456789abcd,
|
||||
0xcdef0123456789ab,
|
||||
0xabcdef0123456789,
|
||||
0x89abcdef01234567,
|
||||
);
|
||||
let bytes = state.as_bytes();
|
||||
|
||||
let state2 = State::try_from(bytes.as_slice());
|
||||
assert_eq!(state2.expect("try_from bytes").x, state.x);
|
||||
|
||||
let state2 = State::from(&bytes);
|
||||
assert_eq!(state2.x, state.x);
|
||||
}
|
||||
}
|
||||
|
||||
33
benches/Cargo.toml
Normal file
33
benches/Cargo.toml
Normal file
@ -0,0 +1,33 @@
|
||||
[package]
|
||||
name = "benches"
|
||||
version = "0.0.0"
|
||||
description = "Criteration benchmarks of the sponges crates"
|
||||
authors = [
|
||||
"Sebastian Ramacher <sebastian.ramacher@ait.ac.at>",
|
||||
"RustCrypto Developers",
|
||||
]
|
||||
edition = "2021"
|
||||
rust-version = "1.60"
|
||||
publish = false
|
||||
|
||||
[workspace]
|
||||
|
||||
[dev-dependencies]
|
||||
ascon = { path = "../ascon" }
|
||||
criterion = "0.4"
|
||||
rand = { version = "0.8", default-features = false, features = [
|
||||
"std_rng",
|
||||
"getrandom",
|
||||
] }
|
||||
# workaround for underspecified dependencies from criterion
|
||||
# see also https://github.com/RustCrypto/formats/pull/720
|
||||
regex = { version = "1.5.1", default-features = false }
|
||||
serde = { version = "1.0.144", default-features = false }
|
||||
|
||||
[features]
|
||||
no_unroll = ["ascon?/no_unroll"]
|
||||
|
||||
[[bench]]
|
||||
name = "ascon"
|
||||
path = "src/ascon.rs"
|
||||
harness = false
|
||||
46
benches/src/ascon.rs
Normal file
46
benches/src/ascon.rs
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright 2022 Sebastian Ramacher
|
||||
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
use ascon::State;
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use rand::{rngs::StdRng, RngCore, SeedableRng};
|
||||
|
||||
fn criterion_bench_permutation(c: &mut Criterion) {
|
||||
let mut rng = StdRng::from_entropy();
|
||||
let mut state = State::new(
|
||||
rng.next_u64(),
|
||||
rng.next_u64(),
|
||||
rng.next_u64(),
|
||||
rng.next_u64(),
|
||||
rng.next_u64(),
|
||||
);
|
||||
|
||||
let mut c = c.benchmark_group("Permutation");
|
||||
c.bench_function("1 round", |b| {
|
||||
b.iter(|| {
|
||||
state.permute_1();
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("6 rounds", |b| {
|
||||
b.iter(|| {
|
||||
state.permute_6();
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("8 rounds", |b| {
|
||||
b.iter(|| {
|
||||
state.permute_8();
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("12 rounds", |b| {
|
||||
b.iter(|| {
|
||||
state.permute_12();
|
||||
})
|
||||
});
|
||||
c.finish();
|
||||
}
|
||||
|
||||
criterion_group!(bench_permutation, criterion_bench_permutation);
|
||||
criterion_main!(bench_permutation);
|
||||
Loading…
x
Reference in New Issue
Block a user