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