1 /** 2 * RTSS (threshold secret sharing) 3 * 4 * Copyright: 5 * (C) 2009 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.constructs.tss; 12 13 import botan.constants; 14 static if (BOTAN_HAS_THRESHOLD_SECRET_SHARING && BOTAN_HAS_SHA1 && BOTAN_HAS_SHA2_32): 15 16 import memutils.vector; 17 import botan.hash.hash; 18 import botan.rng.rng; 19 import botan.utils.loadstor; 20 import botan.filters.pipe; 21 import botan.codec.hex; 22 import botan.hash.sha2_32; 23 import botan.hash.sha160; 24 import botan.utils.types; 25 import botan.utils.exceptn; 26 import botan.utils.get_byte; 27 28 /** 29 * A split secret, using the format from draft-mcgrew-tss-03 30 */ 31 struct RTSS 32 { 33 public: 34 /** 35 * Params: 36 * M = the number of shares needed to reconstruct 37 * N = the number of shares generated 38 * S = the secret to split 39 * S_len = the length of the secret 40 * identifier = the 16 ubyte share identifier 41 * rng = the random number generator to use 42 */ 43 static auto split(ubyte M, ubyte N, 44 const(ubyte)* S, size_t S_len, 45 in ubyte[16] identifier, 46 RandomNumberGenerator rng) 47 { 48 if (M == 0 || N == 0 || M > N) 49 throw new EncodingError("split: M == 0 or N == 0 or M > N"); 50 51 Unique!SHA256 hash = new SHA256(); // always use SHA-256 when generating shares 52 Vector!RTSS shares = Vector!RTSS(N); 53 // Create RTSS header in each share 54 foreach (ubyte i; 0 .. N) 55 { 56 shares[i].m_contents = SecureArray!ubyte(); 57 shares[i].m_contents ~= identifier.ptr[0 .. 16]; 58 shares[i].m_contents ~= rtssHashId(hash.name); 59 shares[i].m_contents ~= M; 60 shares[i].m_contents ~= get_byte(6, S_len); 61 shares[i].m_contents ~= get_byte(7, S_len); 62 } 63 64 // Choose sequential values for X starting from 1 65 foreach (ubyte i; 0 .. N) 66 shares[i].m_contents.pushBack(i+1); 67 68 logTrace("pushed back"); 69 // secret = S || H(S) 70 SecureVector!ubyte secret = SecureVector!ubyte(S[0 .. S_len]); 71 logTrace("secret"); 72 secret ~= hash.process(S, S_len); 73 74 logTrace("secret ", secret[]); 75 foreach (ubyte s; secret[]) 76 { 77 Vector!ubyte coefficients = Vector!ubyte(M-1); 78 rng.randomize(coefficients.ptr, coefficients.length); 79 80 foreach (ubyte j; 0 .. N) 81 { 82 const ubyte X = cast(const ubyte) (j + 1); 83 84 ubyte sum = s; 85 ubyte X_i = X; 86 87 foreach (coefficient; coefficients[]) 88 { 89 sum ^= gfp_mul(X_i, coefficient); 90 X_i = gfp_mul(X_i, X); 91 } 92 93 shares[j].m_contents.pushBack(sum); 94 } 95 } 96 97 return shares.move(); 98 } 99 100 101 /** 102 * Params: 103 * shares = the list of shares 104 */ 105 106 static SecureVector!ubyte reconstruct()(auto const ref Vector!RTSS shares) 107 { 108 __gshared immutable size_t RTSS_HEADER_SIZE = 20; 109 110 foreach (ref share; shares[]) 111 { 112 if (share.length != shares[0].length) 113 throw new DecodingError("Different sized RTSS shares detected"); 114 if (share.shareId() == 0) 115 throw new DecodingError("Invalid (id = 0) RTSS share detected"); 116 if (share.length < RTSS_HEADER_SIZE) 117 throw new DecodingError("Missing or malformed RTSS header"); 118 119 if (!sameMem(&shares[0].m_contents[0], &share.m_contents[0], RTSS_HEADER_SIZE)) 120 throw new DecodingError("Different RTSS headers detected"); 121 } 122 123 if (shares.length < shares[0].m_contents[17]) 124 throw new DecodingError("Insufficient shares to do TSS reconstruction"); 125 126 ushort secret_len = make_ushort(shares[0].m_contents[18], shares[0].m_contents[19]); 127 128 ubyte hash_id = shares[0].m_contents[16]; 129 130 Unique!HashFunction hash = getRtssHashById(hash_id); 131 132 if (shares[0].length != secret_len + hash.outputLength + RTSS_HEADER_SIZE + 1) { 133 hash.release(); 134 assert(hash.outputLength > 0); 135 throw new DecodingError("Bad RTSS length field in header " ~ shares[0].length.to!string ~ " != " ~ (secret_len + hash.outputLength + RTSS_HEADER_SIZE + 1).to!string); 136 } 137 Vector!ubyte V = Vector!ubyte(shares.length); 138 SecureVector!ubyte secret = SecureVector!ubyte(); 139 140 foreach (size_t i; (RTSS_HEADER_SIZE + 1) .. shares[0].length) 141 { 142 foreach (size_t j; 0 .. V.length) 143 V[j] = shares[j].m_contents[i]; 144 145 ubyte r = 0; 146 foreach (size_t k; 0 .. shares.length) 147 { 148 // L_i function: 149 ubyte r2 = 1; 150 foreach (size_t l; 0 .. shares.length) 151 { 152 if (k == l) 153 continue; 154 155 ubyte share_k = shares[k].shareId(); 156 ubyte share_l = shares[l].shareId(); 157 158 if (share_k == share_l) 159 throw new DecodingError("Duplicate shares found in RTSS recovery"); 160 161 ubyte div = RTSS_EXP[(255 + RTSS_LOG[share_l] - RTSS_LOG[share_k ^ share_l]) % 255]; 162 163 r2 = gfp_mul(r2, div); 164 } 165 166 r ^= gfp_mul(V[k], r2); 167 } 168 secret.pushBack(r); 169 } 170 171 if (secret.length != secret_len + hash.outputLength) 172 throw new DecodingError("Bad length in RTSS output"); 173 174 hash.update(secret.ptr, secret_len); 175 SecureVector!ubyte hash_check = hash.finished(); 176 177 if (!sameMem(hash_check.ptr, &secret[secret_len], hash.outputLength)) 178 throw new DecodingError("RTSS hash check failed"); 179 secret.length = secret_len; 180 return secret; 181 } 182 183 184 185 /** 186 * Params: 187 * hex_input = the share encoded in hexadecimal 188 */ 189 this(in string hex_input) 190 { 191 m_contents = SecureArray!ubyte(hexDecodeLocked(hex_input)[]); 192 } 193 194 195 /** 196 * Returns: hex representation 197 */ 198 string toString() const 199 { 200 return hexEncode(m_contents.ptr, m_contents.length); 201 } 202 203 /** 204 * Returns: share identifier 205 */ 206 ubyte shareId() const 207 { 208 if (!initialized()) 209 throw new InvalidState("shareId not initialized"); 210 211 return m_contents[20]; 212 } 213 214 /** 215 * Returns: size of this share in bytes 216 */ 217 size_t size() const { return m_contents.length; } 218 219 /// ditto 220 @property size_t length() const { return size(); } 221 222 /** 223 * Returns: if this TSS share was initialized or not 224 */ 225 bool initialized() const { return (m_contents.length > 0); } 226 private: 227 SecureArray!ubyte m_contents; 228 } 229 230 231 private: 232 233 /** 234 Table for GF(2^8) arithmetic (exponentials) 235 */ 236 __gshared immutable ubyte[256] RTSS_EXP = [ 237 0x01, 0x03, 0x05, 0x0F, 0x11, 0x33, 0x55, 0xFF, 0x1A, 0x2E, 0x72, 238 0x96, 0xA1, 0xF8, 0x13, 0x35, 0x5F, 0xE1, 0x38, 0x48, 0xD8, 0x73, 239 0x95, 0xA4, 0xF7, 0x02, 0x06, 0x0A, 0x1E, 0x22, 0x66, 0xAA, 0xE5, 240 0x34, 0x5C, 0xE4, 0x37, 0x59, 0xEB, 0x26, 0x6A, 0xBE, 0xD9, 0x70, 241 0x90, 0xAB, 0xE6, 0x31, 0x53, 0xF5, 0x04, 0x0C, 0x14, 0x3C, 0x44, 242 0xCC, 0x4F, 0xD1, 0x68, 0xB8, 0xD3, 0x6E, 0xB2, 0xCD, 0x4C, 0xD4, 243 0x67, 0xA9, 0xE0, 0x3B, 0x4D, 0xD7, 0x62, 0xA6, 0xF1, 0x08, 0x18, 244 0x28, 0x78, 0x88, 0x83, 0x9E, 0xB9, 0xD0, 0x6B, 0xBD, 0xDC, 0x7F, 245 0x81, 0x98, 0xB3, 0xCE, 0x49, 0xDB, 0x76, 0x9A, 0xB5, 0xC4, 0x57, 246 0xF9, 0x10, 0x30, 0x50, 0xF0, 0x0B, 0x1D, 0x27, 0x69, 0xBB, 0xD6, 247 0x61, 0xA3, 0xFE, 0x19, 0x2B, 0x7D, 0x87, 0x92, 0xAD, 0xEC, 0x2F, 248 0x71, 0x93, 0xAE, 0xE9, 0x20, 0x60, 0xA0, 0xFB, 0x16, 0x3A, 0x4E, 249 0xD2, 0x6D, 0xB7, 0xC2, 0x5D, 0xE7, 0x32, 0x56, 0xFA, 0x15, 0x3F, 250 0x41, 0xC3, 0x5E, 0xE2, 0x3D, 0x47, 0xC9, 0x40, 0xC0, 0x5B, 0xED, 251 0x2C, 0x74, 0x9C, 0xBF, 0xDA, 0x75, 0x9F, 0xBA, 0xD5, 0x64, 0xAC, 252 0xEF, 0x2A, 0x7E, 0x82, 0x9D, 0xBC, 0xDF, 0x7A, 0x8E, 0x89, 0x80, 253 0x9B, 0xB6, 0xC1, 0x58, 0xE8, 0x23, 0x65, 0xAF, 0xEA, 0x25, 0x6F, 254 0xB1, 0xC8, 0x43, 0xC5, 0x54, 0xFC, 0x1F, 0x21, 0x63, 0xA5, 0xF4, 255 0x07, 0x09, 0x1B, 0x2D, 0x77, 0x99, 0xB0, 0xCB, 0x46, 0xCA, 0x45, 256 0xCF, 0x4A, 0xDE, 0x79, 0x8B, 0x86, 0x91, 0xA8, 0xE3, 0x3E, 0x42, 257 0xC6, 0x51, 0xF3, 0x0E, 0x12, 0x36, 0x5A, 0xEE, 0x29, 0x7B, 0x8D, 258 0x8C, 0x8F, 0x8A, 0x85, 0x94, 0xA7, 0xF2, 0x0D, 0x17, 0x39, 0x4B, 259 0xDD, 0x7C, 0x84, 0x97, 0xA2, 0xFD, 0x1C, 0x24, 0x6C, 0xB4, 0xC7, 260 0x52, 0xF6, 0x01 ]; 261 262 /** 263 Table for GF(2^8) arithmetic (logarithms) 264 */ 265 __gshared immutable ubyte[] RTSS_LOG = [ 266 0x90, 0x00, 0x19, 0x01, 0x32, 0x02, 0x1A, 0xC6, 0x4B, 0xC7, 0x1B, 267 0x68, 0x33, 0xEE, 0xDF, 0x03, 0x64, 0x04, 0xE0, 0x0E, 0x34, 0x8D, 268 0x81, 0xEF, 0x4C, 0x71, 0x08, 0xC8, 0xF8, 0x69, 0x1C, 0xC1, 0x7D, 269 0xC2, 0x1D, 0xB5, 0xF9, 0xB9, 0x27, 0x6A, 0x4D, 0xE4, 0xA6, 0x72, 270 0x9A, 0xC9, 0x09, 0x78, 0x65, 0x2F, 0x8A, 0x05, 0x21, 0x0F, 0xE1, 271 0x24, 0x12, 0xF0, 0x82, 0x45, 0x35, 0x93, 0xDA, 0x8E, 0x96, 0x8F, 272 0xDB, 0xBD, 0x36, 0xD0, 0xCE, 0x94, 0x13, 0x5C, 0xD2, 0xF1, 0x40, 273 0x46, 0x83, 0x38, 0x66, 0xDD, 0xFD, 0x30, 0xBF, 0x06, 0x8B, 0x62, 274 0xB3, 0x25, 0xE2, 0x98, 0x22, 0x88, 0x91, 0x10, 0x7E, 0x6E, 0x48, 275 0xC3, 0xA3, 0xB6, 0x1E, 0x42, 0x3A, 0x6B, 0x28, 0x54, 0xFA, 0x85, 276 0x3D, 0xBA, 0x2B, 0x79, 0x0A, 0x15, 0x9B, 0x9F, 0x5E, 0xCA, 0x4E, 277 0xD4, 0xAC, 0xE5, 0xF3, 0x73, 0xA7, 0x57, 0xAF, 0x58, 0xA8, 0x50, 278 0xF4, 0xEA, 0xD6, 0x74, 0x4F, 0xAE, 0xE9, 0xD5, 0xE7, 0xE6, 0xAD, 279 0xE8, 0x2C, 0xD7, 0x75, 0x7A, 0xEB, 0x16, 0x0B, 0xF5, 0x59, 0xCB, 280 0x5F, 0xB0, 0x9C, 0xA9, 0x51, 0xA0, 0x7F, 0x0C, 0xF6, 0x6F, 0x17, 281 0xC4, 0x49, 0xEC, 0xD8, 0x43, 0x1F, 0x2D, 0xA4, 0x76, 0x7B, 0xB7, 282 0xCC, 0xBB, 0x3E, 0x5A, 0xFB, 0x60, 0xB1, 0x86, 0x3B, 0x52, 0xA1, 283 0x6C, 0xAA, 0x55, 0x29, 0x9D, 0x97, 0xB2, 0x87, 0x90, 0x61, 0xBE, 284 0xDC, 0xFC, 0xBC, 0x95, 0xCF, 0xCD, 0x37, 0x3F, 0x5B, 0xD1, 0x53, 285 0x39, 0x84, 0x3C, 0x41, 0xA2, 0x6D, 0x47, 0x14, 0x2A, 0x9E, 0x5D, 286 0x56, 0xF2, 0xD3, 0xAB, 0x44, 0x11, 0x92, 0xD9, 0x23, 0x20, 0x2E, 287 0x89, 0xB4, 0x7C, 0xB8, 0x26, 0x77, 0x99, 0xE3, 0xA5, 0x67, 0x4A, 288 0xED, 0xDE, 0xC5, 0x31, 0xFE, 0x18, 0x0D, 0x63, 0x8C, 0x80, 0xC0, 289 0xF7, 0x70, 0x07 ]; 290 291 ubyte gfp_mul(ubyte x, ubyte y) 292 { 293 if (x == 0 || y == 0) 294 return 0; 295 return RTSS_EXP[(RTSS_LOG[x] + RTSS_LOG[y]) % 255]; 296 } 297 298 ubyte rtssHashId(in string hash_name) 299 { 300 if (hash_name == "SHA-160") 301 return 1; 302 else if (hash_name == "SHA-256") 303 return 2; 304 else 305 throw new InvalidArgument("RTSS only supports SHA-1 and SHA-256"); 306 } 307 308 HashFunction getRtssHashById(ubyte id) 309 { 310 if (id == 1) 311 return new SHA160; 312 else if (id == 2) 313 return new SHA256; 314 else 315 throw new DecodingError("Bad RTSS hash identifier"); 316 } 317 318 static if (BOTAN_TEST): 319 import botan.test; 320 import botan.rng.auto_rng; 321 import botan.codec.hex; 322 323 static if (BOTAN_HAS_TESTS && !SKIP_TSS_TEST) unittest 324 { 325 logDebug("Testing tss.d ..."); 326 Unique!AutoSeededRNG rng = new AutoSeededRNG; 327 328 size_t fails = 0; 329 330 ubyte[16] id; 331 for(int i = 0; i != 16; ++i) 332 id[i] = cast(ubyte)i; 333 334 const SecureVector!ubyte S = hexDecodeLocked("7465737400"); 335 logTrace("Split TSS: ", S[]); 336 Vector!RTSS shares = RTSS.split(cast(ubyte)2, cast(ubyte)4, cast(const(ubyte)*)&S[0], S.length, id, *rng); 337 logTrace("Reconstruct TSS"); 338 auto back = RTSS.reconstruct(shares); 339 340 if (S != back) 341 { 342 logTrace("TSS-0: " ~ hexEncode(S) ~ " != " ~ hexEncode(back)); 343 ++fails; 344 } 345 Object ta; 346 shares.resize(shares.length-1); 347 348 back = RTSS.reconstruct(shares); 349 350 if (S != back) 351 { 352 logTrace("TSS-1: " ~ hexEncode(S) ~ " != " ~ hexEncode(back)); 353 ++fails; 354 } 355 356 testReport("tss", 2, fails); 357 }