-rw-r--r-- 7469 cryptattacktester-20231020/isd2_prob.cpp raw
#include <map> #include <cassert> #include "transition.h" #include "queue_prob.h" #include "collision_prob.h" #include "isd2_prob.h" using namespace std; bigfloat isd2_prob(const vector<bigint> &params,const vector<bigint> &attackparams) { bigint N = params.at(0); bigint K_orig = params.at(1); bigint W = params.at(2); bigint pos = 0; bigint ITERS = attackparams.at(pos++); bigint RESET = attackparams.at(pos++); bigint X = attackparams.at(pos++); bigint YX = attackparams.at(pos++); auto Y = X+YX; bigint PIJ = attackparams.at(pos++); bigint PI = attackparams.at(pos++); bigint L0 = attackparams.at(pos++); bigint L1 = attackparams.at(pos++); bigint CHECKPI = attackparams.at(pos++); bigint CHECKSUM = attackparams.at(pos++); bigint D = attackparams.at(pos++); bigint Z = attackparams.at(pos++); bigint QU0 = attackparams.at(pos++); bigint QF0 = attackparams.at(pos++); auto PE0 = QF0*QU0; bigint WI0 = attackparams.at(pos++); bigint QU1 = attackparams.at(pos++); bigint QF1 = attackparams.at(pos++); auto PE1 = QF1*QU1; bigint WI1 = attackparams.at(pos++); bigint FW = attackparams.at(pos++); assert(CHECKPI || CHECKSUM); bigint K = K_orig-FW; bigint L = L0+L1; bigint left = (K+L-Z)/2; bigint right = K+L-Z-left; bigint listsize0 = binomial(left,PIJ); bigint listsize1 = binomial(right,PIJ); bigint listsize = listsize0+listsize1; WI0 = min(WI0,bigint(listsize-1)); bigint pool = (2*listsize-WI0-1)*WI0/2; bigint queue_clears = (pool+PE0-1)/PE0; bigint leftovers = pool%PE0; for (bigint prec = 32;;prec *= 2) { bigfloat::set_default_precision(prec); map<pair<bigint,bigint>,bigfloat> prsplit; map<pair<bigint,bigint>,bigint> representations; for (bigint E = 0;E <= 2*PIJ;E += 2) { if (CHECKPI) if (E != PI) continue; for (bigint F = 0;F <= 2*PIJ;F += 2) { if (CHECKPI) if (F != PI) continue; pair<bigint,bigint> EF = make_pair(E,F); // ----- basic error split // chance of (E,F,0,W-E-F) errors in (left,right,Z,N-K-L): // binomial(left,E)*binomial(right,F)*binomial(N-K-L,W-E-F)/binomial(N,W) // compare to chance of having (E+F,W-E-F) errors in (K+L,N-K-L): // binomial(K+L,E+F)*binomial(N-K-L,W-E-F)/binomial(N,W) bigint prnumer = binomial(left,E)*binomial(right,F); bigint prdenom = binomial(K+L,E+F); prsplit[EF] = bigfloat(prnumer)/bigfloat(prdenom); // _conditional_ probability of (E,F,0,W-E-F) _given_ (E+F,W-E-F) // ----- representations // isd2 searches for one of many representations of the E+F errors // representation: xor(first PIJ,second PIJ) on left; xor(first PIJ,second PIJ) on right // on left: binomial(E,E/2) ways to split E into (first part,second part) // and also need PIJ-E/2 further bits shared between first PIJ,second PIJ // overall binomial(left-E,PIJ-E/2)*binomial(E,E/2) left decompositions representations[EF] = binomial(left-E,PIJ-E/2)*binomial(right-F,PIJ-F/2)*binomial(E,E/2)*binomial(F,F/2); } } // ----- restricted differences // isd2 restricts to D choices of differences Delta, with 1<=D<=2^L0 // and finds a representation only if it is suitable // meaning first PIJ on left and first PIJ on right match Delta in L0 bits // and (equivalently) second PIJ on left and second PIJ on right match Delta+syndrome in L0 bits bigfloat halfL0 = exp2(bigfloat(-L0)); bigfloat reprfound = bigfloat(D)*halfL0; // ----- window limit and queue limit for first merge and second merge bigfloat B = bigfloat(listsize0*listsize1)*halfL0; // expect B = listsize0*listsize1/2^L0 collisions bigfloat C = collision_average(listsize0,listsize1,(bigint) WI0,halfL0); // expect C collisions to survive window limit bigfloat q = C/bigfloat(pool); bigfloat full_queue_clears = bigfloat(pool/PE0); bigfloat D = full_queue_clears*queue_average(PE0,q,QU0)+queue_average(leftovers,q,QU0); // expect D collisions to survive queue-length limit if (!D.definitely_positive()) continue; bigfloat DB = D/B; // chance that a collision survives window limit and queue-length limit reprfound *= DB*DB; // have to survive two merges if (D.definitely_less(1)) { ; // model 1 collision as definitely being found } else { // ----- valid portion of the root list // nominal root list size is 2*queue_clears*QU0 // but expect only first 2*D entries to be valid bigint effectiveWI1 = WI1; while (effectiveWI1 > 0 && (2*D).definitely_less(bigfloat(effectiveWI1))) --effectiveWI1; bigfloat validrootpool = (4*D-bigfloat(effectiveWI1)-1)*bigfloat(effectiveWI1)/2; bigfloat validrootfullqueueclears = bigfloat(floor_as_bigint(validrootpool/bigfloat(PE1))); bigfloat validrootleftovers = validrootpool-validrootfullqueueclears*bigfloat(PE1); // ----- window limit and queue limit for root merge bigfloat halfL1 = exp2(bigfloat(-L1)); bigfloat rootB = D*D*halfL1; // expect this many root collisions bigfloat rootC = collision_average(D,D,(bigint) effectiveWI1,halfL1); bigfloat rootq = rootC/validrootpool; bigfloat rootD = validrootfullqueueclears*queue_average(PE1,rootq,QU1)+queue_average(validrootleftovers,rootq,QU1); if (!rootB.definitely_positive()) continue; if (!rootD.definitely_positive()) continue; reprfound *= rootD/rootB; } // ----- putting everything together vector<bool> prsuccessnonzero(4*PIJ+1,0); vector<bigfloat> prsuccess(4*PIJ+1,0); for (bigint E = 0;E <= 2*PIJ;E += 2) { if (CHECKPI) if (E != PI) continue; for (bigint F = 0;F <= 2*PIJ;F += 2) { if (CHECKPI) if (F != PI) continue; pair<bigint,bigint> EF = make_pair(E,F); bigfloat pr = -expm1(bigfloat(representations[EF])*log1p(-reprfound)); pr *= prsplit[EF]; // pr is now the conditional probability // of E,F,0 errors in left,right,Z with suitable representation // surviving window limit and queue limit for first merge, second merge, root merge // given E+F errors in K+L prsuccess.at(E+F) += pr; prsuccessnonzero.at(E+F) = 1; } } // ----- markov chain vector<vector<bigfloat>> T0 = transition_weightstep(N,K+L,W,X); vector<vector<bigfloat>> T1 = transition_checksystematic(W,X,Y); vector<vector<bigfloat>> T2 = transition_identity(W); for (bigint EplusF = 0;EplusF <= 4*PIJ;EplusF += 2) if (prsuccessnonzero.at(EplusF)) T2 = transition_multiply(W,T2,transition_found(W,EplusF,prsuccess.at(EplusF))); vector<vector<bigfloat>> Titer = transition_multiply(W,T0,transition_multiply(W,T1,T2)); vector<vector<bigfloat>> Tmanyiters = transition_power(W,Titer,RESET-1); Tmanyiters = transition_multiply(W,T2,Tmanyiters); bigfloat result = 0; for (bigint u = 0;u <= W;++u) { bigint prstartnumer = binomial(K+L,u)*binomial(N-K-L,W-u); bigint prstartdenom = binomial(N,W); bigfloat prstart = bigfloat(prstartnumer)/bigfloat(prstartdenom); result += prstart*Tmanyiters.at(u).at(W+1); } result = -expm1(bigfloat(ITERS/RESET)*log1p(-result)); if (FW) result *= bigfloat(1)-exp2(bigfloat(-K_orig)); if (result.definitely_positive_and_reasonably_precise()) return result; } }