1 /*
2  * ngtcp2
3  *
4  * Copyright (c) 2020 ngtcp2 contributors
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 #include "util.h"
26 
27 #include <cassert>
28 #include <iostream>
29 #include <fstream>
30 #include <array>
31 
32 #include <ngtcp2/ngtcp2_crypto.h>
33 
34 #include <gnutls/crypto.h>
35 
36 #include "template.h"
37 
38 // Based on https://github.com/ueno/ngtcp2-gnutls-examples
39 
40 namespace ngtcp2 {
41 
42 namespace util {
43 
generate_secure_random(uint8_t * data,size_t datalen)44 int generate_secure_random(uint8_t *data, size_t datalen) {
45   if (gnutls_rnd(GNUTLS_RND_RANDOM, data, datalen) != 0) {
46     return -1;
47   }
48 
49   return 0;
50 }
51 
generate_secret(uint8_t * secret,size_t secretlen)52 int generate_secret(uint8_t *secret, size_t secretlen) {
53   std::array<uint8_t, 16> rand;
54   std::array<uint8_t, 32> md;
55 
56   assert(md.size() == secretlen);
57 
58   if (generate_secure_random(rand.data(), rand.size()) != 0) {
59     return -1;
60   }
61 
62   if (gnutls_hash_fast(GNUTLS_DIG_SHA256, rand.data(), rand.size(),
63                        md.data()) != 0) {
64     return -1;
65   }
66 
67   std::copy_n(std::begin(md), secretlen, secret);
68   return 0;
69 }
70 
read_token(const std::string_view & filename)71 std::optional<std::string> read_token(const std::string_view &filename) {
72   auto f = std::ifstream(filename.data());
73   if (!f) {
74     std::cerr << "Could not read token file " << filename << std::endl;
75     return {};
76   }
77 
78   auto pos = f.tellg();
79   std::vector<char> content(pos);
80   f.seekg(0, std::ios::beg);
81   f.read(content.data(), pos);
82 
83   gnutls_datum_t s;
84   s.data = reinterpret_cast<unsigned char *>(content.data());
85   s.size = content.size();
86 
87   gnutls_datum_t d;
88   if (auto rv = gnutls_pem_base64_decode2("QUIC TOKEN", &s, &d); rv < 0) {
89     std::cerr << "Could not read token in " << filename << std::endl;
90     return {};
91   }
92 
93   auto res = std::string{d.data, d.data + d.size};
94 
95   gnutls_free(d.data);
96 
97   return res;
98 }
99 
write_token(const std::string_view & filename,const uint8_t * token,size_t tokenlen)100 int write_token(const std::string_view &filename, const uint8_t *token,
101                 size_t tokenlen) {
102   auto f = std::ofstream(filename.data());
103   if (!f) {
104     std::cerr << "Could not write token in " << filename << std::endl;
105     return -1;
106   }
107 
108   gnutls_datum_t s;
109   s.data = const_cast<uint8_t *>(token);
110   s.size = tokenlen;
111 
112   gnutls_datum_t d;
113   if (auto rv = gnutls_pem_base64_encode2("QUIC TOKEN", &s, &d); rv < 0) {
114     std::cerr << "Could not encode token in " << filename << std::endl;
115     return -1;
116   }
117 
118   f.write(reinterpret_cast<const char *>(d.data), d.size);
119   gnutls_free(d.data);
120 
121   return 0;
122 }
123 
crypto_aead_aes_128_gcm()124 ngtcp2_crypto_aead crypto_aead_aes_128_gcm() {
125   ngtcp2_crypto_aead aead;
126   ngtcp2_crypto_aead_init(&aead,
127                           reinterpret_cast<void *>(GNUTLS_CIPHER_AES_128_GCM));
128   return aead;
129 }
130 
crypto_md_sha256()131 ngtcp2_crypto_md crypto_md_sha256() {
132   ngtcp2_crypto_md md;
133   ngtcp2_crypto_md_init(&md, reinterpret_cast<void *>(GNUTLS_DIG_SHA256));
134   return md;
135 }
136 
crypto_default_ciphers()137 const char *crypto_default_ciphers() {
138   return "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:"
139          "+CHACHA20-POLY1305:+AES-128-CCM";
140 }
141 
crypto_default_groups()142 const char *crypto_default_groups() {
143   return "-GROUP-ALL:+GROUP-SECP256R1:+GROUP-X25519:+GROUP-SECP384R1:"
144          "+GROUP-SECP521R1";
145 }
146 
147 } // namespace util
148 
149 } // namespace ngtcp2
150