std::vector<RTSS_Share> RTSS_Share::split(byte M, byte N, const byte S[], u16bit S_len, const byte identifier[16], RandomNumberGenerator& rng) { if(M == 0 || N == 0 || M > N) throw Encoding_Error("RTSS_Share::split: M == 0 or N == 0 or M > N"); SHA_256 hash; // always use SHA-256 when generating shares std::vector<RTSS_Share> shares(N); // Create RTSS header in each share for(byte i = 0; i != N; ++i) { shares[i].m_contents += std::make_pair(identifier, 16); shares[i].m_contents += rtss_hash_id(hash.name()); shares[i].m_contents += M; shares[i].m_contents += get_byte(0, S_len); shares[i].m_contents += get_byte(1, S_len); } // Choose sequential values for X starting from 1 for(byte i = 0; i != N; ++i) shares[i].m_contents.push_back(i+1); // secret = S || H(S) secure_vector<byte> secret(S, S + S_len); secret += hash.process(S, S_len); for(size_t i = 0; i != secret.size(); ++i) { std::vector<byte> coefficients(M-1); rng.randomize(coefficients.data(), coefficients.size()); for(byte j = 0; j != N; ++j) { const byte X = j + 1; byte sum = secret[i]; byte X_i = X; for(size_t k = 0; k != coefficients.size(); ++k) { sum ^= gfp_mul(X_i, coefficients[k]); X_i = gfp_mul(X_i, X); } shares[j].m_contents.push_back(sum); } } return shares; }
secure_vector<byte> RTSS_Share::reconstruct(const std::vector<RTSS_Share>& shares) { const size_t RTSS_HEADER_SIZE = 20; for(size_t i = 0; i != shares.size(); ++i) { if(shares[i].size() != shares[0].size()) throw Decoding_Error("Different sized RTSS shares detected"); if(shares[i].share_id() == 0) throw Decoding_Error("Invalid (id = 0) RTSS share detected"); if(shares[i].size() < RTSS_HEADER_SIZE) throw Decoding_Error("Missing or malformed RTSS header"); if(!same_mem(&shares[0].m_contents[0], &shares[i].m_contents[0], RTSS_HEADER_SIZE)) throw Decoding_Error("Different RTSS headers detected"); } if(shares.size() < shares[0].m_contents[17]) throw Decoding_Error("Insufficient shares to do TSS reconstruction"); u16bit secret_len = make_u16bit(shares[0].m_contents[18], shares[0].m_contents[19]); byte hash_id = shares[0].m_contents[16]; std::unique_ptr<HashFunction> hash(get_rtss_hash_by_id(hash_id)); if(shares[0].size() != secret_len + hash->output_length() + RTSS_HEADER_SIZE + 1) throw Decoding_Error("Bad RTSS length field in header"); std::vector<byte> V(shares.size()); secure_vector<byte> secret; for(size_t i = RTSS_HEADER_SIZE + 1; i != shares[0].size(); ++i) { for(size_t j = 0; j != V.size(); ++j) V[j] = shares[j].m_contents[i]; byte r = 0; for(size_t k = 0; k != shares.size(); ++k) { // L_i function: byte r2 = 1; for(size_t l = 0; l != shares.size(); ++l) { if(k == l) continue; byte share_k = shares[k].share_id(); byte share_l = shares[l].share_id(); if(share_k == share_l) throw Decoding_Error("Duplicate shares found in RTSS recovery"); byte div = RTSS_EXP[(255 + RTSS_LOG[share_l] - RTSS_LOG[share_k ^ share_l]) % 255]; r2 = gfp_mul(r2, div); } r ^= gfp_mul(V[k], r2); } secret.push_back(r); } if(secret.size() != secret_len + hash->output_length()) throw Decoding_Error("Bad length in RTSS output"); hash->update(secret.data(), secret_len); secure_vector<byte> hash_check = hash->final(); if(!same_mem(hash_check.data(), &secret[secret_len], hash->output_length())) throw Decoding_Error("RTSS hash check failed"); return secure_vector<byte>(secret.cbegin(), secret.cbegin() + secret_len); }
secure_vector<uint8_t> RTSS_Share::reconstruct(const std::vector<RTSS_Share>& shares) { if(shares.size() <= 1) throw Decoding_Error("Insufficient shares to do TSS reconstruction"); for(size_t i = 0; i != shares.size(); ++i) { if(shares[i].size() < RTSS_HEADER_SIZE + 1) throw Decoding_Error("Missing or malformed RTSS header"); if(shares[i].share_id() == 0) throw Decoding_Error("Invalid (id = 0) RTSS share detected"); if(i > 0) { if(shares[i].size() != shares[0].size()) throw Decoding_Error("Different sized RTSS shares detected"); if(!same_mem(&shares[0].m_contents[0], &shares[i].m_contents[0], RTSS_HEADER_SIZE)) throw Decoding_Error("Different RTSS headers detected"); } } const uint8_t N = shares[0].m_contents[17]; if(shares.size() < N) throw Decoding_Error("Insufficient shares to do TSS reconstruction"); const uint16_t share_len = make_uint16(shares[0].m_contents[18], shares[0].m_contents[19]); const uint8_t hash_id = shares[0].m_contents[16]; std::unique_ptr<HashFunction> hash(get_rtss_hash_by_id(hash_id)); const size_t hash_len = (hash ? hash->output_length() : 0); if(shares[0].size() != RTSS_HEADER_SIZE + share_len) { /* * This second (laxer) check accomodates a bug in TSS that was * fixed in 2.9.0 - previous versions used the length of the * *secret* here, instead of the length of the *share*, which is * precisely 1 + hash_len longer. */ if(shares[0].size() <= RTSS_HEADER_SIZE + 1 + hash_len) throw Decoding_Error("Bad RTSS length field in header"); } std::vector<uint8_t> V(shares.size()); secure_vector<uint8_t> recovered; for(size_t i = RTSS_HEADER_SIZE + 1; i != shares[0].size(); ++i) { for(size_t j = 0; j != V.size(); ++j) V[j] = shares[j].m_contents[i]; uint8_t r = 0; for(size_t k = 0; k != shares.size(); ++k) { // L_i function: uint8_t r2 = 1; for(size_t l = 0; l != shares.size(); ++l) { if(k == l) continue; uint8_t share_k = shares[k].share_id(); uint8_t share_l = shares[l].share_id(); if(share_k == share_l) throw Decoding_Error("Duplicate shares found in RTSS recovery"); uint8_t div = RTSS_EXP[(255 + RTSS_LOG[share_l] - RTSS_LOG[share_k ^ share_l]) % 255]; r2 = gfp_mul(r2, div); } r ^= gfp_mul(V[k], r2); } recovered.push_back(r); } if(hash) { if(recovered.size() < hash->output_length()) throw Decoding_Error("RTSS recovered value too short to be valid"); const size_t secret_len = recovered.size() - hash->output_length(); hash->update(recovered.data(), secret_len); secure_vector<uint8_t> hash_check = hash->final(); if(!constant_time_compare(hash_check.data(), &recovered[secret_len], hash->output_length())) { throw Decoding_Error("RTSS hash check failed"); } // remove the trailing hash value recovered.resize(secret_len); } return recovered; }
std::vector<RTSS_Share> RTSS_Share::split(uint8_t M, uint8_t N, const uint8_t S[], uint16_t S_len, const std::vector<uint8_t>& identifier, const std::string& hash_fn, RandomNumberGenerator& rng) { if(M <= 1 || N <= 1 || M > N || N >= 255) throw Invalid_Argument("RTSS_Share::split: Invalid N or M"); if(identifier.size() > 16) throw Invalid_Argument("RTSS_Share::split Invalid identifier size"); const uint8_t hash_id = rtss_hash_id(hash_fn); std::unique_ptr<HashFunction> hash; if(hash_id > 0) hash = HashFunction::create_or_throw(hash_fn); // secret = S || H(S) secure_vector<uint8_t> secret(S, S + S_len); if(hash) secret += hash->process(S, S_len); if(secret.size() >= 0xFFFE) throw Encoding_Error("RTSS_Share::split secret too large for TSS format"); // +1 byte for the share ID const uint16_t share_len = static_cast<uint16_t>(secret.size() + 1); secure_vector<uint8_t> share_header(RTSS_HEADER_SIZE); copy_mem(&share_header[0], identifier.data(), identifier.size()); share_header[16] = hash_id; share_header[17] = M; share_header[18] = get_byte(0, share_len); share_header[19] = get_byte(1, share_len); // Create RTSS header in each share std::vector<RTSS_Share> shares(N); for(uint8_t i = 0; i != N; ++i) { shares[i].m_contents.reserve(share_header.size() + share_len); shares[i].m_contents = share_header; } // Choose sequential values for X starting from 1 for(uint8_t i = 0; i != N; ++i) shares[i].m_contents.push_back(i+1); for(size_t i = 0; i != secret.size(); ++i) { std::vector<uint8_t> coefficients(M-1); rng.randomize(coefficients.data(), coefficients.size()); for(uint8_t j = 0; j != N; ++j) { const uint8_t X = j + 1; uint8_t sum = secret[i]; uint8_t X_i = X; for(size_t k = 0; k != coefficients.size(); ++k) { sum ^= gfp_mul(X_i, coefficients[k]); X_i = gfp_mul(X_i, X); } shares[j].m_contents.push_back(sum); } } return shares; }