• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Abseil Authors.
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 // HERMETIC NOTE: The randen_hwaes target must not introduce duplicate
16 // symbols from arbitrary system and other headers, since it may be built
17 // with different flags from other targets, using different levels of
18 // optimization, potentially introducing ODR violations.
19 
20 #include "absl/random/internal/randen_hwaes.h"
21 
22 #include <cstdint>
23 #include <cstring>
24 
25 #include "absl/base/attributes.h"
26 #include "absl/numeric/int128.h"
27 #include "absl/random/internal/platform.h"
28 #include "absl/random/internal/randen_traits.h"
29 
30 // ABSL_RANDEN_HWAES_IMPL indicates whether this file will contain
31 // a hardware accelerated implementation of randen, or whether it
32 // will contain stubs that exit the process.
33 #if ABSL_HAVE_ACCELERATED_AES
34 // The following plaforms have implemented RandenHwAws.
35 #if defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32) || \
36     defined(ABSL_ARCH_PPC) || defined(ABSL_ARCH_ARM) ||       \
37     defined(ABSL_ARCH_AARCH64)
38 #define ABSL_RANDEN_HWAES_IMPL 1
39 #endif
40 #endif
41 
42 #if !defined(ABSL_RANDEN_HWAES_IMPL)
43 // No accelerated implementation is supported.
44 // The RandenHwAes functions are stubs that print an error and exit.
45 
46 #include <cstdio>
47 #include <cstdlib>
48 
49 namespace absl {
50 ABSL_NAMESPACE_BEGIN
51 namespace random_internal {
52 
53 // No accelerated implementation.
HasRandenHwAesImplementation()54 bool HasRandenHwAesImplementation() { return false; }
55 
56 // NOLINTNEXTLINE
GetKeys()57 const void* RandenHwAes::GetKeys() {
58   // Attempted to dispatch to an unsupported dispatch target.
59   const int d = ABSL_RANDOM_INTERNAL_AES_DISPATCH;
60   fprintf(stderr, "AES Hardware detection failed (%d).\n", d);
61   exit(1);
62   return nullptr;
63 }
64 
65 // NOLINTNEXTLINE
Absorb(const void *,void *)66 void RandenHwAes::Absorb(const void*, void*) {
67   // Attempted to dispatch to an unsupported dispatch target.
68   const int d = ABSL_RANDOM_INTERNAL_AES_DISPATCH;
69   fprintf(stderr, "AES Hardware detection failed (%d).\n", d);
70   exit(1);
71 }
72 
73 // NOLINTNEXTLINE
Generate(const void *,void *)74 void RandenHwAes::Generate(const void*, void*) {
75   // Attempted to dispatch to an unsupported dispatch target.
76   const int d = ABSL_RANDOM_INTERNAL_AES_DISPATCH;
77   fprintf(stderr, "AES Hardware detection failed (%d).\n", d);
78   exit(1);
79 }
80 
81 }  // namespace random_internal
82 ABSL_NAMESPACE_END
83 }  // namespace absl
84 
85 #else  // defined(ABSL_RANDEN_HWAES_IMPL)
86 //
87 // Accelerated implementations are supported.
88 // We need the per-architecture includes and defines.
89 //
90 namespace {
91 
92 using absl::random_internal::RandenTraits;
93 
94 }  // namespace
95 
96 // TARGET_CRYPTO defines a crypto attribute for each architecture.
97 //
98 // NOTE: Evaluate whether we should eliminate ABSL_TARGET_CRYPTO.
99 #if (defined(__clang__) || defined(__GNUC__))
100 #if defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32)
101 #define ABSL_TARGET_CRYPTO __attribute__((target("aes")))
102 #elif defined(ABSL_ARCH_PPC)
103 #define ABSL_TARGET_CRYPTO __attribute__((target("crypto")))
104 #else
105 #define ABSL_TARGET_CRYPTO
106 #endif
107 #else
108 #define ABSL_TARGET_CRYPTO
109 #endif
110 
111 #if defined(ABSL_ARCH_PPC)
112 // NOTE: Keep in mind that PPC can operate in little-endian or big-endian mode,
113 // however the PPC altivec vector registers (and thus the AES instructions)
114 // always operate in big-endian mode.
115 
116 #include <altivec.h>
117 // <altivec.h> #defines vector __vector; in C++, this is bad form.
118 #undef vector
119 #undef bool
120 
121 // Rely on the PowerPC AltiVec vector operations for accelerated AES
122 // instructions. GCC support of the PPC vector types is described in:
123 // https://gcc.gnu.org/onlinedocs/gcc-4.9.0/gcc/PowerPC-AltiVec_002fVSX-Built-in-Functions.html
124 //
125 // Already provides operator^=.
126 using Vector128 = __vector unsigned long long;  // NOLINT(runtime/int)
127 
128 namespace {
ReverseBytes(const Vector128 & v)129 inline ABSL_TARGET_CRYPTO Vector128 ReverseBytes(const Vector128& v) {
130   // Reverses the bytes of the vector.
131   const __vector unsigned char perm = {15, 14, 13, 12, 11, 10, 9, 8,
132                                        7,  6,  5,  4,  3,  2,  1, 0};
133   return vec_perm(v, v, perm);
134 }
135 
136 // WARNING: these load/store in native byte order. It is OK to load and then
137 // store an unchanged vector, but interpreting the bits as a number or input
138 // to AES will have undefined results.
Vector128Load(const void * from)139 inline ABSL_TARGET_CRYPTO Vector128 Vector128Load(const void* from) {
140   return vec_vsx_ld(0, reinterpret_cast<const Vector128*>(from));
141 }
142 
Vector128Store(const Vector128 & v,void * to)143 inline ABSL_TARGET_CRYPTO void Vector128Store(const Vector128& v, void* to) {
144   vec_vsx_st(v, 0, reinterpret_cast<Vector128*>(to));
145 }
146 
147 // One round of AES. "round_key" is a public constant for breaking the
148 // symmetry of AES (ensures previously equal columns differ afterwards).
AesRound(const Vector128 & state,const Vector128 & round_key)149 inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state,
150                                              const Vector128& round_key) {
151   return Vector128(__builtin_crypto_vcipher(state, round_key));
152 }
153 
154 // Enables native loads in the round loop by pre-swapping.
SwapEndian(absl::uint128 * state)155 inline ABSL_TARGET_CRYPTO void SwapEndian(absl::uint128* state) {
156   for (uint32_t block = 0; block < RandenTraits::kFeistelBlocks; ++block) {
157     Vector128Store(ReverseBytes(Vector128Load(state + block)), state + block);
158   }
159 }
160 
161 }  // namespace
162 
163 #elif defined(ABSL_ARCH_ARM) || defined(ABSL_ARCH_AARCH64)
164 
165 // Rely on the ARM NEON+Crypto advanced simd types, defined in <arm_neon.h>.
166 // uint8x16_t is the user alias for underlying __simd128_uint8_t type.
167 // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0073a/IHI0073A_arm_neon_intrinsics_ref.pdf
168 //
169 // <arm_neon> defines the following
170 //
171 // typedef __attribute__((neon_vector_type(16))) uint8_t uint8x16_t;
172 // typedef __attribute__((neon_vector_type(16))) int8_t int8x16_t;
173 // typedef __attribute__((neon_polyvector_type(16))) int8_t poly8x16_t;
174 //
175 // vld1q_v
176 // vst1q_v
177 // vaeseq_v
178 // vaesmcq_v
179 #include <arm_neon.h>
180 
181 // Already provides operator^=.
182 using Vector128 = uint8x16_t;
183 
184 namespace {
185 
Vector128Load(const void * from)186 inline ABSL_TARGET_CRYPTO Vector128 Vector128Load(const void* from) {
187   return vld1q_u8(reinterpret_cast<const uint8_t*>(from));
188 }
189 
Vector128Store(const Vector128 & v,void * to)190 inline ABSL_TARGET_CRYPTO void Vector128Store(const Vector128& v, void* to) {
191   vst1q_u8(reinterpret_cast<uint8_t*>(to), v);
192 }
193 
194 // One round of AES. "round_key" is a public constant for breaking the
195 // symmetry of AES (ensures previously equal columns differ afterwards).
AesRound(const Vector128 & state,const Vector128 & round_key)196 inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state,
197                                              const Vector128& round_key) {
198   // It is important to always use the full round function - omitting the
199   // final MixColumns reduces security [https://eprint.iacr.org/2010/041.pdf]
200   // and does not help because we never decrypt.
201   //
202   // Note that ARM divides AES instructions differently than x86 / PPC,
203   // And we need to skip the first AddRoundKey step and add an extra
204   // AddRoundKey step to the end. Lucky for us this is just XOR.
205   return vaesmcq_u8(vaeseq_u8(state, uint8x16_t{})) ^ round_key;
206 }
207 
SwapEndian(void *)208 inline ABSL_TARGET_CRYPTO void SwapEndian(void*) {}
209 
210 }  // namespace
211 
212 #elif defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32)
213 // On x86 we rely on the aesni instructions
214 #include <wmmintrin.h>
215 
216 namespace {
217 
218 // Vector128 class is only wrapper for __m128i, benchmark indicates that it's
219 // faster than using __m128i directly.
220 class Vector128 {
221  public:
222   // Convert from/to intrinsics.
Vector128(const __m128i & v)223   inline explicit Vector128(const __m128i& v) : data_(v) {}
224 
data() const225   inline __m128i data() const { return data_; }
226 
operator ^=(const Vector128 & other)227   inline Vector128& operator^=(const Vector128& other) {
228     data_ = _mm_xor_si128(data_, other.data());
229     return *this;
230   }
231 
232  private:
233   __m128i data_;
234 };
235 
Vector128Load(const void * from)236 inline ABSL_TARGET_CRYPTO Vector128 Vector128Load(const void* from) {
237   return Vector128(_mm_load_si128(reinterpret_cast<const __m128i*>(from)));
238 }
239 
Vector128Store(const Vector128 & v,void * to)240 inline ABSL_TARGET_CRYPTO void Vector128Store(const Vector128& v, void* to) {
241   _mm_store_si128(reinterpret_cast<__m128i*>(to), v.data());
242 }
243 
244 // One round of AES. "round_key" is a public constant for breaking the
245 // symmetry of AES (ensures previously equal columns differ afterwards).
AesRound(const Vector128 & state,const Vector128 & round_key)246 inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state,
247                                              const Vector128& round_key) {
248   // It is important to always use the full round function - omitting the
249   // final MixColumns reduces security [https://eprint.iacr.org/2010/041.pdf]
250   // and does not help because we never decrypt.
251   return Vector128(_mm_aesenc_si128(state.data(), round_key.data()));
252 }
253 
SwapEndian(void *)254 inline ABSL_TARGET_CRYPTO void SwapEndian(void*) {}
255 
256 }  // namespace
257 
258 #endif
259 
260 #ifdef __clang__
261 #pragma clang diagnostic push
262 #pragma clang diagnostic ignored "-Wunknown-pragmas"
263 #endif
264 
265 // At this point, all of the platform-specific features have been defined /
266 // implemented.
267 //
268 // REQUIRES: using Vector128 = ...
269 // REQUIRES: Vector128 Vector128Load(void*) {...}
270 // REQUIRES: void Vector128Store(Vector128, void*) {...}
271 // REQUIRES: Vector128 AesRound(Vector128, Vector128) {...}
272 // REQUIRES: void SwapEndian(uint64_t*) {...}
273 //
274 // PROVIDES: absl::random_internal::RandenHwAes::Absorb
275 // PROVIDES: absl::random_internal::RandenHwAes::Generate
276 namespace {
277 
278 // Block shuffles applies a shuffle to the entire state between AES rounds.
279 // Improved odd-even shuffle from "New criterion for diffusion property".
BlockShuffle(absl::uint128 * state)280 inline ABSL_TARGET_CRYPTO void BlockShuffle(absl::uint128* state) {
281   static_assert(RandenTraits::kFeistelBlocks == 16,
282                 "Expecting 16 FeistelBlocks.");
283 
284   constexpr size_t shuffle[RandenTraits::kFeistelBlocks] = {
285       7, 2, 13, 4, 11, 8, 3, 6, 15, 0, 9, 10, 1, 14, 5, 12};
286 
287   const Vector128 v0 = Vector128Load(state + shuffle[0]);
288   const Vector128 v1 = Vector128Load(state + shuffle[1]);
289   const Vector128 v2 = Vector128Load(state + shuffle[2]);
290   const Vector128 v3 = Vector128Load(state + shuffle[3]);
291   const Vector128 v4 = Vector128Load(state + shuffle[4]);
292   const Vector128 v5 = Vector128Load(state + shuffle[5]);
293   const Vector128 v6 = Vector128Load(state + shuffle[6]);
294   const Vector128 v7 = Vector128Load(state + shuffle[7]);
295   const Vector128 w0 = Vector128Load(state + shuffle[8]);
296   const Vector128 w1 = Vector128Load(state + shuffle[9]);
297   const Vector128 w2 = Vector128Load(state + shuffle[10]);
298   const Vector128 w3 = Vector128Load(state + shuffle[11]);
299   const Vector128 w4 = Vector128Load(state + shuffle[12]);
300   const Vector128 w5 = Vector128Load(state + shuffle[13]);
301   const Vector128 w6 = Vector128Load(state + shuffle[14]);
302   const Vector128 w7 = Vector128Load(state + shuffle[15]);
303 
304   Vector128Store(v0, state + 0);
305   Vector128Store(v1, state + 1);
306   Vector128Store(v2, state + 2);
307   Vector128Store(v3, state + 3);
308   Vector128Store(v4, state + 4);
309   Vector128Store(v5, state + 5);
310   Vector128Store(v6, state + 6);
311   Vector128Store(v7, state + 7);
312   Vector128Store(w0, state + 8);
313   Vector128Store(w1, state + 9);
314   Vector128Store(w2, state + 10);
315   Vector128Store(w3, state + 11);
316   Vector128Store(w4, state + 12);
317   Vector128Store(w5, state + 13);
318   Vector128Store(w6, state + 14);
319   Vector128Store(w7, state + 15);
320 }
321 
322 // Feistel round function using two AES subrounds. Very similar to F()
323 // from Simpira v2, but with independent subround keys. Uses 17 AES rounds
324 // per 16 bytes (vs. 10 for AES-CTR). Computing eight round functions in
325 // parallel hides the 7-cycle AESNI latency on HSW. Note that the Feistel
326 // XORs are 'free' (included in the second AES instruction).
FeistelRound(absl::uint128 * state,const absl::uint128 * ABSL_RANDOM_INTERNAL_RESTRICT keys)327 inline ABSL_TARGET_CRYPTO const absl::uint128* FeistelRound(
328     absl::uint128* state,
329     const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
330   static_assert(RandenTraits::kFeistelBlocks == 16,
331                 "Expecting 16 FeistelBlocks.");
332 
333   // MSVC does a horrible job at unrolling loops.
334   // So we unroll the loop by hand to improve the performance.
335   const Vector128 s0 = Vector128Load(state + 0);
336   const Vector128 s1 = Vector128Load(state + 1);
337   const Vector128 s2 = Vector128Load(state + 2);
338   const Vector128 s3 = Vector128Load(state + 3);
339   const Vector128 s4 = Vector128Load(state + 4);
340   const Vector128 s5 = Vector128Load(state + 5);
341   const Vector128 s6 = Vector128Load(state + 6);
342   const Vector128 s7 = Vector128Load(state + 7);
343   const Vector128 s8 = Vector128Load(state + 8);
344   const Vector128 s9 = Vector128Load(state + 9);
345   const Vector128 s10 = Vector128Load(state + 10);
346   const Vector128 s11 = Vector128Load(state + 11);
347   const Vector128 s12 = Vector128Load(state + 12);
348   const Vector128 s13 = Vector128Load(state + 13);
349   const Vector128 s14 = Vector128Load(state + 14);
350   const Vector128 s15 = Vector128Load(state + 15);
351 
352   // Encode even blocks with keys.
353   const Vector128 e0 = AesRound(s0, Vector128Load(keys + 0));
354   const Vector128 e2 = AesRound(s2, Vector128Load(keys + 1));
355   const Vector128 e4 = AesRound(s4, Vector128Load(keys + 2));
356   const Vector128 e6 = AesRound(s6, Vector128Load(keys + 3));
357   const Vector128 e8 = AesRound(s8, Vector128Load(keys + 4));
358   const Vector128 e10 = AesRound(s10, Vector128Load(keys + 5));
359   const Vector128 e12 = AesRound(s12, Vector128Load(keys + 6));
360   const Vector128 e14 = AesRound(s14, Vector128Load(keys + 7));
361 
362   // Encode odd blocks with even output from above.
363   const Vector128 o1 = AesRound(e0, s1);
364   const Vector128 o3 = AesRound(e2, s3);
365   const Vector128 o5 = AesRound(e4, s5);
366   const Vector128 o7 = AesRound(e6, s7);
367   const Vector128 o9 = AesRound(e8, s9);
368   const Vector128 o11 = AesRound(e10, s11);
369   const Vector128 o13 = AesRound(e12, s13);
370   const Vector128 o15 = AesRound(e14, s15);
371 
372   // Store odd blocks. (These will be shuffled later).
373   Vector128Store(o1, state + 1);
374   Vector128Store(o3, state + 3);
375   Vector128Store(o5, state + 5);
376   Vector128Store(o7, state + 7);
377   Vector128Store(o9, state + 9);
378   Vector128Store(o11, state + 11);
379   Vector128Store(o13, state + 13);
380   Vector128Store(o15, state + 15);
381 
382   return keys + 8;
383 }
384 
385 // Cryptographic permutation based via type-2 Generalized Feistel Network.
386 // Indistinguishable from ideal by chosen-ciphertext adversaries using less than
387 // 2^64 queries if the round function is a PRF. This is similar to the b=8 case
388 // of Simpira v2, but more efficient than its generic construction for b=16.
Permute(absl::uint128 * state,const absl::uint128 * ABSL_RANDOM_INTERNAL_RESTRICT keys)389 inline ABSL_TARGET_CRYPTO void Permute(
390     absl::uint128* state,
391     const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
392   // (Successfully unrolled; the first iteration jumps into the second half)
393 #ifdef __clang__
394 #pragma clang loop unroll_count(2)
395 #endif
396   for (size_t round = 0; round < RandenTraits::kFeistelRounds; ++round) {
397     keys = FeistelRound(state, keys);
398     BlockShuffle(state);
399   }
400 }
401 
402 }  // namespace
403 
404 namespace absl {
405 ABSL_NAMESPACE_BEGIN
406 namespace random_internal {
407 
HasRandenHwAesImplementation()408 bool HasRandenHwAesImplementation() { return true; }
409 
GetKeys()410 const void* ABSL_TARGET_CRYPTO RandenHwAes::GetKeys() {
411   // Round keys for one AES per Feistel round and branch.
412   // The canonical implementation uses first digits of Pi.
413 #if defined(ABSL_ARCH_PPC)
414   return kRandenRoundKeysBE;
415 #else
416   return kRandenRoundKeys;
417 #endif
418 }
419 
420 // NOLINTNEXTLINE
Absorb(const void * seed_void,void * state_void)421 void ABSL_TARGET_CRYPTO RandenHwAes::Absorb(const void* seed_void,
422                                             void* state_void) {
423   static_assert(RandenTraits::kCapacityBytes / sizeof(Vector128) == 1,
424                 "Unexpected Randen kCapacityBlocks");
425   static_assert(RandenTraits::kStateBytes / sizeof(Vector128) == 16,
426                 "Unexpected Randen kStateBlocks");
427 
428   auto* state = reinterpret_cast<absl::uint128 * ABSL_RANDOM_INTERNAL_RESTRICT>(
429       state_void);
430   const auto* seed =
431       reinterpret_cast<const absl::uint128 * ABSL_RANDOM_INTERNAL_RESTRICT>(
432           seed_void);
433 
434   Vector128 b1 = Vector128Load(state + 1);
435   b1 ^= Vector128Load(seed + 0);
436   Vector128Store(b1, state + 1);
437 
438   Vector128 b2 = Vector128Load(state + 2);
439   b2 ^= Vector128Load(seed + 1);
440   Vector128Store(b2, state + 2);
441 
442   Vector128 b3 = Vector128Load(state + 3);
443   b3 ^= Vector128Load(seed + 2);
444   Vector128Store(b3, state + 3);
445 
446   Vector128 b4 = Vector128Load(state + 4);
447   b4 ^= Vector128Load(seed + 3);
448   Vector128Store(b4, state + 4);
449 
450   Vector128 b5 = Vector128Load(state + 5);
451   b5 ^= Vector128Load(seed + 4);
452   Vector128Store(b5, state + 5);
453 
454   Vector128 b6 = Vector128Load(state + 6);
455   b6 ^= Vector128Load(seed + 5);
456   Vector128Store(b6, state + 6);
457 
458   Vector128 b7 = Vector128Load(state + 7);
459   b7 ^= Vector128Load(seed + 6);
460   Vector128Store(b7, state + 7);
461 
462   Vector128 b8 = Vector128Load(state + 8);
463   b8 ^= Vector128Load(seed + 7);
464   Vector128Store(b8, state + 8);
465 
466   Vector128 b9 = Vector128Load(state + 9);
467   b9 ^= Vector128Load(seed + 8);
468   Vector128Store(b9, state + 9);
469 
470   Vector128 b10 = Vector128Load(state + 10);
471   b10 ^= Vector128Load(seed + 9);
472   Vector128Store(b10, state + 10);
473 
474   Vector128 b11 = Vector128Load(state + 11);
475   b11 ^= Vector128Load(seed + 10);
476   Vector128Store(b11, state + 11);
477 
478   Vector128 b12 = Vector128Load(state + 12);
479   b12 ^= Vector128Load(seed + 11);
480   Vector128Store(b12, state + 12);
481 
482   Vector128 b13 = Vector128Load(state + 13);
483   b13 ^= Vector128Load(seed + 12);
484   Vector128Store(b13, state + 13);
485 
486   Vector128 b14 = Vector128Load(state + 14);
487   b14 ^= Vector128Load(seed + 13);
488   Vector128Store(b14, state + 14);
489 
490   Vector128 b15 = Vector128Load(state + 15);
491   b15 ^= Vector128Load(seed + 14);
492   Vector128Store(b15, state + 15);
493 }
494 
495 // NOLINTNEXTLINE
Generate(const void * keys_void,void * state_void)496 void ABSL_TARGET_CRYPTO RandenHwAes::Generate(const void* keys_void,
497                                               void* state_void) {
498   static_assert(RandenTraits::kCapacityBytes == sizeof(Vector128),
499                 "Capacity mismatch");
500 
501   auto* state = reinterpret_cast<absl::uint128*>(state_void);
502   const auto* keys = reinterpret_cast<const absl::uint128*>(keys_void);
503 
504   const Vector128 prev_inner = Vector128Load(state);
505 
506   SwapEndian(state);
507 
508   Permute(state, keys);
509 
510   SwapEndian(state);
511 
512   // Ensure backtracking resistance.
513   Vector128 inner = Vector128Load(state);
514   inner ^= prev_inner;
515   Vector128Store(inner, state);
516 }
517 
518 #ifdef __clang__
519 #pragma clang diagnostic pop
520 #endif
521 
522 }  // namespace random_internal
523 ABSL_NAMESPACE_END
524 }  // namespace absl
525 
526 #endif  // (ABSL_RANDEN_HWAES_IMPL)
527