1 /// \file KeyRecover.d 2 3 module tagion.betterC.wallet.KeyRecover; 4 5 //use net directly 6 import tagion.betterC.utils.Memory; 7 import tagion.betterC.wallet.Net; 8 9 // // use better C doc, hibon, hibon record 10 import std.string : representation; 11 import tagion.basic.Types : Buffer; 12 import tagion.betterC.hibon.Document : Document; 13 import tagion.betterC.hibon.HiBON : HiBONT; 14 import std.range : iota, indexed, lockstep; // commented stuff produce error no TypeInfo in betterC 15 import std.algorithm.iteration : filter, map; 16 import std.algorithm.mutation : copy; 17 import tagion.betterC.utils.Miscellaneous; 18 import tagion.betterC.wallet.WalletRecords : RecoverGenerator; 19 20 struct KeyRecover { 21 enum MAX_QUESTION = 10; 22 enum MAX_SEEDS = 64; 23 protected RecoverGenerator generator; 24 25 this(RecoverGenerator generator) { 26 this.generator = generator; 27 } 28 29 inout(HiBONT) toHiBON() inout { 30 return generator.toHiBON; 31 } 32 33 const(Document) toDoc() { 34 return generator.toDoc; 35 } 36 37 /** 38 * Generates the quiz hash of the from a list of questions and answers 39 */ 40 Buffer[] quiz(scope const(string[]) questions, scope const(char[][]) answers) const @trusted 41 in { 42 assert(questions.length is answers.length); 43 } 44 do { 45 46 // auto results = new Buffer[questions.length]; 47 Buffer[] results; 48 results.create(questions.length); 49 50 foreach (i, ref result; results) { 51 scope answer = answers[i]; 52 scope question = questions[i]; 53 scope strip_down = cast(ubyte[]) answer.strip_down; 54 scope answer_hash = rawCalcHash(strip_down); 55 scope question_hash = rawCalcHash(question.representation); 56 // scope (exit) { 57 // strip_down.sceamble; 58 // answer_hash.scramble; 59 // question_hash.scramble; 60 // } 61 // const hash = net.calcHash(answer); 62 answer_hash.write(question_hash.serialize); 63 result = rawCalcHash(answer_hash); 64 } 65 return results; 66 } 67 68 static uint numberOfSeeds(const uint M, const uint N) pure nothrow 69 in { 70 assert(M >= N); 71 assert(M <= 10); 72 } 73 do { 74 return (M - N) * N + 1; 75 } 76 77 static unittest { 78 assert(numberOfSeeds(10, 5) is 26); 79 } 80 81 @trusted 82 Buffer checkHash(scope const(ubyte[]) value, scope const(ubyte[]) salt = null) const { 83 return rawCalcHash(rawCalcHash(value)); 84 } 85 86 static void iterateSeeds( 87 const uint M, const uint N, 88 scope bool delegate(scope const(uint[]) indices) @safe dg) { 89 // scope include = new uint[N]; 90 scope uint[] include; 91 include.create(N); 92 iota(N).copy(include); 93 bool end; 94 void local_search(const int index, const int size) @safe { 95 if ((index >= 0) && !end) { 96 if (dg(include)) { 97 end = true; 98 } 99 else { 100 if (include[index] < size) { 101 include[index]++; 102 local_search(index, size); 103 } 104 else if (index > 0) { 105 include[index - 1]++; 106 local_search(index - 1, size - 1); 107 } 108 } 109 } 110 } 111 112 local_search(cast(int) include.length - 1, M - 1); 113 } 114 115 void createKey(scope const(string[]) questions, scope const(char[][]) answers, const uint confidence) { 116 createKey(quiz(questions, answers), confidence); 117 } 118 119 void createKey(Buffer[] A, const uint confidence) { 120 // scope R = new ubyte[hashSize]; 121 scope ubyte[] R; 122 R.create(hashSize); 123 scramble(R); 124 scope (exit) { 125 scramble(R); 126 } 127 quizSeed(R, A, confidence); 128 } 129 130 /** 131 * Generates the quiz seed values from the privat key R and the quiz list 132 */ 133 void quizSeed(scope ref const(ubyte[]) R, Buffer[] A, const uint confidence) { 134 135 const number_of_questions = cast(uint) A.length; 136 const seeds = numberOfSeeds(number_of_questions, confidence); 137 138 // generator.Y = new Buffer[seeds]; 139 generator.Y.create(seeds); 140 uint count; 141 bool calculate_this_seeds(scope const(uint[]) indices) @safe { 142 scope list_of_selected_answers_and_the_secret = indexed(A, indices); 143 generator.Y[count] = xor(R, xor(list_of_selected_answers_and_the_secret)); 144 count++; 145 return false; 146 } 147 148 iterateSeeds(number_of_questions, confidence, &calculate_this_seeds); 149 } 150 151 bool findSecret(scope ref ubyte[] R, scope const(string[]) questions, scope const(char[][]) answers) const { 152 return findSecret(R, quiz(questions, answers)); 153 } 154 155 bool findSecret(scope ref ubyte[] R, Buffer[] A) const { 156 const number_of_questions = cast(uint) A.length; 157 const seeds = numberOfSeeds(number_of_questions, generator.confidence); 158 159 bool result; 160 bool search_for_the_secret(scope const(uint[]) indices) @safe { 161 scope list_of_selected_answers_and_the_secret = indexed(A, indices); 162 const guess = xor(list_of_selected_answers_and_the_secret); 163 foreach (y; generator.Y) { 164 xor(R, y, guess); 165 if (generator.S == checkHash(R)) { 166 result = true; 167 return true; 168 } 169 } 170 return false; 171 } 172 173 iterateSeeds(number_of_questions, generator.confidence, &search_for_the_secret); 174 return result; 175 } 176 } 177 178 char[] strip_down(const(char[]) text) @safe 179 out (result) { 180 assert(result.length > 0); 181 } 182 do { 183 // import std.ascii : toLower, isAlphaNum; 184 185 char[] res; 186 res.create(text.length); 187 foreach (i, letter; text) { 188 res[i] = text[i]; 189 // char c = cast(char) toLower(letter); 190 // if (isAlphaNum(c)) { 191 // res[i] = c; 192 // } 193 } 194 // return res; 195 return res; 196 } 197 198 static immutable(string[]) standard_questions; 199 200 // shared static this() { 201 // standard_questions = [ 202 // "What is your favorite book?", 203 // "What is the name of the road you grew up on?", 204 // "What is your mother’s maiden name?", 205 // "What was the name of your first/current/favorite pet?", 206 // "What was the first company that you worked for?", 207 // "Where did you meet your spouse?", 208 // "Where did you go to high school/college?", 209 // "What is your favorite food?", 210 // "What city were you born in?", 211 // "Where is your favorite place to vacation?" 212 // ]; 213 // } 214 215 unittest { 216 // import tagion.crypto.SecureNet : StdHashNet; 217 // import std.array : join, array; 218 219 // auto selected_questions = indexed(standard_questions, [0, 2, 3, 7, 8]).array.idup; 220 // //writefln("%s", selected_questions.join("\n")); 221 // string[] answers = [ 222 // "mobidick", 223 // "Mother Teresa!", 224 // "Pluto", 225 // "Pizza", 226 // "Maputo" 227 // ]; 228 // KeyRecover recover; 229 // recover.createKey(selected_questions, answers, 3); 230 231 // // auto R = new ubyte[net.hashSize]; 232 // ubyte[] R; 233 // R.create(hashSize); 234 235 // { // All the ansers are correct 236 // const result = recover.findSecret(R, selected_questions, answers); 237 // //writefln("R=%s", R.toHexString); 238 // assert(R.length == hashSize); 239 // assert(result); // Password found 240 // } 241 242 // { // 3 out of 5 answers are correct. This is a valid answer to generate the secret key 243 // string[] good_answers = [ 244 // "MobiDick", 245 // "MOTHER TERESA", 246 // "Fido", 247 // "pizza", 248 // "Maputo" 249 // ]; 250 // // auto goodR = new ubyte[hashSize]; 251 // ubyte[] goodR; 252 // goodR.create(hashSize); 253 // const result = recover.findSecret(goodR, selected_questions, good_answers); 254 // assert(R.length == hashSize); 255 // assert(result); // Password found 256 // assert(R == goodR); 257 // } 258 259 // { // 2 out of 5 answers are correct. This is NOT a valid answer to generate the secret key 260 // string[] bad_answers = [ 261 // "mobidick", 262 // "Monalisa", 263 // "Fido", 264 // "Burger", 265 // "Maputo" 266 // ]; 267 // // auto badR = new ubyte[net.hashSize]; 268 // ubyte[] badR; 269 // badR.create(hashSize); 270 // const result = recover.findSecret(badR, selected_questions, bad_answers); 271 // assert(!result); // Password not found 272 // assert(R != badR); 273 274 // } 275 }