diff --git a/.github/workflows/ascon.yml b/.github/workflows/ascon.yml new file mode 100644 index 0000000..fcd2585 --- /dev/null +++ b/.github/workflows/ascon.yml @@ -0,0 +1,59 @@ +name: ascon + +on: + pull_request: + paths: + - ".github/workflows/ascon.yml" + - "ascon/**" + - "Cargo.*" + push: + branches: master + +defaults: + run: + working-directory: ascon + +env: + RUSTFLAGS: "-Dwarnings" + CARGO_INCREMENTAL: 0 + +jobs: + set-msrv: + uses: RustCrypto/actions/.github/workflows/set-msrv.yml@master + with: + msrv: 1.41.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 + + minimal-versions: + uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master + with: + working-directory: ${{ github.workflow }} + + test: + needs: set-msrv + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - ${{needs.set-msrv.outputs.msrv}} + - stable + steps: + - uses: actions/checkout@v3 + - uses: RustCrypto/actions/cargo-cache@master + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + override: true + - run: cargo test diff --git a/Cargo.lock b/Cargo.lock index 4b3e05d..98b8930 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,19 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ascon" +version = "0.1.4" +dependencies = [ + "byteorder", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "cpufeatures" version = "0.2.5" diff --git a/Cargo.toml b/Cargo.toml index 67087ef..58c5040 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,5 @@ [workspace] members = [ + "ascon", "keccak", ] diff --git a/ascon/Cargo.toml b/ascon/Cargo.toml new file mode 100644 index 0000000..c973914 --- /dev/null +++ b/ascon/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "ascon" +version = "0.1.4" +authors = ["quininer kel "] +description = "A implementation of ASCON authenticated encryption." +repository = "https://github.com/quininer/ascon" +license = "Apache-2.0 OR MIT" + +[dependencies] +byteorder = { version = "1.0", default-features = false } diff --git a/ascon/LICENSE-APACHE b/ascon/LICENSE-APACHE new file mode 100644 index 0000000..deed143 --- /dev/null +++ b/ascon/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright (c) 2016-2023 quininer kel, RustCrypto Developers + +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 + + http://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. diff --git a/ascon/LICENSE-MIT b/ascon/LICENSE-MIT new file mode 100644 index 0000000..e4ea684 --- /dev/null +++ b/ascon/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016-2023 quininer kel, RustCrypto Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/ascon/README.md b/ascon/README.md new file mode 100644 index 0000000..5dc4f46 --- /dev/null +++ b/ascon/README.md @@ -0,0 +1,7 @@ +ASCON +----- + +A implementation of [ASCON](http://ascon.iaik.tugraz.at) authenticated encryption. + +* [CAESAR competition](http://competitions.cr.yp.to/caesar.html) +* [Ascon Analysis](http://ascon.iaik.tugraz.at/analysis.html) diff --git a/ascon/benches/bench.rs b/ascon/benches/bench.rs new file mode 100644 index 0000000..0f51000 --- /dev/null +++ b/ascon/benches/bench.rs @@ -0,0 +1,30 @@ +#![feature(test)] + +extern crate ascon; +extern crate test; + +use ascon::{aead_decrypt, aead_encrypt}; +use test::Bencher; + +#[bench] +fn ascon_encrypt_bench(b: &mut Bencher) { + let key = [4; 16]; + let iv = [8; 16]; + let aad = [3; 16]; + let message = [99; 1025]; + + b.bytes = message.len() as u64; + b.iter(|| aead_encrypt(&key, &iv, &message, &aad)); +} + +#[bench] +fn ascon_decrypt_bench(b: &mut Bencher) { + let key = [4; 16]; + let iv = [8; 16]; + let aad = [3; 16]; + let message = [99; 1025]; + let (ciphertext, tag) = aead_encrypt(&key, &iv, &message, &aad); + + b.bytes = message.len() as u64; + b.iter(|| aead_decrypt(&key, &iv, &ciphertext, &aad, &tag)); +} diff --git a/ascon/src/lib.rs b/ascon/src/lib.rs new file mode 100644 index 0000000..2445a79 --- /dev/null +++ b/ascon/src/lib.rs @@ -0,0 +1,162 @@ +extern crate byteorder; + +mod ops; +mod util; + +use ops::{finalization, initialization, permutation, process_aad}; + +const KEY_LEN: usize = 16; +const S_SIZE: usize = 320 / 8; +const RATE: usize = 128 / 8; +const A: usize = 12; +const B: usize = 8; + +#[derive(Debug)] +pub enum DecryptFail { + TagLengthError, + AuthenticationFail, +} + +pub fn aead_encrypt(key: &[u8], iv: &[u8], message: &[u8], aad: &[u8]) -> (Vec, [u8; KEY_LEN]) { + let s = aad.len() / RATE + 1; + let t = message.len() / RATE + 1; + let l = message.len() % RATE; + + let mut ss = [0; S_SIZE]; + let mut aa = vec![0; s * RATE]; + let mut mm = vec![0; t * RATE]; + + let mut output = vec![0; message.len()]; + let mut tag = [0; KEY_LEN]; + + // pad aad + aa[..aad.len()].copy_from_slice(aad); + aa[aad.len()] = 0x80; + // pad message + mm[..message.len()].copy_from_slice(message); + mm[message.len()] = 0x80; + + // init + initialization(&mut ss, key, iv); + + // aad + if !aad.is_empty() { + process_aad(&mut ss, &aa, s); + } + ss[S_SIZE - 1] ^= 1; + + // plaintext + for i in 0..(t - 1) { + for j in 0..RATE { + ss[j] ^= mm[i * RATE + j]; + } + output[(i * RATE)..(i * RATE + RATE)].copy_from_slice(&ss[..RATE]); + permutation(&mut ss, 12 - B, B); + } + for j in 0..RATE { + ss[j] ^= mm[(t - 1) * RATE + j]; + } + for j in 0..l { + output[(t - 1) * RATE + j] = ss[j]; + } + + // finalization + finalization(&mut ss, key); + + // tag + tag.copy_from_slice(&ss[S_SIZE - KEY_LEN..]); + + (output, tag) +} + +pub fn aead_decrypt( + key: &[u8], + iv: &[u8], + ciphertext: &[u8], + aad: &[u8], + tag: &[u8], +) -> Result, DecryptFail> { + if tag.len() != KEY_LEN { + Err(DecryptFail::TagLengthError)? + }; + + let s = aad.len() / RATE + 1; + let t = ciphertext.len() / RATE + 1; + let l = ciphertext.len() % RATE; + + let mut ss = [0; S_SIZE]; + 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 + initialization(&mut ss, key, iv); + + // aad + if !aad.is_empty() { + process_aad(&mut ss, &aa, s); + } + ss[S_SIZE - 1] ^= 1; + + // ciphertext + for i in 0..(t - 1) { + for j in 0..RATE { + mm[i * RATE + j] = ss[j] ^ ciphertext[i * RATE + j]; + } + ss[..RATE].copy_from_slice(&ciphertext[(i * RATE)..(i * RATE + RATE)]); + permutation(&mut ss, 12 - B, B); + } + for j in 0..l { + mm[(t - 1) * RATE + j] = ss[j] ^ ciphertext[(t - 1) * RATE + j]; + } + for j in 0..l { + ss[j] = ciphertext[(t - 1) * RATE + j]; + } + ss[l] ^= 0x80; + + // finalization + finalization(&mut ss, key); + + if util::eq(&ss[S_SIZE - KEY_LEN..], tag) { + Ok(mm[..ciphertext.len()].into()) + } else { + Err(DecryptFail::AuthenticationFail) + } +} + +#[test] +fn ascon_test() { + let key = [0; 16]; + let iv = [0; 16]; + let aad = [0; 16]; + let message = [0; 64]; + + let (ciphertext, tag) = aead_encrypt(&key, &iv, &message, &aad); + let plaintext = aead_decrypt(&key, &iv, &ciphertext, &aad, &tag).unwrap(); + assert_eq!(plaintext, &message[..]); + assert!(util::eq(&message, &plaintext)); +} + +#[test] +fn ascon_tv_test() { + let key = [0; 16]; + let iv = [0; 16]; + let aad = b"ASCON"; + let message = b"ascon"; + + let (ciphertext, tag) = aead_encrypt(&key, &iv, message, aad); + assert_eq!(ciphertext, [0x4c, 0x8c, 0x42, 0x89, 0x49]); + assert_eq!( + tag, + [ + 0x65, 0xfd, 0x17, 0xb6, 0xd3, 0x0c, 0xd8, 0x76, 0xa0, 0x5a, 0x8e, 0xfc, 0xec, 0xad, + 0x99, 0x3a + ] + ); + + let plaintext = aead_decrypt(&key, &iv, &ciphertext, aad, &tag).unwrap(); + assert_eq!(plaintext, message); +} diff --git a/ascon/src/ops.rs b/ascon/src/ops.rs new file mode 100644 index 0000000..3781e34 --- /dev/null +++ b/ascon/src/ops.rs @@ -0,0 +1,84 @@ +use util::{u64_to_u8, u8_to_u64}; + +pub fn permutation(s: &mut [u8], start: usize, rounds: usize) { + let mut x = [0; 5]; + let mut t = [0; 5]; + u8_to_u64(s, &mut x); + + for i in start as u64..(start + rounds) as u64 { + x[2] ^= ((0xfu64 - i) << 4) | i; + + 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]; + + 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); + } + + u64_to_u8(&x, s); +} + +pub fn initialization(s: &mut [u8], key: &[u8], nonce: &[u8]) { + s[0] = ::KEY_LEN as u8 * 8; + s[1] = ::RATE as u8 * 8; + s[2] = ::A as u8; + s[3] = ::B as u8; + + let mut pos = ::S_SIZE - 2 * ::KEY_LEN; + s[pos..pos + key.len()].copy_from_slice(key); + pos += ::KEY_LEN; + s[pos..pos + nonce.len()].copy_from_slice(nonce); + + permutation(s, 12 - ::A, ::A); + + for (i, &b) in key.iter().enumerate() { + s[pos + i] ^= b; + } +} + +pub fn finalization(s: &mut [u8], key: &[u8]) { + for (i, &b) in key.iter().enumerate() { + s[::RATE + i] ^= b; + } + permutation(s, 12 - ::A, ::A); + for (i, &b) in key.iter().enumerate() { + s[::S_SIZE - ::KEY_LEN + i] ^= b; + } +} + +pub fn process_aad(ss: &mut [u8], aa: &[u8], s: usize) { + for i in 0..s { + for j in 0..::RATE { + ss[j] ^= aa[i * ::RATE + j]; + } + permutation(ss, 12 - ::B, ::B); + } +} diff --git a/ascon/src/util.rs b/ascon/src/util.rs new file mode 100644 index 0000000..8e932d0 --- /dev/null +++ b/ascon/src/util.rs @@ -0,0 +1,29 @@ +use byteorder::{BigEndian, ByteOrder}; + +pub type Endian = BigEndian; + +#[inline] +pub fn u8_to_u64(input: &[u8], output: &mut [u64]) { + for (i, b) in output.iter_mut().enumerate() { + *b = Endian::read_u64(&input[(i * 8)..((i + 1) * 8)]); + } +} + +#[inline] +pub fn u64_to_u8(input: &[u64], output: &mut [u8]) { + for (i, &b) in input.iter().enumerate() { + Endian::write_u64(&mut output[(i * 8)..((i + 1) * 8)], b) + } +} + +pub fn eq(a: &[u8], b: &[u8]) -> bool { + if a.len() != b.len() { + false + } else { + a.iter() + .zip(b) + .map(|(x, y)| x ^ y) + .fold(0, |sum, next| sum | next) + .eq(&0) + } +}