├── .github └── workflows │ └── cmake.yml ├── CMakeLists.txt ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── aegis128L.cc ├── aegis128L.h ├── aegis128L_benchmark.cc ├── aegis128L_test.cc ├── aegis128L_test.json ├── vec128.h ├── vec128_neon_intrinsics.h ├── vec128_ppc_intrinsics.h ├── vec128_x86_intrinsics.h └── vec256_tuples.h /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | BUILD_TYPE: Debug 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | container: 17 | image: 'ubuntu:24.04' 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | 22 | - name: Install dependencies 23 | run: | 24 | apt-get update -y 25 | apt-get install -y build-essential cmake libgtest-dev libabsl-dev libjsoncpp-dev libbenchmark-dev 26 | 27 | - name: Configure CMake 28 | run: cmake -B ./build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 29 | 30 | - name: Build 31 | run: cmake --build ./build --config ${{env.BUILD_TYPE}} 32 | 33 | - name: Test 34 | run: | 35 | cp aegis128L_test.json build 36 | cd ./build 37 | ctest -C ${{env.BUILD_TYPE}} 38 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | cmake_minimum_required(VERSION 3.10) 18 | 19 | # set the project name 20 | project(AEGIS VERSION 0.1) 21 | set(CMAKE_CXX_STANDARD 17) 22 | 23 | set(CMAKE_CXX_FLAGS "-O2 -msse2 -maes -msse3 -msse4.1") 24 | 25 | find_package(absl REQUIRED) 26 | find_package(jsoncpp REQUIRED) 27 | find_package(GTest REQUIRED) 28 | find_package(Threads REQUIRED) # Benchmark needs pthreads 29 | find_package(benchmark REQUIRED) # Find the Google Benchmark package 30 | 31 | enable_testing() 32 | 33 | add_executable(aegis128L_test aegis128L.cc aegis128L_test.cc) 34 | target_link_libraries(aegis128L_test gtest_main gtest absl::base absl::strings absl::random_random jsoncpp) 35 | add_test(NAME aegis128L_test_test COMMAND aegis128L_test) 36 | 37 | add_executable(aegis128L_benchmark aegis128L.cc aegis128L_benchmark.cc) 38 | target_link_libraries(aegis128L_benchmark benchmark::benchmark benchmark::benchmark_main absl::base absl::strings absl::random_random Threads::Threads) 39 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows [Google's Open Source Community 28 | Guidelines](https://opensource.google/conduct/). 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AEGIS128L for C++ 2 | 3 | AEGIS is an AEAD cipher family as specified in [AEGIS: A Fast Authenticated 4 | Encryption Algorithm](http://competitions.cr.yp.to/round1/aegisv1.pdf). 5 | 6 | This library is a low-level implementation of the AEGIS128L cipher. Please look 7 | at other libraries such as [Tink](https://github.com/google/tink) when you need 8 | a safe cryptographic API. 9 | 10 | This library is intended to be used by cryptographic frameworks that wrap this 11 | library in a safe API. Alternatively, this library is also useful for streaming 12 | operations, but this is in general unsafe. It can be made safe, if you follow 13 | the implementator's guide carefully. Please consult aegis128L.h for an API 14 | documentation. 15 | 16 | We support: 17 | 18 | - x86-64 platforms with AES-NI and SSE2, 19 | - ARM NEON, 20 | - PPC Altivec. 21 | 22 | This is not an officially supported Google product nor is there active 23 | development. We encourage independent forks of this code base. 24 | 25 | ## Building 26 | 27 | Under Ubuntu/Debian: 28 | 29 | ``` 30 | sudo apt-get install libgtest-dev libabsl-dev libjsoncpp-dev 31 | cmake . 32 | make 33 | ./aegis128L_test 34 | ``` 35 | -------------------------------------------------------------------------------- /aegis128L.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "aegis128L.h" 16 | 17 | namespace security { 18 | 19 | namespace aegis { 20 | 21 | using internal::AegisStage, internal::DIRTY, internal::RESET, internal::AAD, 22 | internal::ENCRYPT, internal::DECRYPT, internal::FINALIZED, 23 | internal::VERIFIED; 24 | 25 | namespace { 26 | 27 | inline ABSL_ATTRIBUTE_ALWAYS_INLINE bool CompareTags(Aegis128LTag tag, 28 | const char *expected_tag) { 29 | return Vec128Eq(tag, Vec128Load(expected_tag)); 30 | } 31 | 32 | // This function determines if we can safely load "size" number of bytes from 33 | // ptr. On Intel (and this file is pretty Intel specific already), we cannot 34 | // cause a page-fault, unless we cross page boundaries. For instance, with 32 35 | // bytes to not cross a page boundary, our 32-byte load must not start from the 36 | // last 32 bytes of a page. We check this by (ptr & 0xfe0) != 0xfe0, which is 37 | // only false for the range [0xfe1..0xfff], which amounts to what we want to 38 | // check. This trick generalises to all "size" values that are a power of two. 39 | // 40 | // While we could also use a dynamic page_size other than 4k by using, e.g. 41 | // sysconf(_SC_PAGE_SIZE), 4k is the most conservative choice, and hard-coding 42 | // this value allows the compiler to generate imm instructions, which are much 43 | // faster. 44 | inline ABSL_ATTRIBUTE_ALWAYS_INLINE bool IsWideLoadSafe(const void *ptr, 45 | size_t size) { 46 | #if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && \ 47 | !defined(THREAD_SANITIZER) && !defined(HWADDRESS_SANITIZER) 48 | #ifndef NDEBUG 49 | assert(!(size & (size - 1))); // Power of two? 50 | assert(size < 128); // Caller should use sane values. 51 | #endif 52 | constexpr uint16_t kPageSize = 4096; 53 | uint16_t mask = kPageSize - size; 54 | return ((reinterpret_cast(ptr) & mask) != mask); 55 | #else 56 | // Sanitizers catches us accessing beyond the bounds of ptr, therefore we 57 | // disable wide loads in these cases. 58 | return false; 59 | #endif 60 | } 61 | 62 | inline ABSL_ATTRIBUTE_ALWAYS_INLINE Aegis128LBlock MaskedLoad(const char *data, 63 | size_t offset, 64 | size_t size) { 65 | #ifndef NDEBUG 66 | assert(size <= 32); 67 | #endif 68 | const char *data_aligned = data - offset; 69 | if ((offset == 0 && size == 32) || IsWideLoadSafe(data_aligned, 32)) { 70 | return Vec256Load(data_aligned); 71 | } 72 | // We cannot do a full-load from the memory directly, as a 32-bytes wide load 73 | // might cause page-fault. We copy to scratch space and do a 32-byte load from 74 | // there. 75 | char tmp[32]; 76 | memmove(tmp + offset, data, size); 77 | return Vec256Load(tmp); 78 | } 79 | 80 | void MaskedStore(Aegis128LBlock block, char *data, size_t offset, size_t size) { 81 | #ifndef NDEBUG 82 | assert(size + offset <= 32); 83 | #endif 84 | if (size == 0) { 85 | return; 86 | } 87 | if (size == 32) { 88 | Vec256Store(data, block); 89 | return; 90 | } 91 | 92 | char tmp[32]; 93 | Vec256Store(tmp, block); 94 | memmove(data, tmp + offset, size); 95 | } 96 | 97 | const Vec128 mask_0x20_0x11 = 98 | MakeVec128Epi64x(0x201f1e1d1c1b1a19, 0x1817161514131211); 99 | const Vec128 mask_0x10_0x01 = 100 | MakeVec128Epi64x(0x100f0e0d0c0b0a09, 0x0807060504030201); 101 | 102 | // Constants from the AEGIS paper used in preparation of the IV 103 | Vec128 const0() { 104 | return MakeVec128Epi64x(0x6279e99059372215, 0x0d08050302010100); 105 | } 106 | Vec128 const1() { 107 | return MakeVec128Epi64x(0xdd28b57342311120, 0xf12fc26d55183ddb); 108 | } 109 | 110 | inline ABSL_ATTRIBUTE_ALWAYS_INLINE Aegis128LBlock KeyStream(Vec128 S[8]) { 111 | Vec128 even = Vec128And(S[2], S[3]); 112 | even = Vec128Xor(Vec128Xor(S[1], S[6]), even); 113 | 114 | Vec128 odd = Vec128And(S[6], S[7]); 115 | odd = Vec128Xor(Vec128Xor(S[2], S[5]), odd); 116 | return MakeVec256(even, odd); 117 | } 118 | 119 | inline ABSL_ATTRIBUTE_ALWAYS_INLINE void StateUpdateRounds(Vec128 S[8], 120 | Vec128 S0, 121 | Vec128 S4) { 122 | Vec128 w = Vec128AesRound(S[7], S0); 123 | S[7] = Vec128AesRound(S[6], S[7]); 124 | S[6] = Vec128AesRound(S[5], S[6]); 125 | S[5] = Vec128AesRound(S[4], S[5]); 126 | S[4] = Vec128AesRound(S[3], S4); 127 | S[3] = Vec128AesRound(S[2], S[3]); 128 | S[2] = Vec128AesRound(S[1], S[2]); 129 | S[1] = Vec128AesRound(S[0], S[1]); 130 | S[0] = w; 131 | } 132 | 133 | inline ABSL_ATTRIBUTE_ALWAYS_INLINE void StateUpdate(Vec128 S[8], 134 | Aegis128LBlock msg, 135 | size_t offset, 136 | size_t size) { 137 | #ifndef NDEBUG 138 | assert(size + offset <= 32); 139 | #endif 140 | Vec128 ma; 141 | Vec128 mb; 142 | 143 | std::tie(ma, mb) = msg; 144 | 145 | if (offset == 0) { 146 | StateUpdateRounds(S, S[0], S[4]); 147 | } else { 148 | Vec128 a = MakeVec128BroadcastEpi8(offset); 149 | Vec128 mask_a = Vec128CmpLtEpi8(a, mask_0x10_0x01); 150 | Vec128 mask_b = Vec128CmpLtEpi8(a, mask_0x20_0x11); 151 | ma = Vec128And(ma, mask_a); 152 | mb = Vec128And(mb, mask_b); 153 | } 154 | size_t end = size + offset; 155 | if (end != 32) { 156 | Vec128 a = MakeVec128BroadcastEpi8(end); 157 | Vec128 mask_a = Vec128CmpLtEpi8(a, mask_0x10_0x01); 158 | Vec128 mask_b = Vec128CmpLtEpi8(a, mask_0x20_0x11); 159 | ma = Vec128AndNot(mask_a, ma); 160 | mb = Vec128AndNot(mask_b, mb); 161 | } 162 | S[0] = Vec128Xor(ma, S[0]); 163 | S[4] = Vec128Xor(mb, S[4]); 164 | } 165 | 166 | } // namespace 167 | 168 | namespace internal { 169 | 170 | inline ABSL_ATTRIBUTE_ALWAYS_INLINE void Initialize(Vec128 S[8], Vec128 key, 171 | Vec128 iv) { 172 | S[0] = Vec128Xor(key, iv); 173 | S[1] = const1(); 174 | S[2] = const0(); 175 | S[3] = const1(); 176 | S[4] = Vec128Xor(key, iv); 177 | S[5] = Vec128Xor(key, const0()); 178 | S[6] = Vec128Xor(key, const1()); 179 | S[7] = Vec128Xor(key, const0()); 180 | Aegis128LBlock msg = MakeVec256(iv, key); 181 | for (int i = 0; i < 10; i++) { 182 | StateUpdate(S, msg, 0, 32); 183 | } 184 | } 185 | 186 | inline ABSL_ATTRIBUTE_ALWAYS_INLINE Vec128 Finalize(Vec128 S[8], 187 | size_t aad_size_in_bytes, 188 | size_t pt_size_in_bytes) { 189 | uint64_t aad_bits = 8 * aad_size_in_bytes; 190 | uint64_t pt_bits = 8 * pt_size_in_bytes; 191 | Vec128 tmp0 = MakeVec128Epi64x(pt_bits, aad_bits); 192 | Vec128 tmp = Vec128Xor(tmp0, S[2]); 193 | Aegis128LBlock msg = MakeVec256(tmp, tmp); 194 | for (int i = 0; i < 7; i++) { 195 | StateUpdate(S, msg, 0, 32); 196 | } 197 | Vec128 tag = Vec128Xor(S[0], S[1]); 198 | tag = Vec128Xor(tag, S[2]); 199 | tag = Vec128Xor(tag, S[3]); 200 | tag = Vec128Xor(tag, S[4]); 201 | tag = Vec128Xor(tag, S[5]); 202 | tag = Vec128Xor(tag, S[6]); 203 | // https://eprint.iacr.org/2013/695.pdf computes the tag as sum over S[0..7], 204 | // while http://competitions.cr.yp.to/round1/aegisv1.pdf computes the tag 205 | // over S[0..6] 206 | // tag = Vec128Xor(tag, S[7]); 207 | return tag; 208 | } 209 | 210 | // ProcessBlock unifies data processing across AAD/ENCRYPT/DECRYPT 211 | // runs, and is instantiated inside of Process, and works as follows: 212 | // 213 | // direction == AAD , update state from the input , don't store output. 214 | // direction == ENCRYPT, update state from the input , store output. 215 | // direction == DECRYPT, update state from the output, store output. 216 | // 217 | 218 | template 219 | // force inlining, as otherwise LLVM doesn't get the optimization in Process(..) 220 | inline ABSL_ATTRIBUTE_ALWAYS_INLINE void ProcessBlock( 221 | ResumableState *__restrict rs, const char *input, char *output, 222 | size_t offset, size_t size) { 223 | #ifndef NDEBUG 224 | assert(offset < 32); 225 | #endif 226 | /* This function is designed to completely be inlined including all the 227 | padding helpers. Those padding helpers are designed with bounds checking 228 | that can be optimized away when size is known at compile time. 229 | 230 | The intuition for the padding helpers are that they only opperate on the 231 | input up to n-th byte as given by the "size" argument, and will do nothing 232 | after that byte or assume zeros. */ 233 | 234 | if (!size) { 235 | return; 236 | } 237 | 238 | Aegis128LBlock in_block = MaskedLoad(input, offset, size); 239 | if (direction == AAD) { 240 | StateUpdate(rs->S, in_block, offset, size); 241 | } else if (direction == ENCRYPT || direction == DECRYPT) { 242 | // We resume a partial-block, the key-stream is stored in S[8]/S[9]. 243 | Aegis128LBlock key_stream = 244 | offset ? MakeVec256(rs->S[8], rs->S[9]) : KeyStream(rs->S); 245 | Aegis128LBlock out_block = Vec256Xor(in_block, key_stream); 246 | MaskedStore(out_block, output, offset, size); 247 | StateUpdate(rs->S, direction == ENCRYPT ? in_block : out_block, offset, 248 | size); 249 | if (offset + size != 32) { 250 | // We did not complete a block, store the key-stream in S[8]/S[9], so when 251 | // we pick up processing this partial block, the key-stream is available. 252 | rs->S[8] = std::get<0>(key_stream); 253 | rs->S[9] = std::get<1>(key_stream); 254 | } 255 | } 256 | } 257 | 258 | template 259 | inline ABSL_ATTRIBUTE_ALWAYS_INLINE void Process( 260 | ResumableState *__restrict__ rs, const char *input, char *output, 261 | size_t previous_block_offset, size_t size) { 262 | #ifndef NDEBUG 263 | assert(previous_block_offset < 32); 264 | #endif 265 | 266 | /* This function processes the input mostly in 32-byte chunks. However, it 267 | also has to take care of any incomplete blocks from its last invocation 268 | (indicated by a non-zero previous_block_offset) and also has to partially 269 | process a block, if processing does not end on a block boundary. As 270 | ProcessBlock is designed to be inlined here, the loop in this function does 271 | the heavy-lifting. */ 272 | 273 | if (previous_block_offset) { 274 | size_t bytes_left_in_previous_block = 32 - previous_block_offset; 275 | if (size <= bytes_left_in_previous_block) { 276 | ProcessBlock(rs, input, output, previous_block_offset, size); 277 | // No more processing necessary. 278 | return; 279 | } else { 280 | ProcessBlock(rs, input, output, previous_block_offset, 281 | bytes_left_in_previous_block); 282 | input += bytes_left_in_previous_block; 283 | output += bytes_left_in_previous_block; 284 | size -= bytes_left_in_previous_block; 285 | } 286 | } 287 | 288 | size_t full_blocks = size / 16; 289 | size_t i = 0; 290 | // Unroll this loop twice, as this seems to give better vectorization for AES 291 | // instructions. 292 | #pragma unroll(2) 293 | for (; i + 1 < full_blocks; i += 2) { 294 | // Calling ProcessBlock with a fixed size=32, removes its bounds checks. 295 | ProcessBlock(rs, input + i * 16, (direction != AAD ? output + i * 16 : nullptr), 0, 32); 296 | } 297 | ProcessBlock(rs, input + i * 16, (direction != AAD ? output + i * 16 : nullptr), 0, size % 32); 298 | } 299 | 300 | } // namespace internal 301 | 302 | Aegis128LPreKeyed::Aegis128LPreKeyed(absl::string_view key, 303 | std::function get_nonce) 304 | : key_(Vec128Load(key.data())), get_nonce_(get_nonce) { 305 | assert(key.size() == 16); 306 | } 307 | 308 | void Aegis128LPreKeyed::Encrypt(absl::string_view plaintext, 309 | absl::string_view aad, 310 | std::string *ciphertext) { 311 | // So far we use a 16 byte IV and a 16 byte tag. 312 | const size_t kIvSize = 16; 313 | const size_t kTagSize = 16; 314 | size_t ciphertext_size = plaintext.size() + kIvSize + kTagSize; 315 | ciphertext->resize(ciphertext_size); 316 | char *buffer = &(*ciphertext)[0]; 317 | Aegis128LNonce nonce = get_nonce_(); 318 | Vec128Store(buffer, nonce); 319 | Aegis128LState state; 320 | state.Reset(key_, nonce); 321 | state.AssociateData(aad); 322 | state.Encrypt(plaintext, buffer + kIvSize); 323 | state.Finalize(&buffer[ciphertext_size - 16]); 324 | } 325 | 326 | bool Aegis128LPreKeyed::Decrypt(absl::string_view ciphertext, 327 | absl::string_view aad, std::string *plaintext) { 328 | const size_t kIvSize = 16; 329 | const size_t kTagSize = 16; 330 | if (ciphertext.size() < kIvSize + kTagSize) { 331 | return false; 332 | } 333 | Aegis128LNonce iv = LoadNonce(&ciphertext[0]); 334 | size_t plaintext_size = ciphertext.size() - kIvSize - kTagSize; 335 | 336 | Aegis128LState state; 337 | state.Reset(key_, iv); 338 | state.AssociateData(aad); 339 | absl::string_view raw_ciphertext(&ciphertext[kIvSize], plaintext_size); 340 | plaintext->resize(plaintext_size); 341 | state.Decrypt(raw_ciphertext, &(*plaintext)[0]); 342 | return state.Verify(&ciphertext[ciphertext.size() - kTagSize], [&]() { 343 | // We are free to clear plaintext, as the contract with the caller is that 344 | // we are free to override this string. 345 | memset(&(*plaintext)[0], 0, plaintext_size); 346 | plaintext->resize(0); 347 | }); 348 | } 349 | 350 | void Aegis128LState::Reset(Aegis128LKey key, Aegis128LNonce iv) { 351 | CheckStage(RESET, {DIRTY, RESET, AAD, ENCRYPT, DECRYPT, FINALIZED, VERIFIED}); 352 | aad_size_ = 0; 353 | payload_size_ = 0; 354 | internal::Initialize(rs_.S, key, iv); 355 | } 356 | 357 | void Aegis128LState::AssociateData(const char *aad, size_t s) { 358 | CheckStage(AAD, {RESET, AAD}); 359 | internal::Process(&rs_, aad, nullptr, aad_size_ % 32, s); 360 | aad_size_ += s; 361 | } 362 | 363 | void Aegis128LState::Encrypt(const char *plaintext, size_t s, 364 | char *ciphertext) { 365 | CheckStage(ENCRYPT, {RESET, AAD, ENCRYPT}); 366 | internal::Process(&rs_, plaintext, ciphertext, payload_size_ % 32, 367 | s); 368 | payload_size_ += s; 369 | } 370 | 371 | void Aegis128LState::Decrypt(const char *ciphertext, size_t s, 372 | char *plaintext) { 373 | CheckStage(DECRYPT, {RESET, AAD, DECRYPT}); 374 | internal::Process(&rs_, ciphertext, plaintext, payload_size_ % 32, 375 | s); 376 | payload_size_ += s; 377 | } 378 | 379 | Aegis128LTag Aegis128LState::Finalize() { 380 | CheckStage(internal::FINALIZED, 381 | {internal::RESET, internal::AAD, internal::ENCRYPT}); 382 | return internal::Finalize(rs_.S, aad_size_, payload_size_); 383 | } 384 | 385 | bool Aegis128LState::Verify(const char *expected_tag, 386 | absl::FunctionRef destroy_buffer) { 387 | CheckStage(internal::VERIFIED, 388 | {internal::RESET, internal::AAD, internal::DECRYPT}); 389 | Aegis128LTag tag = internal::Finalize(rs_.S, aad_size_, payload_size_); 390 | if (!CompareTags(tag, expected_tag)) { 391 | destroy_buffer(); 392 | return false; 393 | } 394 | return true; 395 | } 396 | 397 | } // namespace aegis 398 | 399 | } // namespace security 400 | -------------------------------------------------------------------------------- /aegis128L.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef AEGIS_CIPHER_AEGIS128L_H_ 16 | #define AEGIS_CIPHER_AEGIS128L_H_ 17 | 18 | #include "absl/functional/function_ref.h" 19 | #include "absl/numeric/bits.h" 20 | #include "absl/strings/string_view.h" 21 | #include "vec128.h" 22 | #include "vec256_tuples.h" 23 | 24 | namespace security { 25 | 26 | namespace aegis { 27 | 28 | typedef Vec256 Aegis128LBlock; 29 | typedef Vec128 Aegis128LTag; 30 | typedef Vec128 Aegis128LNonce; 31 | typedef Vec128 Aegis128LKey; 32 | 33 | namespace internal { 34 | 35 | enum AegisStage { 36 | DIRTY = 0, 37 | RESET, 38 | AAD, 39 | ENCRYPT, 40 | DECRYPT, 41 | FINALIZED, 42 | VERIFIED 43 | }; 44 | 45 | struct ResumableState final { 46 | // S[0..7] is the internal state S as defined in the AEGIS paper. 47 | // S[8..9] might be used for the key stream when we process a partial block. 48 | 49 | // When we start a fresh block, we extract the key stream from S[0..7] and 50 | // update S[0..7]. When we find that the input does not cover a full block, we 51 | // save the key stream in S[8..9] and use that in the next invocation. 52 | 53 | // NOLINTNEXTLINE 54 | alignas(128) Vec128 S[10]; 55 | }; 56 | 57 | inline uint64_t LittleEndianFromHost64(uint64_t v) { 58 | #if defined(ABSL_IS_LITTLE_ENDIAN) 59 | return v; 60 | #elif defined(ABSL_IS_BIG_ENDIAN) 61 | return std::byteswap(v); 62 | #else 63 | #error ABSL did not define endianness. 64 | #endif 65 | } 66 | 67 | } // namespace internal 68 | 69 | inline Aegis128LKey LoadKey(const char *key) { return Vec128Load(key); } 70 | 71 | inline Aegis128LKey LoadKey(absl::string_view key) { 72 | assert(key.size() == 16); 73 | return Vec128Load(key.data()); 74 | } 75 | 76 | inline Aegis128LNonce LoadNonce(const char *nonce) { return Vec128Load(nonce); } 77 | 78 | inline Aegis128LNonce LoadNonce(absl::string_view nonce) { 79 | assert(nonce.size() == 16); 80 | return Vec128Load(nonce.data()); 81 | } 82 | 83 | // Load nonce from two numbers with n0 being the first part, and n1 being the 84 | // second. The nonce parts are assumed to be in little-endian ordering. 85 | inline Aegis128LNonce LoadNonceLE(uint64_t n1, uint64_t n0) { 86 | // While it is not particularly useful to have LittleEndianFromHost64 87 | // conversions in heavily Intel focused code, it reminds us that we expected 88 | // n1 & n0 to be in little-endian. 89 | return MakeVec128Epi64x(internal::LittleEndianFromHost64(n1), 90 | internal::LittleEndianFromHost64(n0)); 91 | } 92 | 93 | class Aegis128LPreKeyed final { 94 | public: 95 | // Aegis128LPreKeyed is a convience class for one-shot encryption and 96 | // decryption. Users must supply their own nonce generator and must take care 97 | // that nonces are not repeated in the context of a key. For instance, using a 98 | // a simple counter is not safe, when the key is shared between multiple 99 | // encrypting processes. We recommend to use a cryptographically secure random 100 | // generator to generate nonces. 101 | explicit Aegis128LPreKeyed(absl::string_view key, 102 | std::function get_nonce); 103 | // Directly encrypts to ciphertext. 104 | // ciphertext must not overlap with plaintext and aad. 105 | void Encrypt(absl::string_view plaintext, absl::string_view aad, 106 | std::string *ciphertext); 107 | 108 | // Directly decrypts to plaintext. 109 | // plaintext must not overlap with ciphertext and aad. 110 | // 111 | // Returns false when the tag verification fails. 112 | bool Decrypt(absl::string_view ciphertext, absl::string_view aad, 113 | std::string *plaintext); 114 | 115 | protected: 116 | friend class Aegis128LTest; 117 | 118 | private: 119 | const Aegis128LKey key_; 120 | std::function get_nonce_; 121 | }; 122 | 123 | class Aegis128LState final { 124 | public: 125 | // Aegis128LState is a low-level API that provides a streaming API for the 126 | // AEGIS128L cipher. Please read the documentation here in full to avoid 127 | // introducing vulnerabilities. 128 | // 129 | // Aegis128LState represents the Aegis AEAD cipher state. It must be Reset to 130 | // a key and an IV before use. 131 | // 132 | // ENCRYPTION: For encryption we expect the following call sequence: 133 | // Reset{1}->AssociateData{0,m} -> Encrypt{0,n} -> Finalize{1} 134 | // where AssociateData is called between 0 and m times, Encrypt is 135 | // called n times, and Finalize is called exactly once. 136 | // 137 | // DECRYPTION: For decryption we expect the following call sequence: 138 | // Reset{1}->AssociateData{0,m} -> Decrypt{0,n} -> Verify{1} 139 | // where AssociateData is called between 0 and m times, Decrypt is 140 | // called n times, and Verify is called exactly once. 141 | // 142 | // The Decrypt operation is generally safe, when the output is buffered and 143 | // released to further processing ONLY after the Verify operation has 144 | // successfully verified the cryptographic tag. If Verify fails, you MUST 145 | // destroy the buffered output, and you MUST do so in the destroy_buffer 146 | // callback handed to Verify. It is best to sanitize the buffer by memsetting 147 | // the content to zero. 148 | // 149 | // We generally advise to not do any interpretation of the decryption output 150 | // before calling Verify. If you do so, an attacker can feed modified 151 | // ciphertext to you, and then guess the plaintext content based on a 152 | // timing-side channel. Such a side-channel would allow an attacker to 153 | // reconstruct the internal state of Aegis128L and allow forgery attacks. 154 | // 155 | // Aegis128LState state; 156 | // state.Reset(key, iv); 157 | // 158 | // state.AssociateData(aad_1); 159 | // state.AssociateData(aad_2); 160 | // ... 161 | // state.AssociateData(aad_m); 162 | // 163 | // state.Decrypt(cipher_1, plain_1); 164 | // state.Decrypt(cipher_2, plain_2); 165 | // ... 166 | // state.Decrypt(cipher_n, plain_n); 167 | // 168 | // state.Verify( 169 | // tag, 170 | // [plain_1, ..., plain_n] () { 171 | // memset(plain_1, 0, sizeof(plain_1)); 172 | // .. 173 | // memset(plain_n, 0, sizeof(plain_n)); 174 | // }); 175 | // 176 | Aegis128LState() {} 177 | inline ~Aegis128LState() { 178 | // Scrub cipher state before freeing this object. 179 | memset(&rs_, 0, sizeof(internal::ResumableState)); 180 | __asm__ __volatile__("" : : "r"(&rs_) : "memory"); 181 | } 182 | 183 | // Explicitly disallow copying. There is no case where this can be argued to 184 | // be necessary as we never want reuse another state because all states are 185 | // bound to a nonce, which we never want to reuse. Use the default constructor 186 | // and Reset instead. 187 | Aegis128LState(const Aegis128LState &) = delete; 188 | Aegis128LState &operator=(const Aegis128LState &) = delete; 189 | 190 | void Reset(Aegis128LKey key, Aegis128LNonce iv); 191 | void Reset(absl::string_view key, absl::string_view iv) { 192 | Reset(LoadKey(key), LoadNonce(iv)); 193 | } 194 | 195 | // Adds associated data. 196 | void AssociateData(const char *aad, size_t s); 197 | void AssociateData(absl::string_view aad) { 198 | AssociateData(aad.data(), aad.size()); 199 | } 200 | 201 | // Encrypts plaintext into ciphertext. ciphertext needs to be able to hold 202 | // plaintext.size() bytes. 203 | void Encrypt(const char *plaintext, size_t s, char *ciphertext); 204 | void Encrypt(absl::string_view plaintext, char *ciphertext) { 205 | Encrypt(plaintext.data(), plaintext.size(), ciphertext); 206 | } 207 | 208 | // Decrypt ciphertext into plaintext. plaintext needs to be able to hold 209 | // ciphertext.size() bytes. 210 | void Decrypt(const char *ciphertext, size_t s, char *plaintext); 211 | void Decrypt(absl::string_view ciphertext, char *plaintext) { 212 | Decrypt(ciphertext.data(), ciphertext.size(), plaintext); 213 | } 214 | 215 | void Finalize(char *tag) { 216 | Aegis128LTag generated_tag = Finalize(); 217 | Vec128Store(tag, generated_tag); 218 | } 219 | 220 | // Finalizes the Aegis state and checks the result against tag. The caller 221 | // needs to provide a destruction function such that all buffers produced by 222 | // previous calls to Decrypt are destroyed. Please read the class 223 | // documentation on how to implement destroy_buffer. 224 | bool Verify(const char *expected_tag, absl::FunctionRef destroy_buffer); 225 | 226 | protected: 227 | friend class Aegis128LTest; 228 | 229 | private: 230 | internal::ResumableState rs_; 231 | size_t aad_size_; 232 | size_t payload_size_; 233 | #ifndef NDEBUG 234 | void CheckStage(internal::AegisStage current_stage, 235 | std::initializer_list allowed_stages) { 236 | assert(std::find(std::begin(allowed_stages), std::end(allowed_stages), 237 | stage_) != std::end(allowed_stages)); 238 | stage_ = current_stage; 239 | } 240 | internal::AegisStage stage_ = internal::DIRTY; 241 | #else 242 | void CheckStage(internal::AegisStage current_stage, 243 | std::initializer_list allowed_stages) { } 244 | #endif 245 | 246 | // Produces Aegis tag. 247 | Aegis128LTag Finalize(); 248 | }; 249 | 250 | } // namespace aegis 251 | 252 | } // namespace security 253 | 254 | #endif // AEGIS_CIPHER_AEGIS128L_H_ 255 | -------------------------------------------------------------------------------- /aegis128L_benchmark.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "aegis128L.h" 16 | 17 | #include "absl/strings/escaping.h" 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include "gtest/gtest.h" 27 | #include "absl/strings/string_view.h" 28 | 29 | namespace security { 30 | 31 | namespace aegis { 32 | 33 | void BM_AEGIS128L_ASSOCIATE(benchmark::State& bs) { 34 | const size_t size = bs.range(0); 35 | Aegis128LState state; 36 | state.Reset(absl::HexStringToBytes("10010000000000000000000000000000"), 37 | absl::HexStringToBytes("10000200000000000000000000000000")); 38 | 39 | std::string payload(size, '0'); 40 | for (auto s : bs) { 41 | state.AssociateData(payload); 42 | } 43 | bs.SetBytesProcessed(static_cast(bs.iterations()) * size); 44 | } 45 | 46 | void BM_AEGIS128L_ENCRYPT(benchmark::State& bs) { 47 | const size_t size = bs.range(0); 48 | Aegis128LState state; 49 | state.Reset(absl::HexStringToBytes("10010000000000000000000000000000"), 50 | absl::HexStringToBytes("10000200000000000000000000000000")); 51 | 52 | std::string payload(size, '0'); 53 | std::unique_ptr buffer{new char[size]}; 54 | char* buf = buffer.get(); 55 | for (auto s : bs) { 56 | state.Encrypt(payload, buf); 57 | } 58 | bs.SetBytesProcessed(static_cast(bs.iterations()) * size); 59 | } 60 | 61 | void BM_AEGIS128L_DECRYPT(benchmark::State& bs) { 62 | const size_t size = bs.range(0); 63 | Aegis128LState state; 64 | state.Reset(absl::HexStringToBytes("10010000000000000000000000000000"), 65 | absl::HexStringToBytes("10000200000000000000000000000000")); 66 | 67 | std::string payload(size, '0'); 68 | std::unique_ptr buffer{new char[size]}; 69 | char* buf = buffer.get(); 70 | for (auto s : bs) { 71 | state.Decrypt(payload, buf); 72 | } 73 | bs.SetBytesProcessed(static_cast(bs.iterations()) * size); 74 | } 75 | 76 | void BM_AEGIS128L_ENCRYPT_INPLACE(benchmark::State& bs) { 77 | const size_t size = bs.range(0); 78 | std::string key = absl::HexStringToBytes("00112233445566778899aabbccddeeff"); 79 | std::string plaintext(size, '0'); 80 | auto iv = absl::HexStringToBytes("10000200000000000000000000000000"); 81 | Aegis128LState state; 82 | state.Reset(key, iv); 83 | for (auto s : bs) { 84 | state.Encrypt(plaintext.data(), size, plaintext.data()); 85 | } 86 | bs.SetBytesProcessed(static_cast(bs.iterations()) * size); 87 | } 88 | 89 | void BM_AEGIS128L_RESET_AND_FINALIZE(benchmark::State& bs) { 90 | std::string key = absl::HexStringToBytes("00112233445566778899aabbccddeeff"); 91 | auto iv = absl::HexStringToBytes("10000200000000000000000000000000"); 92 | char tag[16]; 93 | for (auto s : bs) { 94 | Aegis128LState state; 95 | state.Reset(key, iv); 96 | state.Finalize(tag); 97 | } 98 | bs.SetBytesProcessed(static_cast(bs.iterations())); 99 | } 100 | 101 | void BM_AEGIS128L_UNALIGNED(benchmark::State& bs) { 102 | // There is no real difference between AAD/Encrypt/Decrypt, so let's pick 103 | // Encrypt as representative case for cipher performance in the unaligned 104 | // data cases. 105 | BM_AEGIS128L_ENCRYPT(bs); 106 | } 107 | 108 | BENCHMARK(BM_AEGIS128L_ASSOCIATE)->Range(32, 1 << 27); 109 | BENCHMARK(BM_AEGIS128L_ENCRYPT)->Range(32, 1 << 27); 110 | BENCHMARK(BM_AEGIS128L_DECRYPT)->Range(32, 1 << 27); 111 | BENCHMARK(BM_AEGIS128L_ENCRYPT_INPLACE)->Range(32, 1 << 27); 112 | 113 | // Pick mostly primes here so that we always have many misalignments 114 | // with 32 bytes. 115 | BENCHMARK(BM_AEGIS128L_UNALIGNED)->Arg(1)->Arg(5)->Arg(23)->Arg(41)->Arg(57); 116 | 117 | BENCHMARK(BM_AEGIS128L_RESET_AND_FINALIZE)->Arg(1); 118 | } // namespace aegis 119 | 120 | } // namespace security 121 | -------------------------------------------------------------------------------- /aegis128L_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "aegis128L.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "gtest/gtest.h" 25 | #include "absl/base/dynamic_annotations.h" 26 | #include "absl/numeric/int128.h" 27 | #include "absl/strings/escaping.h" 28 | #include "absl/strings/string_view.h" 29 | #include "jsoncpp/json/reader.h" 30 | #include "vec128.h" 31 | 32 | namespace security { 33 | 34 | namespace aegis { 35 | 36 | using internal::AegisStage, internal::DIRTY, internal::RESET, internal::AAD, 37 | internal::ENCRYPT, internal::DECRYPT, internal::FINALIZED, 38 | internal::VERIFIED; 39 | 40 | // Short list of test vectors. 41 | // The WycheproofTest below uses a much longer list. 42 | struct TestVector { 43 | std::string key; 44 | std::string iv; 45 | std::string plaintext; // hex encoded plaintext 46 | std::string a_data; 47 | std::string raw_ciphertext; // hex encoded raw ciphertext (no IV, no tag) 48 | std::string tag; 49 | }; 50 | 51 | Aegis128LNonce CounterNonce() { 52 | static std::atomic ctr = absl::uint128(0); 53 | absl::uint128 next = ctr.load(); 54 | while (!ctr.compare_exchange_weak(next, next + 1, std::memory_order_relaxed, 55 | std::memory_order_relaxed)) { 56 | }; 57 | return MakeVec128Epi64x(Uint128Low64(next), Uint128High64(next)); 58 | } 59 | 60 | std::vector* test_vectors = new std::vector({{ 61 | // Test vector adjusted the case where the tag as sum over S[0] .. S[6] 62 | {"00000000000000000000000000000000", "00000000000000000000000000000000", 63 | "00000000000000000000000000000000", "", "41de9000a7b5e40e2d68bb64d99ebb19", 64 | "f4d997cc9b94227ada4fe4165422b1c8"}, 65 | {"00000000000000000000000000000000", "00000000000000000000000000000000", 66 | "00000000000000000000000000000000", "00000000000000000000000000000000", 67 | "29a0ce1f5dce8c404d56d00491668604", "29c9d93afd7e1276112a1fd0c344ccd2"}, 68 | {"00010000000000000000000000000000", "00000200000000000000000000000000", 69 | "00000000000000000000000000000000", "00010203", 70 | "1c0f229f289844def2c1ef28bea0abf0", "1f0799d68840d2364e7eeca6d41b4d05"}, 71 | {"10010000000000000000000000000000", "10000200000000000000000000000000", 72 | "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 73 | "0001020304050607", 74 | "79d94593d8c2119d7e8fd9b8fc77845c5c077a05b2528b6ac54b563aed8efe84", 75 | "cc6f3372f6aa1bb82388d695c3962d9a"}, 76 | // Test vectors from the CAESAR competition 77 | {"55565758595A5B5C5D5E5F6061626364", "B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF", "", 78 | "", "", "439ef345a332a4f83c9cc28debea9be0"}, 79 | {"55565758595A5B5C5D5E5F6061626364", "B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF", "", 80 | "A0", "", "a2694bd5658385c137048077018c0a69"}, 81 | // Test cases generated by the implementation itself 82 | {"10010000000000000000000000000000", "10000200000000000000000000000000", 83 | "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "", 84 | "c1c1e788dd160668e3a9054030ce5741d02556d1c6c35ce2dcd860c28c303c19", 85 | "807df2b3aacd5f944f589b6e3d897113"}, 86 | {"00010000000000000000000000000000", "00000200000000000000000000000000", "", 87 | "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "", 88 | "e5a4d7f0ffb051868dd94fb7438d9949"}, 89 | {"00010000000000000000000000000000", "00000200000000000000000000000000", 90 | "0101", "", "22f6", "a57f182297e44c2788ea1ae8c216e482"}, 91 | }}); 92 | 93 | class Aegis128LTest : public testing::Test { 94 | public: 95 | static std::string m128i2Hex(Vec128 value) { 96 | char bytes[16]; 97 | Vec128Store(bytes, value); 98 | return absl::BytesToHexString(absl::string_view(bytes, 16)); 99 | } 100 | 101 | static Vec128 Hex2m128i(absl::string_view hex) { 102 | std::string val = absl::HexStringToBytes(hex); 103 | assert(16 == val.size()); 104 | return Vec128Load(val.data()); 105 | } 106 | 107 | static void CleanseNonSensitiveStateFields(Aegis128LState* state) { 108 | // Additionally cleanse non-sensitive fields of Aegis128LState that its 109 | // destructor does not override. 110 | state->aad_size_ = state->payload_size_ = 0; 111 | #ifndef NDEBUG 112 | state->stage_ = internal::DIRTY; 113 | #endif 114 | } 115 | 116 | static bool EncryptHex(const std::string& key_hex, const std::string& iv_hex, 117 | const std::string& aad_hex, 118 | const std::string& plaintext_hex, 119 | std::string* raw_ciphertext_hex, 120 | std::string* tag_hex) { 121 | Aegis128LKey key = Hex2m128i(key_hex); 122 | Aegis128LNonce iv = Hex2m128i(iv_hex); 123 | std::string plaintext = absl::HexStringToBytes(plaintext_hex); 124 | std::string aad = absl::HexStringToBytes(aad_hex); 125 | std::unique_ptr buffer(new char[plaintext.size()]); 126 | Aegis128LState state; 127 | state.Reset(key, iv); 128 | state.AssociateData(aad); 129 | state.Encrypt(plaintext, buffer.get()); 130 | Aegis128LTag tag = state.Finalize(); 131 | *raw_ciphertext_hex = absl::BytesToHexString( 132 | absl::string_view(buffer.get(), plaintext.size())); 133 | *tag_hex = m128i2Hex(tag); 134 | return true; 135 | } 136 | 137 | static bool DecryptHex(const std::string& key_hex, const std::string& iv_hex, 138 | const std::string& aad_hex, 139 | const std::string& ciphertext_hex, 140 | const std::string& tag_hex, 141 | std::string* plaintext_hex) { 142 | Aegis128LPreKeyed cipher(absl::HexStringToBytes(key_hex), CounterNonce); 143 | std::string ct = absl::HexStringToBytes(iv_hex + ciphertext_hex + tag_hex); 144 | std::string pt; 145 | bool ok = cipher.Decrypt(ct, absl::HexStringToBytes(aad_hex), &pt); 146 | if (ok) { 147 | *plaintext_hex = absl::BytesToHexString(pt); 148 | } 149 | return ok; 150 | } 151 | 152 | static std::string KeyHex(const Aegis128LPreKeyed& cipher) { 153 | return m128i2Hex(cipher.key_); 154 | } 155 | static void ForceStage(Aegis128LState* state, AegisStage forced_stage) { 156 | #ifndef NDEBUG 157 | state->stage_ = forced_stage; 158 | #endif 159 | } 160 | 161 | static bool WycheproofTest(const Json::Value& root) { 162 | int correct_encryptions = 0; 163 | int correct_decryptions = 0; 164 | for (const Json::Value& test_group : root["testGroups"]) { 165 | for (const Json::Value& test : test_group["tests"]) { 166 | std::string comment = test["comment"].asString(); 167 | // All values below are hexadecimal. 168 | std::string key = test["key"].asString(); 169 | std::string iv = test["iv"].asString(); 170 | std::string msg = test["msg"].asString(); 171 | std::string ct = test["ct"].asString(); 172 | std::string aad = test["aad"].asString(); 173 | std::string tag = test["tag"].asString(); 174 | std::string id = test["tcId"].asString(); 175 | std::string expected = test["result"].asString(); 176 | std::string encrypted; 177 | std::string tag2; 178 | 179 | bool success = EncryptHex(key, iv, aad, msg, &encrypted, &tag2); 180 | if (success) { 181 | if (encrypted + tag2 == ct + tag) { 182 | if (expected == "invalid") { 183 | // There are essentially two ways to get here: 184 | // (1) using an old version of the algorithm, 185 | // (2) encrypting with invalid parameters such as wrong IV size. 186 | ADD_FAILURE() << "invalid encryption:" << id; 187 | } else { 188 | ++correct_encryptions; 189 | } 190 | } else { 191 | if (expected == "invalid") { 192 | // Getting here is completely acceptable. 193 | // Invalid test vectors are typically test vectors with an 194 | // incorrect ciphertext. Trying to reencrypt just gives the 195 | // correct ciphertext and does not detect the broken ciphertext. 196 | ++correct_encryptions; 197 | } else { 198 | ADD_FAILURE() << "Incorrect encryption:" << id 199 | << " encrypted:" << (encrypted + tag2) 200 | << " expected: " << (ct + tag); 201 | } 202 | } 203 | } else { 204 | if (expected == "valid") { 205 | ADD_FAILURE() << "could not encrypt test with tcId:" << id; 206 | } else { 207 | ++correct_encryptions; 208 | } 209 | } 210 | 211 | std::string decrypted; 212 | success = DecryptHex(key, iv, aad, ct, tag, &decrypted); 213 | if (success) { 214 | if (expected == "invalid") { 215 | ADD_FAILURE() << "decrypted invalid ciphertext:" << id; 216 | } else if (msg == decrypted) { 217 | ++correct_decryptions; 218 | } else { 219 | ADD_FAILURE() << "Incorrect decryption:" << id; 220 | } 221 | } else { 222 | if (expected == "valid") { 223 | ADD_FAILURE() << "Could not decrypt test with tcId:" << id; 224 | } else { 225 | ++correct_decryptions; 226 | } 227 | } 228 | } 229 | } 230 | int num_tests = root["numberOfTests"].asInt(); 231 | testing::Message summary; 232 | summary << root["algorithm"].asString() << "\n"; 233 | summary << root["generatorVersion"].asString() << "\n"; 234 | summary << "total number of tests: " << num_tests << "\n"; 235 | summary << "correct encryptions:" << correct_encryptions << "\n"; 236 | summary << "correct decryptions:" << correct_decryptions << "\n"; 237 | std::cout << summary; 238 | return (correct_encryptions == num_tests) && 239 | (correct_decryptions == num_tests); 240 | } 241 | 242 | static std::string FinalizedHex(Aegis128LState& state) { 243 | Aegis128LTag tag = state.Finalize(); 244 | return m128i2Hex(tag); 245 | } 246 | }; 247 | 248 | TEST_F(Aegis128LTest, EncryptDecrypt) { 249 | size_t kMaxSize = 200; 250 | Aegis128LPreKeyed cipher("0123456789abcdef", CounterNonce); 251 | std::string pt = "c"; 252 | for (int i = 0; i <= kMaxSize; i++) { 253 | std::string ct; 254 | std::string decrypted; 255 | cipher.Encrypt(pt, "", &ct); 256 | EXPECT_TRUE(cipher.Decrypt(ct, "", &decrypted)) 257 | << "i:" << i << "pt:" << absl::BytesToHexString(pt) 258 | << "ct:" << absl::BytesToHexString(ct); 259 | EXPECT_EQ(absl::BytesToHexString(pt), absl::BytesToHexString(decrypted)) 260 | << "i:" << i << "ct:" << absl::BytesToHexString(ct); 261 | pt += "x"; 262 | } 263 | } 264 | 265 | TEST_F(Aegis128LTest, EncryptOnce) { 266 | Aegis128LPreKeyed cipher("0123456789abcdef", CounterNonce); 267 | std::string ct; 268 | std::string tag; 269 | std::string hex = "0123456789abcdef0123456789abcdef"; 270 | EncryptHex(hex, hex, hex, hex, &ct, &tag); 271 | EXPECT_EQ(ct, "805d62213434b2532548ff9901c61883"); 272 | EXPECT_EQ(tag, "ea7cf897a74132ddb2df35c754d3388c"); 273 | } 274 | 275 | TEST_F(Aegis128LTest, EncryptDecryptWithAAD) { 276 | size_t kMaxSize = 200; 277 | Aegis128LPreKeyed cipher("0123456789abcdef", CounterNonce); 278 | std::string pt = ""; 279 | std::string aad = ""; 280 | for (int i = 0; i <= kMaxSize; i++) { 281 | std::string ct; 282 | std::string decrypted; 283 | cipher.Encrypt(pt, aad, &ct); 284 | EXPECT_TRUE(cipher.Decrypt(ct, aad, &decrypted)) 285 | << "i:" << i << "pt:" << absl::BytesToHexString(pt) 286 | << "ct:" << absl::BytesToHexString(ct); 287 | EXPECT_EQ(absl::BytesToHexString(pt), absl::BytesToHexString(decrypted)) 288 | << "i:" << i << "ct:" << absl::BytesToHexString(ct); 289 | pt += "x"; 290 | aad += "y"; 291 | } 292 | } 293 | 294 | TEST_F(Aegis128LTest, TestVectorsEncrypt) { 295 | for (const TestVector& v : *test_vectors) { 296 | std::string raw_ciphertext; 297 | std::string tag; 298 | bool ok = 299 | EncryptHex(v.key, v.iv, v.a_data, v.plaintext, &raw_ciphertext, &tag); 300 | ASSERT_TRUE(ok); 301 | EXPECT_EQ(v.raw_ciphertext, raw_ciphertext); 302 | EXPECT_EQ(v.tag, tag); 303 | } 304 | } 305 | 306 | TEST_F(Aegis128LTest, TestVectorsDecrypt) { 307 | for (const TestVector& v : *test_vectors) { 308 | Aegis128LPreKeyed cipher(absl::HexStringToBytes(v.key), CounterNonce); 309 | std::string ct = absl::HexStringToBytes(v.iv + v.raw_ciphertext + v.tag); 310 | std::string aad = absl::HexStringToBytes(v.a_data); 311 | std::string decrypted; 312 | EXPECT_TRUE(cipher.Decrypt(ct, aad, &decrypted)); 313 | EXPECT_EQ(v.plaintext, absl::BytesToHexString(decrypted)); 314 | } 315 | } 316 | 317 | static std::unique_ptr ReadJsonFile(const std::string& filename) { 318 | const std::string kTestVectors = "./"; 319 | std::ifstream input; 320 | input.open(kTestVectors + filename); 321 | std::unique_ptr root(new Json::Value); 322 | input >> (*root); 323 | return root; 324 | } 325 | 326 | TEST_F(Aegis128LTest, TestVectors) { 327 | std::unique_ptr root = ReadJsonFile("aegis128L_test.json"); 328 | ASSERT_TRUE(WycheproofTest(*root)); 329 | } 330 | 331 | TEST_F(Aegis128LTest, IncrementalUpdate) { 332 | std::string key_hex = "10010000000000000000000000000000"; 333 | std::string iv_hex = "10000200000000000000000000000000"; 334 | std::string key = absl::HexStringToBytes(key_hex); 335 | std::string iv = absl::HexStringToBytes(iv_hex); 336 | Aegis128LState state; 337 | state.Reset(key, iv); 338 | 339 | std::string aad_hex = ""; 340 | std::string aad_parts[] = {"00010203", "04050607"}; 341 | for (const std::string& p : aad_parts) { 342 | aad_hex.append(p); 343 | state.AssociateData(absl::HexStringToBytes(p)); 344 | } 345 | 346 | std::string plaintext_hex = ""; 347 | std::string plaintext_parts[] = {"79d94593d8c2119d7e8fd9b8fc77845c5c", 348 | "077a05b2528b6ac54b563a", "ed8efe84"}; 349 | std::string ciphertext_hex = ""; 350 | for (const std::string& p_hex : plaintext_parts) { 351 | plaintext_hex.append(p_hex); 352 | std::string p = absl::HexStringToBytes(p_hex); 353 | std::unique_ptr ciphertext_buffer(new char[p.size()]); 354 | state.Encrypt(p, ciphertext_buffer.get()); 355 | ciphertext_hex.append(absl::BytesToHexString( 356 | absl::string_view(ciphertext_buffer.get(), p.size()))); 357 | } 358 | 359 | std::string raw_ciphertext_hex; 360 | std::string tag_hex; 361 | EncryptHex(key_hex, iv_hex, aad_hex, plaintext_hex, &raw_ciphertext_hex, 362 | &tag_hex); 363 | ASSERT_EQ(FinalizedHex(state), tag_hex); 364 | ASSERT_EQ(raw_ciphertext_hex, ciphertext_hex); 365 | 366 | // Decrypt 367 | Aegis128LState dec_state; 368 | dec_state.Reset(key, iv); 369 | for (const std::string& p : aad_parts) { 370 | dec_state.AssociateData(absl::HexStringToBytes(p)); 371 | } 372 | std::string raw_ciphertext = absl::HexStringToBytes(raw_ciphertext_hex); 373 | std::unique_ptr plaintext_buffer(new char[raw_ciphertext.size()]); 374 | dec_state.Decrypt(raw_ciphertext, plaintext_buffer.get()); 375 | 376 | // We force the last operation to be "ENCRYPT" so that we can call 377 | // dec_state.Finalize. Usually callers should call .Verify, but we would like 378 | // to do a hex comparison, which is an absolutely silly idea outside of test 379 | // cases. 380 | ForceStage(&dec_state, ENCRYPT); 381 | 382 | ASSERT_EQ(FinalizedHex(dec_state), tag_hex); 383 | ASSERT_EQ(plaintext_hex, absl::BytesToHexString(absl::string_view( 384 | plaintext_buffer.get(), raw_ciphertext.size()))); 385 | } 386 | 387 | // Same as above, but try every segmentation of aad and msg into 3 parts. 388 | TEST_F(Aegis128LTest, IncrementalUpdate2) { 389 | std::string key_hex = "00112233445566778899aabbccddeeff"; 390 | std::string iv_hex = "000102030405060708090a0b0c0d0e0f"; 391 | std::string key = absl::HexStringToBytes(key_hex); 392 | std::string iv = absl::HexStringToBytes(iv_hex); 393 | std::string aad = absl::HexStringToBytes( 394 | "011112131415161718191a1b1c1d1e1f20212223242526272829871237319732" 395 | "8917398749812378123714198238213821381471237123712837128319371831" 396 | "9129398419823981249812731419827398124798173981239812398172381231" 397 | "0000000000000000000000000000000000000000000000000000000000000000" 398 | "198731982471982739813982147981249821381238217128371231982321412a"); 399 | std::string msg = absl::HexStringToBytes( 400 | "0001020817989879812798174981291786418764187623871637727374838382" 401 | "9817249812749812749184719827198471982731986378163789821718931121" 402 | "8172987219739857982173198231982739817398149814918479812888888888" 403 | "198274981723987149abcdef28319827498131aadf798gg31739817398173198" 404 | "9871237981247978980000000000478124877182841238174982139812123213" 405 | "0000000000000000000000000000000000000000000000000000000000000000" 406 | "9812398747123817248123123213123124879918288749812738213812372312"); 407 | Aegis128LState ref; 408 | ref.Reset(key, iv); 409 | ref.AssociateData(aad); 410 | std::unique_ptr expected(new char[msg.size()]); 411 | ref.Encrypt(msg, expected.get()); 412 | std::string expected_hex = 413 | absl::BytesToHexString(absl::string_view(expected.get(), msg.size())); 414 | std::string expected_tag = FinalizedHex(ref); 415 | 416 | // Incremental update of aad 417 | for (int s1 = 0; s1 <= aad.size(); s1++) { 418 | for (int s2 = s1; s2 <= aad.size(); s2++) { 419 | std::string aad1(aad, 0, s1); 420 | std::string aad2(aad, s1, s2 - s1); 421 | std::string aad3(aad, s2, aad.size() - s2); 422 | Aegis128LState state; 423 | state.Reset(key, iv); 424 | state.AssociateData(aad1); 425 | state.AssociateData(aad2); 426 | state.AssociateData(aad3); 427 | std::unique_ptr ct(new char[msg.size()]); 428 | memset(&ct[0], 0xff, msg.size()); 429 | state.Encrypt(msg, ct.get()); 430 | ASSERT_EQ(expected_tag, FinalizedHex(state)) 431 | << "s1:" << s1 << " s2:" << s2; 432 | } 433 | } 434 | // Incremental update of msg 435 | for (int s1 = 0; s1 <= msg.size(); s1++) { 436 | for (int s2 = s1; s2 <= msg.size(); s2++) { 437 | std::string m1(msg, 0, s1); 438 | std::string m2(msg, s1, s2 - s1); 439 | std::string m3(msg, s2, msg.size() - s2); 440 | Aegis128LState state; 441 | state.Reset(key, iv); 442 | state.AssociateData(aad); 443 | std::unique_ptr ct(new char[msg.size()]); 444 | memset(&ct[0], 0xff, msg.size()); 445 | state.Encrypt(m1, ct.get()); 446 | state.Encrypt(m2, ct.get() + s1); 447 | state.Encrypt(m3, ct.get() + s2); 448 | ASSERT_EQ(expected_tag, FinalizedHex(state)) 449 | << "s1:" << s1 << " s2:" << s2; 450 | std::string ct_hex = 451 | absl::BytesToHexString(absl::string_view(ct.get(), msg.size())); 452 | ASSERT_EQ(expected_hex, ct_hex) << "s1:" << s1 << " s2:" << s2; 453 | } 454 | } 455 | } 456 | 457 | TEST_F(Aegis128LTest, NonceAPIEquality) { 458 | std::string key_hex = "10010000000000000000000000000000"; 459 | std::string key = absl::HexStringToBytes(key_hex); 460 | 461 | Aegis128LState state1; 462 | state1.Reset( 463 | LoadKey(key), 464 | LoadNonce(absl::HexStringToBytes("000102030405060708090a0b0c0d0e0f"))); 465 | char tag1[16]; 466 | state1.Finalize(tag1); 467 | 468 | Aegis128LState state2; 469 | state2.Reset(LoadKey(key), 470 | LoadNonceLE(0x0f0e0d0c0b0a0908, 0x0706050403020100)); 471 | char tag2[16]; 472 | state2.Finalize(tag2); 473 | 474 | // Verify both nonce APIs return the same result. 475 | ASSERT_EQ(std::memcmp(tag1, tag2, 16), 0); 476 | } 477 | 478 | TEST_F(Aegis128LTest, ScrubbingState) { 479 | std::string key_hex = "10010000000000000000000000000000"; 480 | std::string key = absl::HexStringToBytes(key_hex); 481 | 482 | alignas(Aegis128LState) char backing[sizeof(Aegis128LState)]; 483 | memset(backing, 0, sizeof(Aegis128LState)); 484 | 485 | Aegis128LState* state = new (backing) Aegis128LState(); 486 | state->Reset( 487 | LoadKey(key), 488 | LoadNonce(absl::HexStringToBytes("000102030405060708090a0b0c0d0e0f"))); 489 | // Write zeros to non-sensitive fields. This allows us to compare the whole 490 | // backing space of state with zeros after destruction. 491 | CleanseNonSensitiveStateFields(state); 492 | state->~Aegis128LState(); 493 | ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(backing, sizeof(Aegis128LState)); 494 | for (int i = 0; i < sizeof(Aegis128LState); i++) { 495 | ASSERT_EQ(backing[i], 0); 496 | } 497 | } 498 | } // namespace aegis 499 | 500 | } // namespace security 501 | -------------------------------------------------------------------------------- /vec128.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #if defined(__SSE2__) && defined(__AES__) 16 | #include "vec128_x86_intrinsics.h" 17 | #elif defined(__ARM_NEON) && defined(__ARM_FEATURE_CRYPTO) 18 | #include "vec128_neon_intrinsics.h" 19 | #elif defined(__ALTIVEC__) 20 | #include "vec128_ppc_intrinsics.h" 21 | #else 22 | #error No supported Vec128 implementation found. 23 | #endif 24 | -------------------------------------------------------------------------------- /vec128_neon_intrinsics.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef AEGIS_CIPHER_VEC128_NEON_INTRINSIC_H_ 16 | #define AEGIS_CIPHER_VEC128_NEON_INTRINSIC_H_ 17 | #include "arm_neon.h" 18 | 19 | namespace security { 20 | 21 | namespace aegis { 22 | 23 | typedef uint8x16_t Vec128; 24 | 25 | inline Vec128 Vec128Load(const char *mem) { 26 | return vld1q_u8(reinterpret_cast(mem)); 27 | } 28 | 29 | inline void Vec128Store(char *mem, Vec128 x) { 30 | vst1q_u8(reinterpret_cast(mem), x); 31 | } 32 | 33 | inline Vec128 MakeVec128Epi64x(uint64_t n1, uint64_t n0) { 34 | uint64x2_t c = vcombine_u64(vcreate_u64(n0), vcreate_u64(n1)); 35 | return vreinterpretq_u8_u64(c); 36 | } 37 | 38 | inline Vec128 MakeVec128BroadcastEpi8(uint8_t x) { return vdupq_n_u8(x); } 39 | 40 | inline bool Vec128Eq(Vec128 a, Vec128 b) { 41 | uint64x2_t res = vceqq_u64(vreinterpretq_u64_u8(a), vreinterpretq_u64_u8(b)); 42 | return vminvq_u32(res) != 0; 43 | } 44 | 45 | inline Vec128 Vec128AesRound(Vec128 state, Vec128 round_key) { 46 | return veorq_u8(vaesmcq_u8(vaeseq_u8(state, vdupq_n_u8(0))), round_key); 47 | } 48 | 49 | inline Vec128 Vec128And(Vec128 x, Vec128 y) { return vandq_u8(x, y); } 50 | 51 | inline Vec128 Vec128AndNot(Vec128 x, Vec128 y) { return vbicq_u8(y, x); } 52 | 53 | inline Vec128 Vec128Xor(Vec128 x, Vec128 y) { return veorq_u8(x, y); } 54 | 55 | inline Vec128 Vec128CmpLtEpi8(Vec128 x, Vec128 y) { return vcltq_s8(x, y); } 56 | 57 | } // namespace aegis 58 | 59 | } // namespace security 60 | 61 | #endif // AEGIS_CIPHER_VEC128_NEON_INTRINSICS_H_ 62 | -------------------------------------------------------------------------------- /vec128_ppc_intrinsics.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef AEGIS_CIPHER_VEC128_PPC_INTRINSIC_H_ 16 | #define AEGIS_CIPHER_VEC128_PPC_INTRINSIC_H_ 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | namespace security { 23 | 24 | namespace aegis { 25 | 26 | // This implementation of Vec128 for PPC is keeping the vector content in 27 | // big-endian format, as the AES round transformation is implemented for 28 | // big-endian on PPC. As our AEGIS implementation assumes little-endian for 29 | // key/iv/plaintext/ciphertext, we convert little-endian<->big-endian on 30 | // load, stores and uint64 conversion. 31 | 32 | typedef vector unsigned char Vec128; 33 | 34 | inline Vec128 Vec128Load(const char *mem) { 35 | // Load from memory with an endian conversion. 36 | return vec_xl_be(0, reinterpret_cast(mem)); 37 | } 38 | 39 | inline void Vec128Store(char *mem, Vec128 x) { 40 | // Store to memory with an endian conversion. 41 | vec_xst_be(x, 0, reinterpret_cast(mem)); 42 | } 43 | 44 | inline Vec128 MakeVec128Epi64x(uint64_t end_le, uint64_t start_le) { 45 | // All uint64_t constants in aegis128L.cc are stated with the assumption that 46 | // when stored to memory result in a little endian ordering. So we need to 47 | // convert both uint64_t given. 48 | vector unsigned long long le = 49 | vec_insert(end_le, vec_promote((unsigned long long)start_le, 1), 0); 50 | // Reverse elements on the 16 byte vector to convert to big endian. 51 | return vec_revb(le); 52 | } 53 | 54 | inline Vec128 MakeVec128BroadcastEpi8(uint8_t x) { return vec_splat_u8(x); } 55 | 56 | inline bool Vec128Eq(Vec128 a, Vec128 b) { return vec_all_eq(a, b) != 0; } 57 | 58 | inline Vec128 Vec128AesRound(Vec128 state, Vec128 round_key) { 59 | return vec_cipher_be(state, round_key); 60 | } 61 | 62 | inline Vec128 Vec128And(Vec128 x, Vec128 y) { return vec_and(x, y); } 63 | 64 | inline Vec128 Vec128AndNot(Vec128 x, Vec128 y) { return vec_andc(y, x); } 65 | 66 | inline Vec128 Vec128Xor(Vec128 x, Vec128 y) { return vec_xor(x, y); } 67 | 68 | inline Vec128 Vec128CmpLtEpi8(Vec128 x, Vec128 y) { 69 | // This API implements a signed comparison, therefore cast from 'unsigned 70 | // char' to 'signed char'. We do implement this API as signed comparison 71 | // solely because Intel intrinsics don't have an unsigned comparison on 72 | // unsigned ints. 73 | return vec_cmplt((vector signed char)x, (vector signed char)y); 74 | } 75 | 76 | } // namespace aegis 77 | 78 | } // namespace security 79 | 80 | #endif // AEGIS_CIPHER_VEC128_PPC_INTRINSICS_H_ 81 | -------------------------------------------------------------------------------- /vec128_x86_intrinsics.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef AEGIS_CIPHER_VEC128_X86_INTRINSIC_H_ 16 | #define AEGIS_CIPHER_VEC128_X86_INTRINSIC_H_ 17 | #ifdef __SSE2__ 18 | #ifdef __AES__ 19 | 20 | #include // SSE2 21 | #include 22 | #include // AES_NI instructions. 23 | #include // Datatype __mm128i 24 | 25 | namespace security { 26 | 27 | namespace aegis { 28 | 29 | typedef __m128i Vec128; 30 | 31 | inline Vec128 Vec128Load(const char *mem) { 32 | return _mm_loadu_si128(reinterpret_cast(mem)); 33 | } 34 | 35 | inline void Vec128Store(char *mem, Vec128 x) { 36 | _mm_storeu_si128(reinterpret_cast(mem), x); 37 | } 38 | 39 | inline Vec128 MakeVec128Epi64x(uint64_t n1, uint64_t n0) { 40 | return _mm_set_epi64x(n1, n0); 41 | } 42 | 43 | inline Vec128 MakeVec128BroadcastEpi8(uint8_t x) { return _mm_set1_epi8(x); } 44 | 45 | inline bool Vec128Eq(Vec128 a, Vec128 b) { 46 | // Compare byte wise. 47 | // A byte in eq is 0xff if the corresponding byte in x and y are equal 48 | // and 0x00 if the corresponding byte in x and y are not equal. 49 | Vec128 eq = _mm_cmpeq_epi8(a, b); 50 | // Extract the 16 most significant bits of each byte in eq. 51 | int bits = _mm_movemask_epi8(eq); 52 | return 0xFFFF == bits; 53 | } 54 | 55 | inline Vec128 Vec128AesRound(Vec128 x, Vec128 y) { 56 | return _mm_aesenc_si128(x, y); 57 | } 58 | 59 | inline Vec128 Vec128And(Vec128 x, Vec128 y) { return _mm_and_si128(x, y); } 60 | 61 | inline Vec128 Vec128AndNot(Vec128 x, Vec128 y) { 62 | return _mm_andnot_si128(x, y); 63 | } 64 | 65 | inline Vec128 Vec128Xor(Vec128 x, Vec128 y) { return _mm_xor_si128(x, y); } 66 | 67 | inline Vec128 Vec128CmpLtEpi8(Vec128 x, Vec128 y) { 68 | return _mm_cmplt_epi8(x, y); 69 | } 70 | 71 | } // namespace aegis 72 | 73 | } // namespace security 74 | 75 | #else 76 | #error AESNI instruction set required. 77 | #endif // __AES__ 78 | #else 79 | #error SSE2 instruction set required. 80 | #endif // __SSE2__ 81 | 82 | #endif // AEGIS_CIPHER_VEC128_X86_INTRINSICS_H_ 83 | -------------------------------------------------------------------------------- /vec256_tuples.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef AEGIS_CIPHER_VEC256_TUPLES_H_ 16 | #define AEGIS_CIPHER_VEC256_TUPLES_H_ 17 | 18 | #include 19 | 20 | #include "vec128.h" 21 | 22 | namespace security { 23 | 24 | namespace aegis { 25 | 26 | typedef std::tuple Vec256; 27 | 28 | inline void Vec256Store(char *mem, Vec256 x) { 29 | Vec128 block1; 30 | Vec128 block2; 31 | std::tie(block1, block2) = x; 32 | Vec128Store(mem, block1); 33 | Vec128Store(mem + 16, block2); 34 | } 35 | 36 | inline Vec256 MakeVec256(Vec128 x, Vec128 y) { return std::make_tuple(x, y); } 37 | 38 | inline Vec256 Vec256Xor(Vec256 x, Vec256 y) { 39 | Vec128 x1, x2; 40 | Vec128 y1, y2; 41 | std::tie(x1, x2) = x; 42 | std::tie(y1, y2) = y; 43 | return MakeVec256(Vec128Xor(x1, y1), Vec128Xor(x2, y2)); 44 | } 45 | 46 | inline Vec256 Vec256Load(const char *mem) { 47 | return std::make_tuple(Vec128Load(mem), Vec128Load(mem + 16)); 48 | } 49 | 50 | } // namespace aegis 51 | 52 | } // namespace security 53 | 54 | #endif // AEGIS_CIPHER_VEC256_TUPLES_H_ 55 | --------------------------------------------------------------------------------