Coverage Report

Created: 2020-02-14 15:38

/src/botan/src/lib/entropy/rdseed/rdseed.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
* Entropy Source Using Intel's rdseed instruction
3
* (C) 2015 Daniel Neus
4
* (C) 2015,2019 Jack Lloyd
5
*
6
* Botan is released under the Simplified BSD License (see license.txt)
7
*/
8
9
#include <botan/internal/rdseed.h>
10
#include <botan/cpuid.h>
11
12
#include <immintrin.h>
13
14
namespace Botan {
15
16
namespace {
17
18
BOTAN_FUNC_ISA("rdseed")
19
bool read_rdseed(secure_vector<uint32_t>& seed)
20
0
   {
21
0
   /*
22
0
   * RDSEED is not guaranteed to generate an output within any specific number
23
0
   * of attempts. However in testing on a Skylake system, with all hyperthreads
24
0
   * occupied in tight RDSEED loops, RDSEED will still usually succeed in under
25
0
   * 150 attempts. The maximum ever seen was 230 attempts until success. When
26
0
   * idle, RDSEED usually succeeds in 1 or 2 attempts.
27
0
   *
28
0
   * We set an upper bound of 512 attempts, because it is possible that due
29
0
   * to firmware issue RDSEED is simply broken and never succeeds. We do not
30
0
   * want to loop forever in that case. If we exceed that limit, then we assume
31
0
   * the hardware is actually just broken, and stop the poll.
32
0
   */
33
0
   const size_t RDSEED_RETRIES = 512;
34
0
35
0
   for(size_t i = 0; i != RDSEED_RETRIES; ++i)
36
0
      {
37
0
      uint32_t r = 0;
38
0
      int cf = 0;
39
0
40
0
#if defined(BOTAN_USE_GCC_INLINE_ASM)
41
0
      asm("rdseed %0; adcl $0,%1" :
42
0
          "=r" (r), "=r" (cf) : "0" (r), "1" (cf) : "cc");
43
#else
44
      cf = _rdseed32_step(&r);
45
#endif
46
47
0
      if(1 == cf)
48
0
         {
49
0
         seed.push_back(r);
50
0
         return true;
51
0
         }
52
0
53
0
      // Intel suggests pausing if RDSEED fails.
54
0
      _mm_pause();
55
0
      }
56
0
57
0
   return false; // failed to produce an output after many attempts
58
0
   }
59
60
}
61
62
size_t Intel_Rdseed::poll(RandomNumberGenerator& rng)
63
0
   {
64
0
   const size_t RDSEED_BYTES = 1024;
65
0
   static_assert(RDSEED_BYTES % 4 == 0, "Bad RDSEED configuration");
66
0
67
0
   if(CPUID::has_rdseed())
68
0
      {
69
0
      secure_vector<uint32_t> seed;
70
0
      seed.reserve(RDSEED_BYTES / 4);
71
0
72
0
      for(size_t p = 0; p != RDSEED_BYTES / 4; ++p)
73
0
         {
74
0
         /*
75
0
         If at any point we exceed our retry count, we stop the entire seed
76
0
         gathering process. This situation will only occur in situations of
77
0
         extremely high RDSEED utilization. If RDSEED is currently so highly
78
0
         contended, then the rest of the poll is likely to also face contention and
79
0
         it is better to quit now rather than (presumably) face very high retry
80
0
         times for the rest of the poll.
81
0
         */
82
0
         if(!read_rdseed(seed))
83
0
            break;
84
0
         }
85
0
86
0
      if(seed.size() > 0)
87
0
         {
88
0
         rng.add_entropy(reinterpret_cast<const uint8_t*>(seed.data()),
89
0
                         seed.size() * sizeof(uint32_t));
90
0
         }
91
0
      }
92
0
93
0
   // RDSEED is used but not trusted
94
0
   return 0;
95
0
   }
96
97
}