def swap_halves(self, input=True, output=True): if input: fx = lambda x: Bin(x, self.input_size()).swap_halves().int else: fx = lambda x: x if output: fy = lambda y: Bin(y, self.output_size()).swap_halves().int else: fy = lambda y: y return self.transform_graph(lambda x, y: (fx(x), fy(y)))
def int_tuple_list_vector(v): if isinstance(v, int): return v if isinstance(v, Integer): return int(v) if isinstance(v, tuple): return Bin(v).int if isinstance(v, list): return Bin(v).int if is_Vector(v): return Bin(tuple(v)).int raise TypeError("%r is not tuple, list, vector or integer")
def matrix_mult_int(mat, x): """ MSB to LSB vector >>> matrix_mult_int( \ matrix(GF(2), [[1, 0, 1], [1, 0, 0]]), \ 0b110) # read as 6 -> 1,1,0 -> 1,1 -> 3 3 """ assert mat.base_ring() == GF(2) n = mat.ncols() x = vector(GF(2), Bin(x, n).tuple) y = mat * x return Bin(y).int
def matrix_mult_int_rev(mat, x): """ LSB to MSB vector >>> matrix_mult_int_rev( \ matrix(GF(2), [[1, 0, 1], [1, 0, 0]]), \ 0b110) # read as 6 -> 0,1,1 -> 1,0 -> 1 1 """ assert mat.base_ring() == GF(2) n = mat.ncols() x = vector(GF(2), Bin(x, n).tuple[::-1]) y = mat * x return Bin(y[::-1]).int
def are_CCZ_equivalent(s1, s2): s1, s2 = convert_sboxes(s1, s2) assert_equal_sizes(s1, s2) lin1 = s1.is_linear() lin2 = s2.is_linear() if lin1 ^ lin2: return False if lin1 and lin2: return True inp, out = s1.n, s1.m M1 = matrix(GF(2), 1 + inp + out, 2**inp) M2 = matrix(GF(2), 1 + inp + out, 2**inp) for x in range(2**inp): M1.set_column( x, Bin.concat(Bin(1, 1) + Bin(x, inp) + Bin(s1[x], out)).tuple) M2.set_column( x, Bin.concat(Bin(1, 1) + Bin(x, inp) + Bin(s2[x], out)).tuple) L1 = LinearCode(M1) L2 = LinearCode(M2) # Annoying: this is not checked in "is_permutation_equivalent" code! # raises a TypeError instead if L1.generator_matrix().nrows() != L2.generator_matrix().nrows(): return False return L1.is_permutation_equivalent(L2)
def propagate_single_permutation(self, pos): outs_by_consts = [defaultdict(dict) for _ in range(self.num_out)] for x, y in self.s.graph(): xs = Bin(x, self.num_in * self.width_in).split(parts=self.num_in) ys = Bin(y, self.num_out * self.width_out).split(parts=self.num_out) key = tuple(xs[i] for i in range(self.num_in) if i != pos) for j, y in enumerate(ys): d = outs_by_consts[j][key] d.setdefault(y, 0) d[y] += 1 out_prop = [] for j in range(self.num_out): has_const = False has_perm = False has_balanced = False has_unknown = False for key, d in outs_by_consts[j].items(): if self.width_in == self.width_out and\ len(d) == 2**self.width_in: has_perm = True continue if len(d) == 1: has_const = True continue xorsum = 0 for y, cnt in d.items(): if cnt & 1: xorsum ^= y if xorsum == 0: has_balanced = True else: has_unknown = True break if has_unknown: result = UNKNOWN elif has_balanced: result = BALANCED elif has_const and not has_perm: result = CONST elif has_perm and not has_const: result = PERM else: assert False out_prop.append(result) return "".join(out_prop)
def test_SECCON_2020_sharsable(): from bint import Bin # SECCON 2020 - sharsable n = 142793817321992828777925840162504083304079023834001118099549928854335392622287928254035247188624975743042449746066633491912316354241339908190889792327014012472372654378644158878787350693992259970146885854641856991605625756536504266728483088687985429310233421251081614258665472164668993082471923690196082829593 # noqa e1, c1 = [ 82815162880874815458042429141267540989513396527359063805652845923737062346339641683097075730151688566721221542188377672708478777831586255213972947470222613130635483227797717393291856129771004300757155687587305350059401683671715424063527610425941387424425367153041852997937972925839362190900175155479532582934, # noqa 108072697038795075732704334514926058617161875495016327352871122917196026504758904760148391499245235850616838765611460630089577948665981247735905622903872682862860306107704253287284051312867625831877418240290183661755993649928399992531008191618616452091127799880839665225093055618092869662205901927957599941568, # noqa ] e2, c2 = [ 84856171747859965508406237198459622554468224770252249975158471902036102010991476445962577679301719179079633469099994226630172251817358960347828156301869905575867853640850107406452911333646573296923235424617864473580743418995994067645338437540627399276292679100115018844287273293945121023787594592185295794983, # noqa 101960082023987498941061751761131381167414505957511290567652602520714324823481487410890478130601013005035303795327512367595187718926017321227779179404306882163521882309833982882201152721855538832465833869251505131262098978117904455226014402089126682222497271578420753565370375178303927777655414023662528363360, # noqa ] m = Lattice([ [e1, 1, 0], [e2, 0, 1], [n, 0, 0], ]) m.set_bounds([n**0.66, n**0.16, n**0.16], precision=1) d1, d2 = m.LLL().apply_map(abs)[0][1:] msg = int(pow(c1, int(d1), n) * pow(c2, int(d2), n) % n) assert Bin(msg).bytes == b'SECCON{sh4r4bl3_r54_1s_useful?}' assert d1, d2 == m.BKZ().apply_map(abs)[0][1:]
def as_matrix(self): assert self.is_linear() m = matrix(GF(2), self.output_size(), self.input_size()) for e in range(self.input_size()): vec = Bin(self[2**e], self.output_size()).tuple m.set_column(self.input_size() - 1 - e, vec) return m
def mat_field_mul_const(field, c): assert field.base_ring() == GF(2) d = field.degree() m = matrix(GF(2), d, d) for i, e in enumerate(reversed(range(d))): x = 1 << e res = field.fetch_int(x) * field.fetch_int(c) res = res.integer_representation() m.set_column(i, Bin(res, d).tuple) return m
def hdim(self, right_to_left=False): """ hdim[i,j] = i-th output bit contains monomial x1...xn/xj """ res = matrix(GF(2), self.in_bits, self.out_bits) anf = mobius(tuple(self)) for j in range(self.in_bits): mask = (1 << self.in_bits) - 1 mask ^= 1 << (self.in_bits - 1 - j) res.set_column(j, Bin(anf[mask], self.out_bits).tuple) if right_to_left: res = res[::-1, ::-1] return res
def anfs(self): names = ["x%d" % e for e in range(self.input_size())] bpr = BooleanPolynomialRing(names=names) vs = list((bpr.gens())) res = [] for f in self.mobius().coordinates(): anf = bpr(0) for mask, take in f.graph(): if not take: continue clause = bpr(1) for b, v in zip(Bin(mask, self.input_size()).tuple, vs): if b: clause *= v anf += clause res.append(anf) return res
def test_SECCON_2020_crypto01(): masked_flag = 8077275413285507538357977814283814136815067070436948406142717872478737670143034266458000767181162602217137657160614964831459653429727469965712631294123225 ciphertexts = [ ( 886775008733978973740257525650074677865489026053222554158847150065839924739525729402395428639350660027796013999414358689067171124475540042281892825140355436902197270317502862419355737833115120643020255177020178331283541002427279377648285229476212651491301857891044943211950307475969543789857568915505152189, 428559018841948586040450870835405618958247103990365896475476359721456520823105336402250177610460462882418373365551361955769011150860740050608377302711322189733933012507453380208960247649622047461113987220437673085, (80103920082434941308951713928956232093682985133090231319482222058601362901404235742975739345738195056858602864819514638254604213654261535503537998613664606957411824998071339098079622119781772477940471338367084695408560473046701072890754255641388442248683562485936267601216704036749070688609079527182189924, 842529568002033827493313169405480104392127700904794758022297608679141466431472390397211660887148306350328312067659313538964875094877785244888004725864621826211254824064492961341512012172365417791553455922872091302295614295785447354268009914265614957287377743897340475899980395298019735631787570231395791009 ), 59051335744243522933765175665, ), ( 37244493713246153778174562251362609960152354778990433088693945121840521598316370898923829767722653817418453309557995775960963654893571162621888675965423771020678918714406461976659339420718804301006282789714856197345257634581330970033427061846386874027391337895557558939538516456076874074642348992933600929747, 152657520846237173082171645969128953029734435220247551748055538422696193261367413610113664730705318574898280226247526051526753570012356026049784218573767765351341949785570284026342156807244268439356037529507501666987, (14301224815357080657295611483943004076662022528706782110580690613822599700117720290144067866898573981166927919045773324162651193822888938569692341428439887892150361361566562459037438581637126808773605536449091609307652818862273375400935935851849153633881318435727224452416837785155763820052159016539063161774, 711567521767597846126014317418558888899966530302382481896965868976010995445213619798358362850235642988939870722858624888544954189591439381230859036120045216842190726357421800117080884618285875510251442474167884705409694074544449959388473647082485564659040849556130494057742499029963072560315800049629531101 ), 56178670950277431873900982569, ), ( 6331516792334912993168705942035497262087604457828016897033541606942408964421467661323530702416001851055818803331278149668787143629857676822265398153269496232656278975610802921303671791426728632525217213666490425139054750899596417296214549901614709644307007461708968510489409278966392105040423902797155302293, 2041454339352622193656627164408774503102680941657965640324880658919242765901541504713444825283928928662755298871656269360429687747026596290805541345773049732439830112665647963627438433525577186905830365395002284129, (4957181230366693742871089608567285130576943723414681430592899115510633732100117146460557849481224254840925101767808247789524040371495334272723624632991086495766920694854539353934108291010560628236400352109307607758762704896162926316651462302829906095279281186440520100069819712951163378589378112311816255944, 2715356151156550887523953822376791368905549782137733960800063674240100539578667744855739741955125966795181973779300950371154326834354436541128751075733334790425302253483346802547763927140263874521376312837536602237535819978061120675338002761853910905172273772708894687214799261210445915799607932569795429868 ), 70953285682097151767648136059, ), ] HINTSIZE = 96 def decrypt(c, d, n): n = int(n) size = n.bit_length() // 2 c_high, c_low = c b = (c_low**2 - c_high**3) % n EC = EllipticCurve(Zmod(n), [0, b]) m_high, m_low = (EC((c_high, c_low)) * d).xy() m_high, m_low = int(m_high), int(m_low) return (m_high << size) | m_low hintmod = 2**HINTSIZE todec = masked_flag for data in ciphertexts: n, e, c, hint = data c_high, c_low = c for f in (e / QQ(n)).continued_fraction().convergents(): y = f.numerator() x = f.denominator() if is_prime(x) and is_prime(y): print("good", x, y) break plo = hint qlo = n * inverse_mod(plo, hintmod) % hintmod slo = (plo + qlo) % hintmod assert plo * qlo % hintmod == n % hintmod a = e * x - (n + 1) * y z_mod_y = (-a) % y z_mod_hintmod = (-a + slo * y) % hintmod midmod = hintmod * y zmid = crt([z_mod_y, z_mod_hintmod], [y, hintmod]) aa = a - slo * y + zmid assert aa % midmod == 0 aa //= midmod # shi = (p + q) // hintmod # zhi is ~216 bits # assert shi == aa + zhi sumpq = aa * hintmod eps = 2**216 * hintmod rsa = RSA(n) pbound, qbound = rsa.bound_primes_from_approximate_sum( sumpq - eps, 2 * eps) try: print("factor1") p, q = rsa.factor_with_prime_hint(low_mod=(plo, hintmod), bounds=pbound, beta=0.49) except RuntimeError: print("factor2") p, q = rsa.factor_with_prime_hint(low_mod=(qlo, hintmod), bounds=pbound, beta=0.49) print("ok!") d = inverse_mod(e, n + p + q + 1) mask = decrypt(c, d, n) todec ^= mask assert Bin(todec).bytes == b'SECCON{gut_poweeeeeeeeeeeeeer}'
def mat_from_linear_func(m, n, func): mat = matrix(GF(2), n, m) for i, e in enumerate(reversed(range(m))): x = 1 << e mat.set_column(i, Bin(func(x), n).tuple) return mat
def squeeze_by_mask(self, mask): mask = int_tuple_list_vector(mask) assert mask in self.output_range() m = self.output_size() return type(self)([Bin(y, m).squeeze_by_mask(mask).int for y in self], m=Bin(mask).hw)
def __getitem__(self, lst): assert len(lst) == self.num_in x = Bin.concat(Bin.array(lst, n=self.width_in)) y = self.s[x] return Bin(y, self.width_out).split(parts=self.num_out)
def decrypt(self, msg): msg = Bin(msg).int msg = power_mod(msg, self.d, self.n) return Bin(msg).bytes
def component(self, mask): mask = int_tuple_list_vector(mask) tt = [Bin(y & mask).parity() for y in self] yield type(self)(tt, m=1)
def concat(funcs): assert len(set(f.input_size() for f in funcs)) == 1 sizes = tuple(f.output_size() for f in funcs) return SBox2([Bin.concat(*Bin.array(ys, ns=sizes)) for ys in zip(*funcs)], m=sum(sizes))