]> git.zerfleddert.de Git - proxmark3-svn/blame - client/hardnested/hardnested_bruteforce.c
Update appmain.c
[proxmark3-svn] / client / hardnested / hardnested_bruteforce.c
CommitLineData
c48c4d78 1//-----------------------------------------------------------------------------
2// Copyright (C) 2016, 2017 by piwi
3//
4// This code is licensed to you under the terms of the GNU GPL, version 2 or,
5// at your option, any later version. See the LICENSE.txt file for the text of
6// the license.
7//-----------------------------------------------------------------------------
8// Implements a card only attack based on crypto text (encrypted nonces
9// received during a nested authentication) only. Unlike other card only
10// attacks this doesn't rely on implementation errors but only on the
11// inherent weaknesses of the crypto1 cypher. Described in
12// Carlo Meijer, Roel Verdult, "Ciphertext-only Cryptanalysis on Hardened
13// Mifare Classic Cards" in Proceedings of the 22nd ACM SIGSAC Conference on
14// Computer and Communications Security, 2015
15//-----------------------------------------------------------------------------
16//
17// brute forcing is based on @aczids bitsliced brute forcer
18// https://github.com/aczid/crypto1_bs with some modifications. Mainly:
19// - don't rollback. Start with 2nd byte of nonce instead
20// - reuse results of filter subfunctions
21// - reuse results of previous nonces if some first bits are identical
22//
23//-----------------------------------------------------------------------------
24// aczid's Copyright notice:
25//
26// Bit-sliced Crypto-1 brute-forcing implementation
27// Builds on the data structures returned by CraptEV1 craptev1_get_space(nonces, threshold, uid)
28/*
29Copyright (c) 2015-2016 Aram Verstegen
30
31Permission is hereby granted, free of charge, to any person obtaining a copy
32of this software and associated documentation files (the "Software"), to deal
33in the Software without restriction, including without limitation the rights
34to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
35copies of the Software, and to permit persons to whom the Software is
36furnished to do so, subject to the following conditions:
37
38The above copyright notice and this permission notice shall be included in
39all copies or substantial portions of the Software.
40
41THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
44AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
45LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
46OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
47THE SOFTWARE.
48*/
49
50#include "hardnested_bruteforce.h"
51
52#include <inttypes.h>
53#include <stdbool.h>
54#include <stdio.h>
55#include <pthread.h>
56#include <string.h>
c3d117a8 57#include <stdlib.h>
c48c4d78 58#include "proxmark3.h"
59#include "cmdhfmfhard.h"
60#include "hardnested_bf_core.h"
61#include "ui.h"
62#include "util.h"
ec9c7112 63#include "util_posix.h"
c48c4d78 64#include "crapto1/crapto1.h"
65#include "parity.h"
66
67#define NUM_BRUTE_FORCE_THREADS (num_CPUs())
68#define DEFAULT_BRUTE_FORCE_RATE (120000000.0) // if benchmark doesn't succeed
69#define TEST_BENCH_SIZE (6000) // number of odd and even states for brute force benchmark
70#define TEST_BENCH_FILENAME "hardnested/bf_bench_data.bin"
71//#define WRITE_BENCH_FILE
72
73// debugging options
74#define DEBUG_KEY_ELIMINATION
75// #define DEBUG_BRUTE_FORCE
76
77typedef enum {
78 EVEN_STATE = 0,
79 ODD_STATE = 1
80} odd_even_t;
81
82static uint32_t nonces_to_bruteforce = 0;
83static uint32_t bf_test_nonce[256];
84static uint8_t bf_test_nonce_2nd_byte[256];
85static uint8_t bf_test_nonce_par[256];
86static uint32_t bucket_count = 0;
87static statelist_t* buckets[128];
88static uint32_t keys_found = 0;
89static uint64_t num_keys_tested;
90
91
92uint8_t trailing_zeros(uint8_t byte)
93{
94 static const uint8_t trailing_zeros_LUT[256] = {
95 8, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
96 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
97 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
98 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
99 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
100 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
101 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
102 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
103 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
104 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
105 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
106 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
107 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
108 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
109 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
110 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
111 };
112
113 return trailing_zeros_LUT[byte];
114}
115
116
117bool verify_key(uint32_t cuid, noncelist_t *nonces, uint8_t *best_first_bytes, uint32_t odd, uint32_t even)
118{
119 struct Crypto1State pcs;
120 for (uint16_t test_first_byte = 1; test_first_byte < 256; test_first_byte++) {
121 noncelistentry_t *test_nonce = nonces[best_first_bytes[test_first_byte]].first;
122 while (test_nonce != NULL) {
123 pcs.odd = odd;
124 pcs.even = even;
125 lfsr_rollback_byte(&pcs, (cuid >> 24) ^ best_first_bytes[0], true);
126 for (int8_t byte_pos = 3; byte_pos >= 0; byte_pos--) {
127 uint8_t test_par_enc_bit = (test_nonce->par_enc >> byte_pos) & 0x01; // the encoded parity bit
128 uint8_t test_byte_enc = (test_nonce->nonce_enc >> (8*byte_pos)) & 0xff; // the encoded nonce byte
129 uint8_t test_byte_dec = crypto1_byte(&pcs, test_byte_enc /* ^ (cuid >> (8*byte_pos)) */, true) ^ test_byte_enc; // decode the nonce byte
130 uint8_t ks_par = filter(pcs.odd); // the keystream bit to encode/decode the parity bit
131 uint8_t test_par_enc2 = ks_par ^ evenparity8(test_byte_dec); // determine the decoded byte's parity and encode it
132 if (test_par_enc_bit != test_par_enc2) {
133 return false;
134 }
135 }
136 test_nonce = test_nonce->next;
137 }
138 }
139 return true;
140}
141
ec087218 142static void*
143#ifdef __has_attribute
144#if __has_attribute(force_align_arg_pointer)
145__attribute__((force_align_arg_pointer))
146#endif
147#endif
148crack_states_thread(void* x){
c48c4d78 149
150 struct arg {
151 bool silent;
152 int thread_ID;
153 uint32_t cuid;
154 uint32_t num_acquired_nonces;
155 uint64_t maximum_states;
156 noncelist_t *nonces;
157 uint8_t* best_first_bytes;
158 } *thread_arg;
159
160 thread_arg = (struct arg *)x;
161 const int thread_id = thread_arg->thread_ID;
162 uint32_t current_bucket = thread_id;
163 while(current_bucket < bucket_count){
164 statelist_t *bucket = buckets[current_bucket];
165 if(bucket){
166#if defined (DEBUG_BRUTE_FORCE)
167 printf("Thread %u starts working on bucket %u\n", thread_id, current_bucket);
168#endif
169 const uint64_t key = crack_states_bitsliced(thread_arg->cuid, thread_arg->best_first_bytes, bucket, &keys_found, &num_keys_tested, nonces_to_bruteforce, bf_test_nonce_2nd_byte, thread_arg->nonces);
170 if(key != -1){
171 __sync_fetch_and_add(&keys_found, 1);
172 char progress_text[80];
173 sprintf(progress_text, "Brute force phase completed. Key found: %012" PRIx64, key);
174 hardnested_print_progress(thread_arg->num_acquired_nonces, progress_text, 0.0, 0);
175 break;
176 } else if(keys_found){
177 break;
178 } else {
179 if (!thread_arg->silent) {
180 char progress_text[80];
181 sprintf(progress_text, "Brute force phase: %6.02f%%", 100.0*(float)num_keys_tested/(float)(thread_arg->maximum_states));
182 float remaining_bruteforce = thread_arg->nonces[thread_arg->best_first_bytes[0]].expected_num_brute_force - (float)num_keys_tested/2;
183 hardnested_print_progress(thread_arg->num_acquired_nonces, progress_text, remaining_bruteforce, 5000);
184 }
185 }
186 }
187 current_bucket += NUM_BRUTE_FORCE_THREADS;
188 }
189 return NULL;
190}
191
192
193void prepare_bf_test_nonces(noncelist_t *nonces, uint8_t best_first_byte)
194{
195 // we do bitsliced brute forcing with best_first_bytes[0] only.
196 // Extract the corresponding 2nd bytes
197 noncelistentry_t *test_nonce = nonces[best_first_byte].first;
198 uint32_t i = 0;
199 while (test_nonce != NULL) {
200 bf_test_nonce[i] = test_nonce->nonce_enc;
201 bf_test_nonce_par[i] = test_nonce->par_enc;
202 bf_test_nonce_2nd_byte[i] = (test_nonce->nonce_enc >> 16) & 0xff;
203 test_nonce = test_nonce->next;
204 i++;
205 }
206 nonces_to_bruteforce = i;
207
208 // printf("Nonces to bruteforce: %d\n", nonces_to_bruteforce);
209 // printf("Common bits of first 4 2nd nonce bytes (before sorting): %u %u %u\n",
210 // trailing_zeros(bf_test_nonce_2nd_byte[1] ^ bf_test_nonce_2nd_byte[0]),
211 // trailing_zeros(bf_test_nonce_2nd_byte[2] ^ bf_test_nonce_2nd_byte[1]),
212 // trailing_zeros(bf_test_nonce_2nd_byte[3] ^ bf_test_nonce_2nd_byte[2]));
213
214 uint8_t best_4[4] = {0};
215 int sum_best = -1;
216 for (uint16_t n1 = 0; n1 < nonces_to_bruteforce; n1++) {
217 for (uint16_t n2 = 0; n2 < nonces_to_bruteforce; n2++) {
218 if (n2 != n1) {
219 for (uint16_t n3 = 0; n3 < nonces_to_bruteforce; n3++) {
220 if ((n3 != n2 && n3 != n1) || nonces_to_bruteforce < 3
221 // && trailing_zeros(bf_test_nonce_2nd_byte[n1] ^ bf_test_nonce_2nd_byte[n2])
222 // > trailing_zeros(bf_test_nonce_2nd_byte[n2] ^ bf_test_nonce_2nd_byte[n3])
223 ) {
224 for (uint16_t n4 = 0; n4 < nonces_to_bruteforce; n4++) {
225 if ((n4 != n3 && n4 != n2 && n4 != n1) || nonces_to_bruteforce < 4
226 // && trailing_zeros(bf_test_nonce_2nd_byte[n2] ^ bf_test_nonce_2nd_byte[n3])
227 // > trailing_zeros(bf_test_nonce_2nd_byte[n3] ^ bf_test_nonce_2nd_byte[n4])
228 ) {
229 int sum = nonces_to_bruteforce > 1 ? trailing_zeros(bf_test_nonce_2nd_byte[n1] ^ bf_test_nonce_2nd_byte[n2]) : 0.0
230 + nonces_to_bruteforce > 2 ? trailing_zeros(bf_test_nonce_2nd_byte[n2] ^ bf_test_nonce_2nd_byte[n3]) : 0.0
231 + nonces_to_bruteforce > 3 ? trailing_zeros(bf_test_nonce_2nd_byte[n3] ^ bf_test_nonce_2nd_byte[n4]) : 0.0;
232 if (sum > sum_best) {
233 sum_best = sum;
234 best_4[0] = n1;
235 best_4[1] = n2;
236 best_4[2] = n3;
237 best_4[3] = n4;
238 }
239 }
240 }
241 }
242 }
243 }
244 }
245 }
246
247 uint32_t bf_test_nonce_temp[4];
248 uint8_t bf_test_nonce_par_temp[4];
249 uint8_t bf_test_nonce_2nd_byte_temp[4];
250 for (uint8_t i = 0; i < 4 && i < nonces_to_bruteforce; i++) {
251 bf_test_nonce_temp[i] = bf_test_nonce[best_4[i]];
252
253 bf_test_nonce_par_temp[i] = bf_test_nonce_par[best_4[i]];
254 bf_test_nonce_2nd_byte_temp[i] = bf_test_nonce_2nd_byte[best_4[i]];
255 }
256 for (uint8_t i = 0; i < 4 && i < nonces_to_bruteforce; i++) {
257 bf_test_nonce[i] = bf_test_nonce_temp[i];
258 bf_test_nonce_par[i] = bf_test_nonce_par_temp[i];
259 bf_test_nonce_2nd_byte[i] = bf_test_nonce_2nd_byte_temp[i];
260 }
261}
262
263
264#if defined (WRITE_BENCH_FILE)
265static void write_benchfile(statelist_t *candidates) {
266
267 printf("Writing brute force benchmark data...");
268 FILE *benchfile = fopen(TEST_BENCH_FILENAME, "wb");
269 fwrite(&nonces_to_bruteforce, 1, sizeof(nonces_to_bruteforce), benchfile);
270 for (uint32_t i = 0; i < nonces_to_bruteforce; i++) {
271 fwrite(&(bf_test_nonce[i]), 1, sizeof(bf_test_nonce[i]), benchfile);
272 fwrite(&(bf_test_nonce_par[i]), 1, sizeof(bf_test_nonce_par[i]), benchfile);
273 }
274 uint32_t num_states = MIN(candidates->len[EVEN_STATE], TEST_BENCH_SIZE);
275 fwrite(&num_states, 1, sizeof(num_states), benchfile);
276 for (uint32_t i = 0; i < num_states; i++) {
277 fwrite(&(candidates->states[EVEN_STATE][i]), 1, sizeof(uint32_t), benchfile);
278 }
279 num_states = MIN(candidates->len[ODD_STATE], TEST_BENCH_SIZE);
280 fwrite(&num_states, 1, sizeof(num_states), benchfile);
281 for (uint32_t i = 0; i < num_states; i++) {
282 fwrite(&(candidates->states[ODD_STATE][i]), 1, sizeof(uint32_t), benchfile);
283 }
284 fclose(benchfile);
285 printf("done.\n");
286}
287#endif
288
289
290bool brute_force_bs(float *bf_rate, statelist_t *candidates, uint32_t cuid, uint32_t num_acquired_nonces, uint64_t maximum_states, noncelist_t *nonces, uint8_t *best_first_bytes)
291{
292#if defined (WRITE_BENCH_FILE)
293 write_benchfile(candidates);
294#endif
295 bool silent = (bf_rate != NULL);
296
297 // if (!silent) {
298 // PrintAndLog("Brute force phase starting.");
299 // PrintAndLog("Using %u-bit bitslices", MAX_BITSLICES);
300 // }
301
302 keys_found = 0;
303 num_keys_tested = 0;
304
305 bitslice_test_nonces(nonces_to_bruteforce, bf_test_nonce, bf_test_nonce_par);
306
307 // count number of states to go
308 bucket_count = 0;
309 for (statelist_t *p = candidates; p != NULL; p = p->next) {
310 if (p->states[ODD_STATE] != NULL && p->states[EVEN_STATE] != NULL) {
311 buckets[bucket_count] = p;
312 bucket_count++;
313 }
314 }
315
316 uint64_t start_time = msclock();
317 // enumerate states using all hardware threads, each thread handles one bucket
318 // if (!silent) {
319 // PrintAndLog("Starting %u cracking threads to search %u buckets containing a total of %" PRIu64" states...\n", NUM_BRUTE_FORCE_THREADS, bucket_count, maximum_states);
320 // printf("Common bits of first 4 2nd nonce bytes: %u %u %u\n",
321 // trailing_zeros(bf_test_nonce_2nd_byte[1] ^ bf_test_nonce_2nd_byte[0]),
322 // trailing_zeros(bf_test_nonce_2nd_byte[2] ^ bf_test_nonce_2nd_byte[1]),
323 // trailing_zeros(bf_test_nonce_2nd_byte[3] ^ bf_test_nonce_2nd_byte[2]));
324 // }
325
326 pthread_t threads[NUM_BRUTE_FORCE_THREADS];
327 struct args {
328 bool silent;
329 int thread_ID;
330 uint32_t cuid;
331 uint32_t num_acquired_nonces;
332 uint64_t maximum_states;
333 noncelist_t *nonces;
334 uint8_t *best_first_bytes;
335 } thread_args[NUM_BRUTE_FORCE_THREADS];
336
337 for(uint32_t i = 0; i < NUM_BRUTE_FORCE_THREADS; i++){
338 thread_args[i].thread_ID = i;
339 thread_args[i].silent = silent;
340 thread_args[i].cuid = cuid;
341 thread_args[i].num_acquired_nonces = num_acquired_nonces;
342 thread_args[i].maximum_states = maximum_states;
343 thread_args[i].nonces = nonces;
344 thread_args[i].best_first_bytes = best_first_bytes;
345 pthread_create(&threads[i], NULL, crack_states_thread, (void*)&thread_args[i]);
346 }
347 for(uint32_t i = 0; i < NUM_BRUTE_FORCE_THREADS; i++){
348 pthread_join(threads[i], 0);
349 }
350
351 uint64_t elapsed_time = msclock() - start_time;
352
353 // if (!silent) {
354 // printf("Brute force completed after testing %" PRIu64" (2^%1.1f) keys in %1.1f seconds at a rate of %1.0f (2^%1.1f) keys per second.\n",
355 // num_keys_tested,
356 // log(num_keys_tested) / log(2.0),
357 // (float)elapsed_time/1000.0,
358 // (float)num_keys_tested / ((float)elapsed_time / 1000.0),
359 // log((float)num_keys_tested / ((float)elapsed_time/1000.0)) / log(2.0));
360 // }
361
362 if (bf_rate != NULL) {
363 *bf_rate = (float)num_keys_tested / ((float)elapsed_time / 1000.0);
364 }
365
366 return (keys_found != 0);
367}
368
369
370static bool read_bench_data(statelist_t *test_candidates) {
371
372 size_t bytes_read = 0;
373 uint32_t temp = 0;
374 uint32_t num_states = 0;
375 uint32_t states_read = 0;
376
377 char bench_file_path[strlen(get_my_executable_directory()) + strlen(TEST_BENCH_FILENAME) + 1];
378 strcpy(bench_file_path, get_my_executable_directory());
379 strcat(bench_file_path, TEST_BENCH_FILENAME);
380
381 FILE *benchfile = fopen(bench_file_path, "rb");
382 if (benchfile == NULL) {
383 return false;
384 }
385 bytes_read = fread(&nonces_to_bruteforce, 1, sizeof(nonces_to_bruteforce), benchfile);
386 if (bytes_read != sizeof(nonces_to_bruteforce)) {
387 fclose(benchfile);
388 return false;
389 }
390 for (uint16_t i = 0; i < nonces_to_bruteforce && i < 256; i++) {
391 bytes_read = fread(&bf_test_nonce[i], 1, sizeof(uint32_t), benchfile);
392 if (bytes_read != sizeof(uint32_t)) {
393 fclose(benchfile);
394 return false;
395 }
396 bf_test_nonce_2nd_byte[i] = (bf_test_nonce[i] >> 16) & 0xff;
397 bytes_read = fread(&bf_test_nonce_par[i], 1, sizeof(uint8_t), benchfile);
398 if (bytes_read != sizeof(uint8_t)) {
399 fclose(benchfile);
400 return false;
401 }
402 }
403 bytes_read = fread(&num_states, 1, sizeof(uint32_t), benchfile);
404 if (bytes_read != sizeof(uint32_t)) {
405 fclose(benchfile);
406 return false;
407 }
408 for (states_read = 0; states_read < MIN(num_states, TEST_BENCH_SIZE); states_read++) {
409 bytes_read = fread(test_candidates->states[EVEN_STATE] + states_read, 1, sizeof(uint32_t), benchfile);
410 if (bytes_read != sizeof(uint32_t)) {
411 fclose(benchfile);
412 return false;
413 }
414 }
415 for (uint32_t i = states_read; i < TEST_BENCH_SIZE; i++) {
416 test_candidates->states[EVEN_STATE][i] = test_candidates->states[EVEN_STATE][i-states_read];
417 }
418 for (uint32_t i = states_read; i < num_states; i++) {
419 bytes_read = fread(&temp, 1, sizeof(uint32_t), benchfile);
420 if (bytes_read != sizeof(uint32_t)) {
421 fclose(benchfile);
422 return false;
423 }
424 }
425 for (states_read = 0; states_read < MIN(num_states, TEST_BENCH_SIZE); states_read++) {
426 bytes_read = fread(test_candidates->states[ODD_STATE] + states_read, 1, sizeof(uint32_t), benchfile);
427 if (bytes_read != sizeof(uint32_t)) {
428 fclose(benchfile);
429 return false;
430 }
431 }
432 for (uint32_t i = states_read; i < TEST_BENCH_SIZE; i++) {
433 test_candidates->states[ODD_STATE][i] = test_candidates->states[ODD_STATE][i-states_read];
434 }
435
436 fclose(benchfile);
437 return true;
438}
439
440
441float brute_force_benchmark()
442{
443 statelist_t test_candidates[NUM_BRUTE_FORCE_THREADS];
444
445 test_candidates[0].states[ODD_STATE] = malloc((TEST_BENCH_SIZE+1) * sizeof(uint32_t));
446 test_candidates[0].states[EVEN_STATE] = malloc((TEST_BENCH_SIZE+1) * sizeof(uint32_t));
447 for (uint8_t i = 0; i < NUM_BRUTE_FORCE_THREADS - 1; i++){
448 test_candidates[i].next = test_candidates + i + 1;
449 test_candidates[i+1].states[ODD_STATE] = test_candidates[0].states[ODD_STATE];
450 test_candidates[i+1].states[EVEN_STATE] = test_candidates[0].states[EVEN_STATE];
451 }
452 test_candidates[NUM_BRUTE_FORCE_THREADS-1].next = NULL;
453
454 if (!read_bench_data(test_candidates)) {
455 PrintAndLog("Couldn't read benchmark data. Assuming brute force rate of %1.0f states per second", DEFAULT_BRUTE_FORCE_RATE);
456 return DEFAULT_BRUTE_FORCE_RATE;
457 }
458
459 for (uint8_t i = 0; i < NUM_BRUTE_FORCE_THREADS; i++) {
460 test_candidates[i].len[ODD_STATE] = TEST_BENCH_SIZE;
461 test_candidates[i].len[EVEN_STATE] = TEST_BENCH_SIZE;
462 test_candidates[i].states[ODD_STATE][TEST_BENCH_SIZE] = -1;
463 test_candidates[i].states[EVEN_STATE][TEST_BENCH_SIZE] = -1;
464 }
465
466 uint64_t maximum_states = TEST_BENCH_SIZE*TEST_BENCH_SIZE*(uint64_t)NUM_BRUTE_FORCE_THREADS;
467
468 float bf_rate;
469 brute_force_bs(&bf_rate, test_candidates, 0, 0, maximum_states, NULL, 0);
470
471 free(test_candidates[0].states[ODD_STATE]);
472 free(test_candidates[0].states[EVEN_STATE]);
473
474 return bf_rate;
475}
476
477
Impressum, Datenschutz