├── .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 |
--------------------------------------------------------------------------------