1 // Copyright 2020 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 //      http://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 "prng/chacha_prng_util.h"
16 
17 #include <cstdint>
18 #include <vector>
19 
20 #include "absl/memory/memory.h"
21 #include "absl/strings/str_cat.h"
22 #include <openssl/chacha.h>
23 #include <openssl/crypto.h>
24 #include <openssl/rand.h>
25 #include "status_macros.h"
26 
27 namespace rlwe {
28 namespace internal {
29 
ChaChaPrngResalt(absl::string_view key,int buffer_size,int * salt_counter,int * position_in_buffer,std::vector<Uint8> * buffer)30 absl::Status ChaChaPrngResalt(absl::string_view key, int buffer_size,
31                               int* salt_counter, int* position_in_buffer,
32                               std::vector<Uint8>* buffer) {
33   buffer->assign(buffer_size, 0);
34 
35   // Following https://tools.ietf.org/html/rfc7539, Sec 2.3, we create the
36   // nonce as a kChaChaNonceSize (=12) bytes string, where the 4 first
37   // bytes are fixed, and the next 8 bytes correspond to the counter.
38   std::string nonce = "salt00000000";
39   if (nonce.size() != kChaChaNonceSize) {
40     return absl::InternalError("The salt length is incorrect.");
41   }
42   Uint64 counter = static_cast<Uint64>(*salt_counter);
43   for (int i = 0; i < 8; i++) {
44     nonce[4 + i] = counter & 0xFF;
45     counter >>= 8;
46   }
47 
48   // We call the CRYPTO_chacha_20() function from OpenSSL. Note that the last
49   // parameter is a *block* counter. The salt counter needs instead to be
50   // included in the nonce.
51   CRYPTO_chacha_20(buffer->data(), buffer->data(), buffer->size(),
52                    reinterpret_cast<const Uint8*>(key.data()),
53                    reinterpret_cast<const Uint8*>(nonce.data()),
54                    /* counter = */ 0);
55 
56   ++(*salt_counter);
57   *position_in_buffer = 0;
58   return absl::OkStatus();
59 }
60 
ChaChaPrngGenerateKey()61 rlwe::StatusOr<std::string> ChaChaPrngGenerateKey() {
62   std::unique_ptr<Uint8[]> buf(new Uint8[kChaChaKeyBytesSize]);
63   // BoringSSL documentation says that it always returns 1; while
64   // OpenSSL documentation says that it returns 1 on success, 0 otherwise. Check
65   // for an error just in case.
66   if (RAND_bytes(buf.get(), kChaChaKeyBytesSize) == 0) {
67     return absl::InternalError("Internal error generating random PRNG key.");
68   }
69   return std::string(reinterpret_cast<const char*>(buf.get()),
70                      kChaChaKeyBytesSize);
71 }
72 
ChaChaPrngRand8(absl::string_view key,int * position_in_buffer,int * salt_counter,std::vector<Uint8> * buffer)73 rlwe::StatusOr<Uint8> ChaChaPrngRand8(absl::string_view key,
74                                       int* position_in_buffer,
75                                       int* salt_counter,
76                                       std::vector<Uint8>* buffer) {
77   Uint8 rand;
78   if (*position_in_buffer >= buffer->size()) {
79     RLWE_RETURN_IF_ERROR(ChaChaPrngResalt(key, kChaChaOutputBytes, salt_counter,
80                                           position_in_buffer, buffer));
81   }
82   rand = buffer->at(*position_in_buffer);
83   ++(*position_in_buffer);
84   return rand;
85 }
86 
ChaChaPrngRand64(absl::string_view key,int * position_in_buffer,int * salt_counter,std::vector<Uint8> * buffer)87 rlwe::StatusOr<Uint64> ChaChaPrngRand64(absl::string_view key,
88                                         int* position_in_buffer,
89                                         int* salt_counter,
90                                         std::vector<Uint8>* buffer) {
91   Uint64 rand64 = 0;
92   for (int i = 0; i < 8; ++i) {
93     RLWE_ASSIGN_OR_RETURN(Uint8 rand8, ChaChaPrngRand8(key, position_in_buffer,
94                                                        salt_counter, buffer));
95     rand64 += Uint64{rand8} << (8 * i);
96   }
97   return rand64;
98 }
99 
100 }  // namespace internal
101 }  // namespace rlwe
102