10#include <botan/exceptn.h>
11#include <botan/hash.h>
14#include <botan/internal/ct_utils.h>
15#include <botan/internal/loadstor.h>
21const size_t RTSS_HEADER_SIZE = 20;
26uint8_t tss_gf_mul(uint8_t x, uint8_t y) {
28 for(
size_t i = 0; i != 8; ++i) {
30 x = (x << 1) ^ CT::Mask<uint8_t>::expand_top_bit(x).if_set_return(0x1B);
44uint8_t tss_gf_inv(uint8_t x) {
45 const uint8_t x2 = tss_gf_mul(x, x);
46 const uint8_t x3 = tss_gf_mul(x2, x);
47 const uint8_t x6 = tss_gf_mul(x3, x3);
48 const uint8_t x12 = tss_gf_mul(x6, x6);
49 const uint8_t x15 = tss_gf_mul(x12, x3);
50 const uint8_t x30 = tss_gf_mul(x15, x15);
51 const uint8_t x60 = tss_gf_mul(x30, x30);
52 const uint8_t x120 = tss_gf_mul(x60, x60);
53 const uint8_t x126 = tss_gf_mul(x120, x6);
54 const uint8_t x127 = tss_gf_mul(x126, x);
55 return tss_gf_mul(x127, x127);
58uint8_t rtss_hash_id(std::string_view hash_name) {
59 if(hash_name ==
"None") {
61 }
else if(hash_name ==
"SHA-1") {
63 }
else if(hash_name ==
"SHA-256") {
70std::unique_ptr<HashFunction> get_rtss_hash_by_id(uint8_t
id) {
72 return std::unique_ptr<HashFunction>();
90 m_contents.assign(bin, bin + len);
98 if(m_contents.size() < RTSS_HEADER_SIZE + 1) {
102 return m_contents[20];
106 return hex_encode(m_contents.data(), m_contents.size());
110 uint8_t M, uint8_t N,
const uint8_t S[], uint16_t S_len,
const uint8_t identifier[16],
RandomNumberGenerator& rng) {
111 return RTSS_Share::split(M, N, S, S_len, std::vector<uint8_t>(identifier, identifier + 16),
"SHA-256", rng);
118 const std::vector<uint8_t>& identifier,
119 std::string_view hash_fn,
121 if(M <= 1 || N <= 1 || M > N || N >= 255) {
125 if(identifier.size() > 16) {
129 const uint8_t hash_id = rtss_hash_id(hash_fn);
131 std::unique_ptr<HashFunction> hash;
139 secret += hash->process(S, S_len);
142 if(secret.size() >= 0xFFFE) {
143 throw Encoding_Error(
"RTSS_Share::split secret too large for TSS format");
147 const uint16_t share_len =
static_cast<uint16_t
>(secret.size() + 1);
150 copy_mem(share_header.data(), identifier.data(), identifier.size());
151 share_header[16] = hash_id;
152 share_header[17] = M;
157 std::vector<RTSS_Share> shares(N);
159 for(uint8_t i = 0; i != N; ++i) {
160 shares[i].m_contents.reserve(share_header.size() + share_len);
161 shares[i].m_contents = share_header;
165 for(uint8_t i = 0; i != N; ++i) {
166 shares[i].m_contents.push_back(i + 1);
169 for(
const uint8_t secret_byte : secret) {
170 std::vector<uint8_t> coefficients(M - 1);
171 rng.
randomize(coefficients.data(), coefficients.size());
173 for(uint8_t j = 0; j != N; ++j) {
174 const uint8_t X = j + 1;
176 uint8_t sum = secret_byte;
179 for(
const uint8_t cb : coefficients) {
180 sum ^= tss_gf_mul(X_i, cb);
181 X_i = tss_gf_mul(X_i, X);
184 shares[j].m_contents.push_back(sum);
192 if(shares.size() <= 1) {
193 throw Decoding_Error(
"Insufficient shares to do TSS reconstruction");
196 for(
size_t i = 0; i != shares.size(); ++i) {
197 if(shares[i].
size() < RTSS_HEADER_SIZE + 1) {
206 if(shares[i].
size() != shares[0].
size()) {
210 if(!
CT::is_equal(shares[0].m_contents.data(), shares[i].m_contents.data(), RTSS_HEADER_SIZE).as_bool()) {
216 const uint8_t N = shares[0].m_contents[17];
218 if(shares.size() < N) {
219 throw Decoding_Error(
"Insufficient shares to do TSS reconstruction");
222 const uint16_t share_len =
make_uint16(shares[0].m_contents[18], shares[0].m_contents[19]);
224 const uint8_t hash_id = shares[0].m_contents[16];
225 auto hash = get_rtss_hash_by_id(hash_id);
226 const size_t hash_len = (hash ? hash->output_length() : 0);
228 if(shares[0].
size() != RTSS_HEADER_SIZE + share_len) {
235 if(shares[0].
size() <= RTSS_HEADER_SIZE + 1 + hash_len) {
240 std::vector<uint8_t> V(shares.size());
244 std::vector<uint8_t> lagrange_coeffs(shares.size());
245 for(
size_t k = 0; k != shares.size(); ++k) {
247 for(
size_t l = 0; l != shares.size(); ++l) {
251 const uint8_t share_k = shares[k].share_id();
252 const uint8_t share_l = shares[l].share_id();
253 if(share_k == share_l) {
258 const uint8_t div = tss_gf_mul(share_l, tss_gf_inv(share_k ^ share_l));
259 coeff = tss_gf_mul(coeff, div);
261 lagrange_coeffs[k] = coeff;
264 for(
size_t i = RTSS_HEADER_SIZE + 1; i != shares[0].size(); ++i) {
265 for(
size_t j = 0; j != V.size(); ++j) {
266 V[j] = shares[j].m_contents[i];
278 for(
size_t k = 0; k != shares.size(); ++k) {
279 r ^= tss_gf_mul(V[k], lagrange_coeffs[k]);
281 recovered.push_back(r);
285 if(recovered.size() < hash->output_length()) {
286 throw Decoding_Error(
"RTSS recovered value too short to be valid");
289 const size_t secret_len = recovered.size() - hash->output_length();
291 hash->update(recovered.data(), secret_len);
294 if(!
CT::is_equal(hash_check.data(), &recovered[secret_len], hash->output_length()).as_bool()) {
299 recovered.resize(secret_len);
#define BOTAN_ASSERT_NOMSG(expr)
static constexpr Mask< T > expand(T v)
static std::unique_ptr< HashFunction > create_or_throw(std::string_view algo_spec, std::string_view provider="")
static std::vector< RTSS_Share > split(uint8_t M, uint8_t N, const uint8_t secret[], uint16_t secret_len, const uint8_t identifier[16], RandomNumberGenerator &rng)
std::string to_string() const
static secure_vector< uint8_t > reconstruct(const std::vector< RTSS_Share > &shares)
void randomize(std::span< uint8_t > output)
constexpr CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
constexpr uint8_t get_byte(T input)
constexpr void copy_mem(T *out, const T *in, size_t n)
secure_vector< uint8_t > hex_decode_locked(const char input[], size_t input_length, bool ignore_ws)
void hex_encode(char output[], const uint8_t input[], size_t input_length, bool uppercase)
std::vector< T, secure_allocator< T > > secure_vector
constexpr uint16_t make_uint16(uint8_t i0, uint8_t i1)