| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368 |
- """
- This file contains some classical ciphers and routines
- implementing a linear-feedback shift register (LFSR)
- and the Diffie-Hellman key exchange.
- .. warning::
- This module is intended for educational purposes only. Do not use the
- functions in this module for real cryptographic applications. If you wish
- to encrypt real data, we recommend using something like the `cryptography
- <https://cryptography.io/en/latest/>`_ module.
- """
- from string import whitespace, ascii_uppercase as uppercase, printable
- from functools import reduce
- import string
- import warnings
- from itertools import cycle
- from sympy.external.gmpy import GROUND_TYPES
- from sympy.core import Symbol
- from sympy.core.numbers import Rational
- from sympy.core.random import _randrange, _randint
- from sympy.external.gmpy import gcd, invert
- from sympy.functions.combinatorial.numbers import (totient as _euler,
- reduced_totient as _carmichael)
- from sympy.matrices import Matrix
- from sympy.ntheory import isprime, primitive_root, factorint
- from sympy.ntheory.generate import nextprime
- from sympy.ntheory.modular import crt
- from sympy.polys.domains import FF
- from sympy.polys.polytools import Poly
- from sympy.utilities.misc import as_int, filldedent, translate
- from sympy.utilities.iterables import uniq, multiset
- from sympy.utilities.decorator import doctest_depends_on
- if GROUND_TYPES == 'flint':
- __doctest_skip__ = ['lfsr_sequence']
- class NonInvertibleCipherWarning(RuntimeWarning):
- """A warning raised if the cipher is not invertible."""
- def __init__(self, msg):
- self.fullMessage = msg
- def __str__(self):
- return '\n\t' + self.fullMessage
- def warn(self, stacklevel=3):
- warnings.warn(self, stacklevel=stacklevel)
- def AZ(s=None):
- """Return the letters of ``s`` in uppercase. In case more than
- one string is passed, each of them will be processed and a list
- of upper case strings will be returned.
- Examples
- ========
- >>> from sympy.crypto.crypto import AZ
- >>> AZ('Hello, world!')
- 'HELLOWORLD'
- >>> AZ('Hello, world!'.split())
- ['HELLO', 'WORLD']
- See Also
- ========
- check_and_join
- """
- if not s:
- return uppercase
- t = isinstance(s, str)
- if t:
- s = [s]
- rv = [check_and_join(i.upper().split(), uppercase, filter=True)
- for i in s]
- if t:
- return rv[0]
- return rv
- bifid5 = AZ().replace('J', '')
- bifid6 = AZ() + string.digits
- bifid10 = printable
- def padded_key(key, symbols):
- """Return a string of the distinct characters of ``symbols`` with
- those of ``key`` appearing first. A ValueError is raised if
- a) there are duplicate characters in ``symbols`` or
- b) there are characters in ``key`` that are not in ``symbols``.
- Examples
- ========
- >>> from sympy.crypto.crypto import padded_key
- >>> padded_key('PUPPY', 'OPQRSTUVWXY')
- 'PUYOQRSTVWX'
- >>> padded_key('RSA', 'ARTIST')
- Traceback (most recent call last):
- ...
- ValueError: duplicate characters in symbols: T
- """
- syms = list(uniq(symbols))
- if len(syms) != len(symbols):
- extra = ''.join(sorted({
- i for i in symbols if symbols.count(i) > 1}))
- raise ValueError('duplicate characters in symbols: %s' % extra)
- extra = set(key) - set(syms)
- if extra:
- raise ValueError(
- 'characters in key but not symbols: %s' % ''.join(
- sorted(extra)))
- key0 = ''.join(list(uniq(key)))
- # remove from syms characters in key0
- return key0 + translate(''.join(syms), None, key0)
- def check_and_join(phrase, symbols=None, filter=None):
- """
- Joins characters of ``phrase`` and if ``symbols`` is given, raises
- an error if any character in ``phrase`` is not in ``symbols``.
- Parameters
- ==========
- phrase
- String or list of strings to be returned as a string.
- symbols
- Iterable of characters allowed in ``phrase``.
- If ``symbols`` is ``None``, no checking is performed.
- Examples
- ========
- >>> from sympy.crypto.crypto import check_and_join
- >>> check_and_join('a phrase')
- 'a phrase'
- >>> check_and_join('a phrase'.upper().split())
- 'APHRASE'
- >>> check_and_join('a phrase!'.upper().split(), 'ARE', filter=True)
- 'ARAE'
- >>> check_and_join('a phrase!'.upper().split(), 'ARE')
- Traceback (most recent call last):
- ...
- ValueError: characters in phrase but not symbols: "!HPS"
- """
- rv = ''.join(''.join(phrase))
- if symbols is not None:
- symbols = check_and_join(symbols)
- missing = ''.join(sorted(set(rv) - set(symbols)))
- if missing:
- if not filter:
- raise ValueError(
- 'characters in phrase but not symbols: "%s"' % missing)
- rv = translate(rv, None, missing)
- return rv
- def _prep(msg, key, alp, default=None):
- if not alp:
- if not default:
- alp = AZ()
- msg = AZ(msg)
- key = AZ(key)
- else:
- alp = default
- else:
- alp = ''.join(alp)
- key = check_and_join(key, alp, filter=True)
- msg = check_and_join(msg, alp, filter=True)
- return msg, key, alp
- def cycle_list(k, n):
- """
- Returns the elements of the list ``range(n)`` shifted to the
- left by ``k`` (so the list starts with ``k`` (mod ``n``)).
- Examples
- ========
- >>> from sympy.crypto.crypto import cycle_list
- >>> cycle_list(3, 10)
- [3, 4, 5, 6, 7, 8, 9, 0, 1, 2]
- """
- k = k % n
- return list(range(k, n)) + list(range(k))
- ######## shift cipher examples ############
- def encipher_shift(msg, key, symbols=None):
- """
- Performs shift cipher encryption on plaintext msg, and returns the
- ciphertext.
- Parameters
- ==========
- key : int
- The secret key.
- msg : str
- Plaintext of upper-case letters.
- Returns
- =======
- str
- Ciphertext of upper-case letters.
- Examples
- ========
- >>> from sympy.crypto.crypto import encipher_shift, decipher_shift
- >>> msg = "GONAVYBEATARMY"
- >>> ct = encipher_shift(msg, 1); ct
- 'HPOBWZCFBUBSNZ'
- To decipher the shifted text, change the sign of the key:
- >>> encipher_shift(ct, -1)
- 'GONAVYBEATARMY'
- There is also a convenience function that does this with the
- original key:
- >>> decipher_shift(ct, 1)
- 'GONAVYBEATARMY'
- Notes
- =====
- ALGORITHM:
- STEPS:
- 0. Number the letters of the alphabet from 0, ..., N
- 1. Compute from the string ``msg`` a list ``L1`` of
- corresponding integers.
- 2. Compute from the list ``L1`` a new list ``L2``, given by
- adding ``(k mod 26)`` to each element in ``L1``.
- 3. Compute from the list ``L2`` a string ``ct`` of
- corresponding letters.
- The shift cipher is also called the Caesar cipher, after
- Julius Caesar, who, according to Suetonius, used it with a
- shift of three to protect messages of military significance.
- Caesar's nephew Augustus reportedly used a similar cipher, but
- with a right shift of 1.
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Caesar_cipher
- .. [2] https://mathworld.wolfram.com/CaesarsMethod.html
- See Also
- ========
- decipher_shift
- """
- msg, _, A = _prep(msg, '', symbols)
- shift = len(A) - key % len(A)
- key = A[shift:] + A[:shift]
- return translate(msg, key, A)
- def decipher_shift(msg, key, symbols=None):
- """
- Return the text by shifting the characters of ``msg`` to the
- left by the amount given by ``key``.
- Examples
- ========
- >>> from sympy.crypto.crypto import encipher_shift, decipher_shift
- >>> msg = "GONAVYBEATARMY"
- >>> ct = encipher_shift(msg, 1); ct
- 'HPOBWZCFBUBSNZ'
- To decipher the shifted text, change the sign of the key:
- >>> encipher_shift(ct, -1)
- 'GONAVYBEATARMY'
- Or use this function with the original key:
- >>> decipher_shift(ct, 1)
- 'GONAVYBEATARMY'
- """
- return encipher_shift(msg, -key, symbols)
- def encipher_rot13(msg, symbols=None):
- """
- Performs the ROT13 encryption on a given plaintext ``msg``.
- Explanation
- ===========
- ROT13 is a substitution cipher which substitutes each letter
- in the plaintext message for the letter furthest away from it
- in the English alphabet.
- Equivalently, it is just a Caeser (shift) cipher with a shift
- key of 13 (midway point of the alphabet).
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/ROT13
- See Also
- ========
- decipher_rot13
- encipher_shift
- """
- return encipher_shift(msg, 13, symbols)
- def decipher_rot13(msg, symbols=None):
- """
- Performs the ROT13 decryption on a given plaintext ``msg``.
- Explanation
- ============
- ``decipher_rot13`` is equivalent to ``encipher_rot13`` as both
- ``decipher_shift`` with a key of 13 and ``encipher_shift`` key with a
- key of 13 will return the same results. Nonetheless,
- ``decipher_rot13`` has nonetheless been explicitly defined here for
- consistency.
- Examples
- ========
- >>> from sympy.crypto.crypto import encipher_rot13, decipher_rot13
- >>> msg = 'GONAVYBEATARMY'
- >>> ciphertext = encipher_rot13(msg);ciphertext
- 'TBANILORNGNEZL'
- >>> decipher_rot13(ciphertext)
- 'GONAVYBEATARMY'
- >>> encipher_rot13(msg) == decipher_rot13(msg)
- True
- >>> msg == decipher_rot13(ciphertext)
- True
- """
- return decipher_shift(msg, 13, symbols)
- ######## affine cipher examples ############
- def encipher_affine(msg, key, symbols=None, _inverse=False):
- r"""
- Performs the affine cipher encryption on plaintext ``msg``, and
- returns the ciphertext.
- Explanation
- ===========
- Encryption is based on the map `x \rightarrow ax+b` (mod `N`)
- where ``N`` is the number of characters in the alphabet.
- Decryption is based on the map `x \rightarrow cx+d` (mod `N`),
- where `c = a^{-1}` (mod `N`) and `d = -a^{-1}b` (mod `N`).
- In particular, for the map to be invertible, we need
- `\mathrm{gcd}(a, N) = 1` and an error will be raised if this is
- not true.
- Parameters
- ==========
- msg : str
- Characters that appear in ``symbols``.
- a, b : int, int
- A pair integers, with ``gcd(a, N) = 1`` (the secret key).
- symbols
- String of characters (default = uppercase letters).
- When no symbols are given, ``msg`` is converted to upper case
- letters and all other characters are ignored.
- Returns
- =======
- ct
- String of characters (the ciphertext message)
- Notes
- =====
- ALGORITHM:
- STEPS:
- 0. Number the letters of the alphabet from 0, ..., N
- 1. Compute from the string ``msg`` a list ``L1`` of
- corresponding integers.
- 2. Compute from the list ``L1`` a new list ``L2``, given by
- replacing ``x`` by ``a*x + b (mod N)``, for each element
- ``x`` in ``L1``.
- 3. Compute from the list ``L2`` a string ``ct`` of
- corresponding letters.
- This is a straightforward generalization of the shift cipher with
- the added complexity of requiring 2 characters to be deciphered in
- order to recover the key.
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Affine_cipher
- See Also
- ========
- decipher_affine
- """
- msg, _, A = _prep(msg, '', symbols)
- N = len(A)
- a, b = key
- assert gcd(a, N) == 1
- if _inverse:
- c = invert(a, N)
- d = -b*c
- a, b = c, d
- B = ''.join([A[(a*i + b) % N] for i in range(N)])
- return translate(msg, A, B)
- def decipher_affine(msg, key, symbols=None):
- r"""
- Return the deciphered text that was made from the mapping,
- `x \rightarrow ax+b` (mod `N`), where ``N`` is the
- number of characters in the alphabet. Deciphering is done by
- reciphering with a new key: `x \rightarrow cx+d` (mod `N`),
- where `c = a^{-1}` (mod `N`) and `d = -a^{-1}b` (mod `N`).
- Examples
- ========
- >>> from sympy.crypto.crypto import encipher_affine, decipher_affine
- >>> msg = "GO NAVY BEAT ARMY"
- >>> key = (3, 1)
- >>> encipher_affine(msg, key)
- 'TROBMVENBGBALV'
- >>> decipher_affine(_, key)
- 'GONAVYBEATARMY'
- See Also
- ========
- encipher_affine
- """
- return encipher_affine(msg, key, symbols, _inverse=True)
- def encipher_atbash(msg, symbols=None):
- r"""
- Enciphers a given ``msg`` into its Atbash ciphertext and returns it.
- Explanation
- ===========
- Atbash is a substitution cipher originally used to encrypt the Hebrew
- alphabet. Atbash works on the principle of mapping each alphabet to its
- reverse / counterpart (i.e. a would map to z, b to y etc.)
- Atbash is functionally equivalent to the affine cipher with ``a = 25``
- and ``b = 25``
- See Also
- ========
- decipher_atbash
- """
- return encipher_affine(msg, (25, 25), symbols)
- def decipher_atbash(msg, symbols=None):
- r"""
- Deciphers a given ``msg`` using Atbash cipher and returns it.
- Explanation
- ===========
- ``decipher_atbash`` is functionally equivalent to ``encipher_atbash``.
- However, it has still been added as a separate function to maintain
- consistency.
- Examples
- ========
- >>> from sympy.crypto.crypto import encipher_atbash, decipher_atbash
- >>> msg = 'GONAVYBEATARMY'
- >>> encipher_atbash(msg)
- 'TLMZEBYVZGZINB'
- >>> decipher_atbash(msg)
- 'TLMZEBYVZGZINB'
- >>> encipher_atbash(msg) == decipher_atbash(msg)
- True
- >>> msg == encipher_atbash(encipher_atbash(msg))
- True
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Atbash
- See Also
- ========
- encipher_atbash
- """
- return decipher_affine(msg, (25, 25), symbols)
- #################### substitution cipher ###########################
- def encipher_substitution(msg, old, new=None):
- r"""
- Returns the ciphertext obtained by replacing each character that
- appears in ``old`` with the corresponding character in ``new``.
- If ``old`` is a mapping, then new is ignored and the replacements
- defined by ``old`` are used.
- Explanation
- ===========
- This is a more general than the affine cipher in that the key can
- only be recovered by determining the mapping for each symbol.
- Though in practice, once a few symbols are recognized the mappings
- for other characters can be quickly guessed.
- Examples
- ========
- >>> from sympy.crypto.crypto import encipher_substitution, AZ
- >>> old = 'OEYAG'
- >>> new = '034^6'
- >>> msg = AZ("go navy! beat army!")
- >>> ct = encipher_substitution(msg, old, new); ct
- '60N^V4B3^T^RM4'
- To decrypt a substitution, reverse the last two arguments:
- >>> encipher_substitution(ct, new, old)
- 'GONAVYBEATARMY'
- In the special case where ``old`` and ``new`` are a permutation of
- order 2 (representing a transposition of characters) their order
- is immaterial:
- >>> old = 'NAVY'
- >>> new = 'ANYV'
- >>> encipher = lambda x: encipher_substitution(x, old, new)
- >>> encipher('NAVY')
- 'ANYV'
- >>> encipher(_)
- 'NAVY'
- The substitution cipher, in general, is a method
- whereby "units" (not necessarily single characters) of plaintext
- are replaced with ciphertext according to a regular system.
- >>> ords = dict(zip('abc', ['\\%i' % ord(i) for i in 'abc']))
- >>> print(encipher_substitution('abc', ords))
- \97\98\99
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Substitution_cipher
- """
- return translate(msg, old, new)
- ######################################################################
- #################### Vigenere cipher examples ########################
- ######################################################################
- def encipher_vigenere(msg, key, symbols=None):
- """
- Performs the Vigenere cipher encryption on plaintext ``msg``, and
- returns the ciphertext.
- Examples
- ========
- >>> from sympy.crypto.crypto import encipher_vigenere, AZ
- >>> key = "encrypt"
- >>> msg = "meet me on monday"
- >>> encipher_vigenere(msg, key)
- 'QRGKKTHRZQEBPR'
- Section 1 of the Kryptos sculpture at the CIA headquarters
- uses this cipher and also changes the order of the
- alphabet [2]_. Here is the first line of that section of
- the sculpture:
- >>> from sympy.crypto.crypto import decipher_vigenere, padded_key
- >>> alp = padded_key('KRYPTOS', AZ())
- >>> key = 'PALIMPSEST'
- >>> msg = 'EMUFPHZLRFAXYUSDJKZLDKRNSHGNFIVJ'
- >>> decipher_vigenere(msg, key, alp)
- 'BETWEENSUBTLESHADINGANDTHEABSENC'
- Explanation
- ===========
- The Vigenere cipher is named after Blaise de Vigenere, a sixteenth
- century diplomat and cryptographer, by a historical accident.
- Vigenere actually invented a different and more complicated cipher.
- The so-called *Vigenere cipher* was actually invented
- by Giovan Batista Belaso in 1553.
- This cipher was used in the 1800's, for example, during the American
- Civil War. The Confederacy used a brass cipher disk to implement the
- Vigenere cipher (now on display in the NSA Museum in Fort
- Meade) [1]_.
- The Vigenere cipher is a generalization of the shift cipher.
- Whereas the shift cipher shifts each letter by the same amount
- (that amount being the key of the shift cipher) the Vigenere
- cipher shifts a letter by an amount determined by the key (which is
- a word or phrase known only to the sender and receiver).
- For example, if the key was a single letter, such as "C", then the
- so-called Vigenere cipher is actually a shift cipher with a
- shift of `2` (since "C" is the 2nd letter of the alphabet, if
- you start counting at `0`). If the key was a word with two
- letters, such as "CA", then the so-called Vigenere cipher will
- shift letters in even positions by `2` and letters in odd positions
- are left alone (shifted by `0`, since "A" is the 0th letter, if
- you start counting at `0`).
- ALGORITHM:
- INPUT:
- ``msg``: string of characters that appear in ``symbols``
- (the plaintext)
- ``key``: a string of characters that appear in ``symbols``
- (the secret key)
- ``symbols``: a string of letters defining the alphabet
- OUTPUT:
- ``ct``: string of characters (the ciphertext message)
- STEPS:
- 0. Number the letters of the alphabet from 0, ..., N
- 1. Compute from the string ``key`` a list ``L1`` of
- corresponding integers. Let ``n1 = len(L1)``.
- 2. Compute from the string ``msg`` a list ``L2`` of
- corresponding integers. Let ``n2 = len(L2)``.
- 3. Break ``L2`` up sequentially into sublists of size
- ``n1``; the last sublist may be smaller than ``n1``
- 4. For each of these sublists ``L`` of ``L2``, compute a
- new list ``C`` given by ``C[i] = L[i] + L1[i] (mod N)``
- to the ``i``-th element in the sublist, for each ``i``.
- 5. Assemble these lists ``C`` by concatenation into a new
- list of length ``n2``.
- 6. Compute from the new list a string ``ct`` of
- corresponding letters.
- Once it is known that the key is, say, `n` characters long,
- frequency analysis can be applied to every `n`-th letter of
- the ciphertext to determine the plaintext. This method is
- called *Kasiski examination* (although it was first discovered
- by Babbage). If they key is as long as the message and is
- comprised of randomly selected characters -- a one-time pad -- the
- message is theoretically unbreakable.
- The cipher Vigenere actually discovered is an "auto-key" cipher
- described as follows.
- ALGORITHM:
- INPUT:
- ``key``: a string of letters (the secret key)
- ``msg``: string of letters (the plaintext message)
- OUTPUT:
- ``ct``: string of upper-case letters (the ciphertext message)
- STEPS:
- 0. Number the letters of the alphabet from 0, ..., N
- 1. Compute from the string ``msg`` a list ``L2`` of
- corresponding integers. Let ``n2 = len(L2)``.
- 2. Let ``n1`` be the length of the key. Append to the
- string ``key`` the first ``n2 - n1`` characters of
- the plaintext message. Compute from this string (also of
- length ``n2``) a list ``L1`` of integers corresponding
- to the letter numbers in the first step.
- 3. Compute a new list ``C`` given by
- ``C[i] = L1[i] + L2[i] (mod N)``.
- 4. Compute from the new list a string ``ct`` of letters
- corresponding to the new integers.
- To decipher the auto-key ciphertext, the key is used to decipher
- the first ``n1`` characters and then those characters become the
- key to decipher the next ``n1`` characters, etc...:
- >>> m = AZ('go navy, beat army! yes you can'); m
- 'GONAVYBEATARMYYESYOUCAN'
- >>> key = AZ('gold bug'); n1 = len(key); n2 = len(m)
- >>> auto_key = key + m[:n2 - n1]; auto_key
- 'GOLDBUGGONAVYBEATARMYYE'
- >>> ct = encipher_vigenere(m, auto_key); ct
- 'MCYDWSHKOGAMKZCELYFGAYR'
- >>> n1 = len(key)
- >>> pt = []
- >>> while ct:
- ... part, ct = ct[:n1], ct[n1:]
- ... pt.append(decipher_vigenere(part, key))
- ... key = pt[-1]
- ...
- >>> ''.join(pt) == m
- True
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Vigenere_cipher
- .. [2] https://web.archive.org/web/20071116100808/https://filebox.vt.edu/users/batman/kryptos.html
- (short URL: https://goo.gl/ijr22d)
- """
- msg, key, A = _prep(msg, key, symbols)
- map = {c: i for i, c in enumerate(A)}
- key = [map[c] for c in key]
- N = len(map)
- k = len(key)
- rv = []
- for i, m in enumerate(msg):
- rv.append(A[(map[m] + key[i % k]) % N])
- rv = ''.join(rv)
- return rv
- def decipher_vigenere(msg, key, symbols=None):
- """
- Decode using the Vigenere cipher.
- Examples
- ========
- >>> from sympy.crypto.crypto import decipher_vigenere
- >>> key = "encrypt"
- >>> ct = "QRGK kt HRZQE BPR"
- >>> decipher_vigenere(ct, key)
- 'MEETMEONMONDAY'
- """
- msg, key, A = _prep(msg, key, symbols)
- map = {c: i for i, c in enumerate(A)}
- N = len(A) # normally, 26
- K = [map[c] for c in key]
- n = len(K)
- C = [map[c] for c in msg]
- rv = ''.join([A[(-K[i % n] + c) % N] for i, c in enumerate(C)])
- return rv
- #################### Hill cipher ########################
- def encipher_hill(msg, key, symbols=None, pad="Q"):
- r"""
- Return the Hill cipher encryption of ``msg``.
- Explanation
- ===========
- The Hill cipher [1]_, invented by Lester S. Hill in the 1920's [2]_,
- was the first polygraphic cipher in which it was practical
- (though barely) to operate on more than three symbols at once.
- The following discussion assumes an elementary knowledge of
- matrices.
- First, each letter is first encoded as a number starting with 0.
- Suppose your message `msg` consists of `n` capital letters, with no
- spaces. This may be regarded an `n`-tuple M of elements of
- `Z_{26}` (if the letters are those of the English alphabet). A key
- in the Hill cipher is a `k x k` matrix `K`, all of whose entries
- are in `Z_{26}`, such that the matrix `K` is invertible (i.e., the
- linear transformation `K: Z_{N}^k \rightarrow Z_{N}^k`
- is one-to-one).
- Parameters
- ==========
- msg
- Plaintext message of `n` upper-case letters.
- key
- A `k \times k` invertible matrix `K`, all of whose entries are
- in `Z_{26}` (or whatever number of symbols are being used).
- pad
- Character (default "Q") to use to make length of text be a
- multiple of ``k``.
- Returns
- =======
- ct
- Ciphertext of upper-case letters.
- Notes
- =====
- ALGORITHM:
- STEPS:
- 0. Number the letters of the alphabet from 0, ..., N
- 1. Compute from the string ``msg`` a list ``L`` of
- corresponding integers. Let ``n = len(L)``.
- 2. Break the list ``L`` up into ``t = ceiling(n/k)``
- sublists ``L_1``, ..., ``L_t`` of size ``k`` (with
- the last list "padded" to ensure its size is
- ``k``).
- 3. Compute new list ``C_1``, ..., ``C_t`` given by
- ``C[i] = K*L_i`` (arithmetic is done mod N), for each
- ``i``.
- 4. Concatenate these into a list ``C = C_1 + ... + C_t``.
- 5. Compute from ``C`` a string ``ct`` of corresponding
- letters. This has length ``k*t``.
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Hill_cipher
- .. [2] Lester S. Hill, Cryptography in an Algebraic Alphabet,
- The American Mathematical Monthly Vol.36, June-July 1929,
- pp.306-312.
- See Also
- ========
- decipher_hill
- """
- assert key.is_square
- assert len(pad) == 1
- msg, pad, A = _prep(msg, pad, symbols)
- map = {c: i for i, c in enumerate(A)}
- P = [map[c] for c in msg]
- N = len(A)
- k = key.cols
- n = len(P)
- m, r = divmod(n, k)
- if r:
- P = P + [map[pad]]*(k - r)
- m += 1
- rv = ''.join([A[c % N] for j in range(m) for c in
- list(key*Matrix(k, 1, [P[i]
- for i in range(k*j, k*(j + 1))]))])
- return rv
- def decipher_hill(msg, key, symbols=None):
- """
- Deciphering is the same as enciphering but using the inverse of the
- key matrix.
- Examples
- ========
- >>> from sympy.crypto.crypto import encipher_hill, decipher_hill
- >>> from sympy import Matrix
- >>> key = Matrix([[1, 2], [3, 5]])
- >>> encipher_hill("meet me on monday", key)
- 'UEQDUEODOCTCWQ'
- >>> decipher_hill(_, key)
- 'MEETMEONMONDAY'
- When the length of the plaintext (stripped of invalid characters)
- is not a multiple of the key dimension, extra characters will
- appear at the end of the enciphered and deciphered text. In order to
- decipher the text, those characters must be included in the text to
- be deciphered. In the following, the key has a dimension of 4 but
- the text is 2 short of being a multiple of 4 so two characters will
- be added.
- >>> key = Matrix([[1, 1, 1, 2], [0, 1, 1, 0],
- ... [2, 2, 3, 4], [1, 1, 0, 1]])
- >>> msg = "ST"
- >>> encipher_hill(msg, key)
- 'HJEB'
- >>> decipher_hill(_, key)
- 'STQQ'
- >>> encipher_hill(msg, key, pad="Z")
- 'ISPK'
- >>> decipher_hill(_, key)
- 'STZZ'
- If the last two characters of the ciphertext were ignored in
- either case, the wrong plaintext would be recovered:
- >>> decipher_hill("HD", key)
- 'ORMV'
- >>> decipher_hill("IS", key)
- 'UIKY'
- See Also
- ========
- encipher_hill
- """
- assert key.is_square
- msg, _, A = _prep(msg, '', symbols)
- map = {c: i for i, c in enumerate(A)}
- C = [map[c] for c in msg]
- N = len(A)
- k = key.cols
- n = len(C)
- m, r = divmod(n, k)
- if r:
- C = C + [0]*(k - r)
- m += 1
- key_inv = key.inv_mod(N)
- rv = ''.join([A[p % N] for j in range(m) for p in
- list(key_inv*Matrix(
- k, 1, [C[i] for i in range(k*j, k*(j + 1))]))])
- return rv
- #################### Bifid cipher ########################
- def encipher_bifid(msg, key, symbols=None):
- r"""
- Performs the Bifid cipher encryption on plaintext ``msg``, and
- returns the ciphertext.
- This is the version of the Bifid cipher that uses an `n \times n`
- Polybius square.
- Parameters
- ==========
- msg
- Plaintext string.
- key
- Short string for key.
- Duplicate characters are ignored and then it is padded with the
- characters in ``symbols`` that were not in the short key.
- symbols
- `n \times n` characters defining the alphabet.
- (default is string.printable)
- Returns
- =======
- ciphertext
- Ciphertext using Bifid5 cipher without spaces.
- See Also
- ========
- decipher_bifid, encipher_bifid5, encipher_bifid6
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Bifid_cipher
- """
- msg, key, A = _prep(msg, key, symbols, bifid10)
- long_key = ''.join(uniq(key)) or A
- n = len(A)**.5
- if n != int(n):
- raise ValueError(
- 'Length of alphabet (%s) is not a square number.' % len(A))
- N = int(n)
- if len(long_key) < N**2:
- long_key = list(long_key) + [x for x in A if x not in long_key]
- # the fractionalization
- row_col = {ch: divmod(i, N) for i, ch in enumerate(long_key)}
- r, c = zip(*[row_col[x] for x in msg])
- rc = r + c
- ch = {i: ch for ch, i in row_col.items()}
- rv = ''.join(ch[i] for i in zip(rc[::2], rc[1::2]))
- return rv
- def decipher_bifid(msg, key, symbols=None):
- r"""
- Performs the Bifid cipher decryption on ciphertext ``msg``, and
- returns the plaintext.
- This is the version of the Bifid cipher that uses the `n \times n`
- Polybius square.
- Parameters
- ==========
- msg
- Ciphertext string.
- key
- Short string for key.
- Duplicate characters are ignored and then it is padded with the
- characters in symbols that were not in the short key.
- symbols
- `n \times n` characters defining the alphabet.
- (default=string.printable, a `10 \times 10` matrix)
- Returns
- =======
- deciphered
- Deciphered text.
- Examples
- ========
- >>> from sympy.crypto.crypto import (
- ... encipher_bifid, decipher_bifid, AZ)
- Do an encryption using the bifid5 alphabet:
- >>> alp = AZ().replace('J', '')
- >>> ct = AZ("meet me on monday!")
- >>> key = AZ("gold bug")
- >>> encipher_bifid(ct, key, alp)
- 'IEILHHFSTSFQYE'
- When entering the text or ciphertext, spaces are ignored so it
- can be formatted as desired. Re-entering the ciphertext from the
- preceding, putting 4 characters per line and padding with an extra
- J, does not cause problems for the deciphering:
- >>> decipher_bifid('''
- ... IEILH
- ... HFSTS
- ... FQYEJ''', key, alp)
- 'MEETMEONMONDAY'
- When no alphabet is given, all 100 printable characters will be
- used:
- >>> key = ''
- >>> encipher_bifid('hello world!', key)
- 'bmtwmg-bIo*w'
- >>> decipher_bifid(_, key)
- 'hello world!'
- If the key is changed, a different encryption is obtained:
- >>> key = 'gold bug'
- >>> encipher_bifid('hello world!', 'gold_bug')
- 'hg2sfuei7t}w'
- And if the key used to decrypt the message is not exact, the
- original text will not be perfectly obtained:
- >>> decipher_bifid(_, 'gold pug')
- 'heldo~wor6d!'
- """
- msg, _, A = _prep(msg, '', symbols, bifid10)
- long_key = ''.join(uniq(key)) or A
- n = len(A)**.5
- if n != int(n):
- raise ValueError(
- 'Length of alphabet (%s) is not a square number.' % len(A))
- N = int(n)
- if len(long_key) < N**2:
- long_key = list(long_key) + [x for x in A if x not in long_key]
- # the reverse fractionalization
- row_col = {
- ch: divmod(i, N) for i, ch in enumerate(long_key)}
- rc = [i for c in msg for i in row_col[c]]
- n = len(msg)
- rc = zip(*(rc[:n], rc[n:]))
- ch = {i: ch for ch, i in row_col.items()}
- rv = ''.join(ch[i] for i in rc)
- return rv
- def bifid_square(key):
- """Return characters of ``key`` arranged in a square.
- Examples
- ========
- >>> from sympy.crypto.crypto import (
- ... bifid_square, AZ, padded_key, bifid5)
- >>> bifid_square(AZ().replace('J', ''))
- Matrix([
- [A, B, C, D, E],
- [F, G, H, I, K],
- [L, M, N, O, P],
- [Q, R, S, T, U],
- [V, W, X, Y, Z]])
- >>> bifid_square(padded_key(AZ('gold bug!'), bifid5))
- Matrix([
- [G, O, L, D, B],
- [U, A, C, E, F],
- [H, I, K, M, N],
- [P, Q, R, S, T],
- [V, W, X, Y, Z]])
- See Also
- ========
- padded_key
- """
- A = ''.join(uniq(''.join(key)))
- n = len(A)**.5
- if n != int(n):
- raise ValueError(
- 'Length of alphabet (%s) is not a square number.' % len(A))
- n = int(n)
- f = lambda i, j: Symbol(A[n*i + j])
- rv = Matrix(n, n, f)
- return rv
- def encipher_bifid5(msg, key):
- r"""
- Performs the Bifid cipher encryption on plaintext ``msg``, and
- returns the ciphertext.
- Explanation
- ===========
- This is the version of the Bifid cipher that uses the `5 \times 5`
- Polybius square. The letter "J" is ignored so it must be replaced
- with something else (traditionally an "I") before encryption.
- ALGORITHM: (5x5 case)
- STEPS:
- 0. Create the `5 \times 5` Polybius square ``S`` associated
- to ``key`` as follows:
- a) moving from left-to-right, top-to-bottom,
- place the letters of the key into a `5 \times 5`
- matrix,
- b) if the key has less than 25 letters, add the
- letters of the alphabet not in the key until the
- `5 \times 5` square is filled.
- 1. Create a list ``P`` of pairs of numbers which are the
- coordinates in the Polybius square of the letters in
- ``msg``.
- 2. Let ``L1`` be the list of all first coordinates of ``P``
- (length of ``L1 = n``), let ``L2`` be the list of all
- second coordinates of ``P`` (so the length of ``L2``
- is also ``n``).
- 3. Let ``L`` be the concatenation of ``L1`` and ``L2``
- (length ``L = 2*n``), except that consecutive numbers
- are paired ``(L[2*i], L[2*i + 1])``. You can regard
- ``L`` as a list of pairs of length ``n``.
- 4. Let ``C`` be the list of all letters which are of the
- form ``S[i, j]``, for all ``(i, j)`` in ``L``. As a
- string, this is the ciphertext of ``msg``.
- Parameters
- ==========
- msg : str
- Plaintext string.
- Converted to upper case and filtered of anything but all letters
- except J.
- key
- Short string for key; non-alphabetic letters, J and duplicated
- characters are ignored and then, if the length is less than 25
- characters, it is padded with other letters of the alphabet
- (in alphabetical order).
- Returns
- =======
- ct
- Ciphertext (all caps, no spaces).
- Examples
- ========
- >>> from sympy.crypto.crypto import (
- ... encipher_bifid5, decipher_bifid5)
- "J" will be omitted unless it is replaced with something else:
- >>> round_trip = lambda m, k: \
- ... decipher_bifid5(encipher_bifid5(m, k), k)
- >>> key = 'a'
- >>> msg = "JOSIE"
- >>> round_trip(msg, key)
- 'OSIE'
- >>> round_trip(msg.replace("J", "I"), key)
- 'IOSIE'
- >>> j = "QIQ"
- >>> round_trip(msg.replace("J", j), key).replace(j, "J")
- 'JOSIE'
- Notes
- =====
- The Bifid cipher was invented around 1901 by Felix Delastelle.
- It is a *fractional substitution* cipher, where letters are
- replaced by pairs of symbols from a smaller alphabet. The
- cipher uses a `5 \times 5` square filled with some ordering of the
- alphabet, except that "J" is replaced with "I" (this is a so-called
- Polybius square; there is a `6 \times 6` analog if you add back in
- "J" and also append onto the usual 26 letter alphabet, the digits
- 0, 1, ..., 9).
- According to Helen Gaines' book *Cryptanalysis*, this type of cipher
- was used in the field by the German Army during World War I.
- See Also
- ========
- decipher_bifid5, encipher_bifid
- """
- msg, key, _ = _prep(msg.upper(), key.upper(), None, bifid5)
- key = padded_key(key, bifid5)
- return encipher_bifid(msg, '', key)
- def decipher_bifid5(msg, key):
- r"""
- Return the Bifid cipher decryption of ``msg``.
- Explanation
- ===========
- This is the version of the Bifid cipher that uses the `5 \times 5`
- Polybius square; the letter "J" is ignored unless a ``key`` of
- length 25 is used.
- Parameters
- ==========
- msg
- Ciphertext string.
- key
- Short string for key; duplicated characters are ignored and if
- the length is less then 25 characters, it will be padded with
- other letters from the alphabet omitting "J".
- Non-alphabetic characters are ignored.
- Returns
- =======
- plaintext
- Plaintext from Bifid5 cipher (all caps, no spaces).
- Examples
- ========
- >>> from sympy.crypto.crypto import encipher_bifid5, decipher_bifid5
- >>> key = "gold bug"
- >>> encipher_bifid5('meet me on friday', key)
- 'IEILEHFSTSFXEE'
- >>> encipher_bifid5('meet me on monday', key)
- 'IEILHHFSTSFQYE'
- >>> decipher_bifid5(_, key)
- 'MEETMEONMONDAY'
- """
- msg, key, _ = _prep(msg.upper(), key.upper(), None, bifid5)
- key = padded_key(key, bifid5)
- return decipher_bifid(msg, '', key)
- def bifid5_square(key=None):
- r"""
- 5x5 Polybius square.
- Produce the Polybius square for the `5 \times 5` Bifid cipher.
- Examples
- ========
- >>> from sympy.crypto.crypto import bifid5_square
- >>> bifid5_square("gold bug")
- Matrix([
- [G, O, L, D, B],
- [U, A, C, E, F],
- [H, I, K, M, N],
- [P, Q, R, S, T],
- [V, W, X, Y, Z]])
- """
- if not key:
- key = bifid5
- else:
- _, key, _ = _prep('', key.upper(), None, bifid5)
- key = padded_key(key, bifid5)
- return bifid_square(key)
- def encipher_bifid6(msg, key):
- r"""
- Performs the Bifid cipher encryption on plaintext ``msg``, and
- returns the ciphertext.
- This is the version of the Bifid cipher that uses the `6 \times 6`
- Polybius square.
- Parameters
- ==========
- msg
- Plaintext string (digits okay).
- key
- Short string for key (digits okay).
- If ``key`` is less than 36 characters long, the square will be
- filled with letters A through Z and digits 0 through 9.
- Returns
- =======
- ciphertext
- Ciphertext from Bifid cipher (all caps, no spaces).
- See Also
- ========
- decipher_bifid6, encipher_bifid
- """
- msg, key, _ = _prep(msg.upper(), key.upper(), None, bifid6)
- key = padded_key(key, bifid6)
- return encipher_bifid(msg, '', key)
- def decipher_bifid6(msg, key):
- r"""
- Performs the Bifid cipher decryption on ciphertext ``msg``, and
- returns the plaintext.
- This is the version of the Bifid cipher that uses the `6 \times 6`
- Polybius square.
- Parameters
- ==========
- msg
- Ciphertext string (digits okay); converted to upper case
- key
- Short string for key (digits okay).
- If ``key`` is less than 36 characters long, the square will be
- filled with letters A through Z and digits 0 through 9.
- All letters are converted to uppercase.
- Returns
- =======
- plaintext
- Plaintext from Bifid cipher (all caps, no spaces).
- Examples
- ========
- >>> from sympy.crypto.crypto import encipher_bifid6, decipher_bifid6
- >>> key = "gold bug"
- >>> encipher_bifid6('meet me on monday at 8am', key)
- 'KFKLJJHF5MMMKTFRGPL'
- >>> decipher_bifid6(_, key)
- 'MEETMEONMONDAYAT8AM'
- """
- msg, key, _ = _prep(msg.upper(), key.upper(), None, bifid6)
- key = padded_key(key, bifid6)
- return decipher_bifid(msg, '', key)
- def bifid6_square(key=None):
- r"""
- 6x6 Polybius square.
- Produces the Polybius square for the `6 \times 6` Bifid cipher.
- Assumes alphabet of symbols is "A", ..., "Z", "0", ..., "9".
- Examples
- ========
- >>> from sympy.crypto.crypto import bifid6_square
- >>> key = "gold bug"
- >>> bifid6_square(key)
- Matrix([
- [G, O, L, D, B, U],
- [A, C, E, F, H, I],
- [J, K, M, N, P, Q],
- [R, S, T, V, W, X],
- [Y, Z, 0, 1, 2, 3],
- [4, 5, 6, 7, 8, 9]])
- """
- if not key:
- key = bifid6
- else:
- _, key, _ = _prep('', key.upper(), None, bifid6)
- key = padded_key(key, bifid6)
- return bifid_square(key)
- #################### RSA #############################
- def _decipher_rsa_crt(i, d, factors):
- """Decipher RSA using chinese remainder theorem from the information
- of the relatively-prime factors of the modulus.
- Parameters
- ==========
- i : integer
- Ciphertext
- d : integer
- The exponent component.
- factors : list of relatively-prime integers
- The integers given must be coprime and the product must equal
- the modulus component of the original RSA key.
- Examples
- ========
- How to decrypt RSA with CRT:
- >>> from sympy.crypto.crypto import rsa_public_key, rsa_private_key
- >>> primes = [61, 53]
- >>> e = 17
- >>> args = primes + [e]
- >>> puk = rsa_public_key(*args)
- >>> prk = rsa_private_key(*args)
- >>> from sympy.crypto.crypto import encipher_rsa, _decipher_rsa_crt
- >>> msg = 65
- >>> crt_primes = primes
- >>> encrypted = encipher_rsa(msg, puk)
- >>> decrypted = _decipher_rsa_crt(encrypted, prk[1], primes)
- >>> decrypted
- 65
- """
- moduluses = [pow(i, d, p) for p in factors]
- result = crt(factors, moduluses)
- if not result:
- raise ValueError("CRT failed")
- return result[0]
- def _rsa_key(*args, public=True, private=True, totient='Euler', index=None, multipower=None):
- r"""A private subroutine to generate RSA key
- Parameters
- ==========
- public, private : bool, optional
- Flag to generate either a public key, a private key.
- totient : 'Euler' or 'Carmichael'
- Different notation used for totient.
- multipower : bool, optional
- Flag to bypass warning for multipower RSA.
- """
- if len(args) < 2:
- return False
- if totient not in ('Euler', 'Carmichael'):
- raise ValueError(
- "The argument totient={} should either be " \
- "'Euler', 'Carmichalel'." \
- .format(totient))
- if totient == 'Euler':
- _totient = _euler
- else:
- _totient = _carmichael
- if index is not None:
- index = as_int(index)
- if totient != 'Carmichael':
- raise ValueError(
- "Setting the 'index' keyword argument requires totient"
- "notation to be specified as 'Carmichael'.")
- primes, e = args[:-1], args[-1]
- if not all(isprime(p) for p in primes):
- new_primes = []
- for i in primes:
- new_primes.extend(factorint(i, multiple=True))
- primes = new_primes
- n = reduce(lambda i, j: i*j, primes)
- tally = multiset(primes)
- if all(v == 1 for v in tally.values()):
- phi = int(_totient(tally))
- else:
- if not multipower:
- NonInvertibleCipherWarning(
- 'Non-distinctive primes found in the factors {}. '
- 'The cipher may not be decryptable for some numbers '
- 'in the complete residue system Z[{}], but the cipher '
- 'can still be valid if you restrict the domain to be '
- 'the reduced residue system Z*[{}]. You can pass '
- 'the flag multipower=True if you want to suppress this '
- 'warning.'
- .format(primes, n, n)
- # stacklevel=4 because most users will call a function that
- # calls this function
- ).warn(stacklevel=4)
- phi = int(_totient(tally))
- if gcd(e, phi) == 1:
- if public and not private:
- if isinstance(index, int):
- e = e % phi
- e += index * phi
- return n, e
- if private and not public:
- d = invert(e, phi)
- if isinstance(index, int):
- d += index * phi
- return n, d
- return False
- def rsa_public_key(*args, **kwargs):
- r"""Return the RSA *public key* pair, `(n, e)`
- Parameters
- ==========
- args : naturals
- If specified as `p, q, e` where `p` and `q` are distinct primes
- and `e` is a desired public exponent of the RSA, `n = p q` and
- `e` will be verified against the totient
- `\phi(n)` (Euler totient) or `\lambda(n)` (Carmichael totient)
- to be `\gcd(e, \phi(n)) = 1` or `\gcd(e, \lambda(n)) = 1`.
- If specified as `p_1, p_2, \dots, p_n, e` where
- `p_1, p_2, \dots, p_n` are specified as primes,
- and `e` is specified as a desired public exponent of the RSA,
- it will be able to form a multi-prime RSA, which is a more
- generalized form of the popular 2-prime RSA.
- It can also be possible to form a single-prime RSA by specifying
- the argument as `p, e`, which can be considered a trivial case
- of a multiprime RSA.
- Furthermore, it can be possible to form a multi-power RSA by
- specifying two or more pairs of the primes to be same.
- However, unlike the two-distinct prime RSA or multi-prime
- RSA, not every numbers in the complete residue system
- (`\mathbb{Z}_n`) will be decryptable since the mapping
- `\mathbb{Z}_{n} \rightarrow \mathbb{Z}_{n}`
- will not be bijective.
- (Only except for the trivial case when
- `e = 1`
- or more generally,
- .. math::
- e \in \left \{ 1 + k \lambda(n)
- \mid k \in \mathbb{Z} \land k \geq 0 \right \}
- when RSA reduces to the identity.)
- However, the RSA can still be decryptable for the numbers in the
- reduced residue system (`\mathbb{Z}_n^{\times}`), since the
- mapping
- `\mathbb{Z}_{n}^{\times} \rightarrow \mathbb{Z}_{n}^{\times}`
- can still be bijective.
- If you pass a non-prime integer to the arguments
- `p_1, p_2, \dots, p_n`, the particular number will be
- prime-factored and it will become either a multi-prime RSA or a
- multi-power RSA in its canonical form, depending on whether the
- product equals its radical or not.
- `p_1 p_2 \dots p_n = \text{rad}(p_1 p_2 \dots p_n)`
- totient : bool, optional
- If ``'Euler'``, it uses Euler's totient `\phi(n)` which is
- :meth:`sympy.functions.combinatorial.numbers.totient` in SymPy.
- If ``'Carmichael'``, it uses Carmichael's totient `\lambda(n)`
- which is :meth:`sympy.functions.combinatorial.numbers.reduced_totient` in SymPy.
- Unlike private key generation, this is a trivial keyword for
- public key generation because
- `\gcd(e, \phi(n)) = 1 \iff \gcd(e, \lambda(n)) = 1`.
- index : nonnegative integer, optional
- Returns an arbitrary solution of a RSA public key at the index
- specified at `0, 1, 2, \dots`. This parameter needs to be
- specified along with ``totient='Carmichael'``.
- Similarly to the non-uniquenss of a RSA private key as described
- in the ``index`` parameter documentation in
- :meth:`rsa_private_key`, RSA public key is also not unique and
- there is an infinite number of RSA public exponents which
- can behave in the same manner.
- From any given RSA public exponent `e`, there are can be an
- another RSA public exponent `e + k \lambda(n)` where `k` is an
- integer, `\lambda` is a Carmichael's totient function.
- However, considering only the positive cases, there can be
- a principal solution of a RSA public exponent `e_0` in
- `0 < e_0 < \lambda(n)`, and all the other solutions
- can be canonicalzed in a form of `e_0 + k \lambda(n)`.
- ``index`` specifies the `k` notation to yield any possible value
- an RSA public key can have.
- An example of computing any arbitrary RSA public key:
- >>> from sympy.crypto.crypto import rsa_public_key
- >>> rsa_public_key(61, 53, 17, totient='Carmichael', index=0)
- (3233, 17)
- >>> rsa_public_key(61, 53, 17, totient='Carmichael', index=1)
- (3233, 797)
- >>> rsa_public_key(61, 53, 17, totient='Carmichael', index=2)
- (3233, 1577)
- multipower : bool, optional
- Any pair of non-distinct primes found in the RSA specification
- will restrict the domain of the cryptosystem, as noted in the
- explanation of the parameter ``args``.
- SymPy RSA key generator may give a warning before dispatching it
- as a multi-power RSA, however, you can disable the warning if
- you pass ``True`` to this keyword.
- Returns
- =======
- (n, e) : int, int
- `n` is a product of any arbitrary number of primes given as
- the argument.
- `e` is relatively prime (coprime) to the Euler totient
- `\phi(n)`.
- False
- Returned if less than two arguments are given, or `e` is
- not relatively prime to the modulus.
- Examples
- ========
- >>> from sympy.crypto.crypto import rsa_public_key
- A public key of a two-prime RSA:
- >>> p, q, e = 3, 5, 7
- >>> rsa_public_key(p, q, e)
- (15, 7)
- >>> rsa_public_key(p, q, 30)
- False
- A public key of a multiprime RSA:
- >>> primes = [2, 3, 5, 7, 11, 13]
- >>> e = 7
- >>> args = primes + [e]
- >>> rsa_public_key(*args)
- (30030, 7)
- Notes
- =====
- Although the RSA can be generalized over any modulus `n`, using
- two large primes had became the most popular specification because a
- product of two large primes is usually the hardest to factor
- relatively to the digits of `n` can have.
- However, it may need further understanding of the time complexities
- of each prime-factoring algorithms to verify the claim.
- See Also
- ========
- rsa_private_key
- encipher_rsa
- decipher_rsa
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/RSA_%28cryptosystem%29
- .. [2] https://cacr.uwaterloo.ca/techreports/2006/cacr2006-16.pdf
- .. [3] https://link.springer.com/content/pdf/10.1007/BFb0055738.pdf
- .. [4] https://www.itiis.org/digital-library/manuscript/1381
- """
- return _rsa_key(*args, public=True, private=False, **kwargs)
- def rsa_private_key(*args, **kwargs):
- r"""Return the RSA *private key* pair, `(n, d)`
- Parameters
- ==========
- args : naturals
- The keyword is identical to the ``args`` in
- :meth:`rsa_public_key`.
- totient : bool, optional
- If ``'Euler'``, it uses Euler's totient convention `\phi(n)`
- which is :meth:`sympy.functions.combinatorial.numbers.totient` in SymPy.
- If ``'Carmichael'``, it uses Carmichael's totient convention
- `\lambda(n)` which is
- :meth:`sympy.functions.combinatorial.numbers.reduced_totient` in SymPy.
- There can be some output differences for private key generation
- as examples below.
- Example using Euler's totient:
- >>> from sympy.crypto.crypto import rsa_private_key
- >>> rsa_private_key(61, 53, 17, totient='Euler')
- (3233, 2753)
- Example using Carmichael's totient:
- >>> from sympy.crypto.crypto import rsa_private_key
- >>> rsa_private_key(61, 53, 17, totient='Carmichael')
- (3233, 413)
- index : nonnegative integer, optional
- Returns an arbitrary solution of a RSA private key at the index
- specified at `0, 1, 2, \dots`. This parameter needs to be
- specified along with ``totient='Carmichael'``.
- RSA private exponent is a non-unique solution of
- `e d \mod \lambda(n) = 1` and it is possible in any form of
- `d + k \lambda(n)`, where `d` is an another
- already-computed private exponent, and `\lambda` is a
- Carmichael's totient function, and `k` is any integer.
- However, considering only the positive cases, there can be
- a principal solution of a RSA private exponent `d_0` in
- `0 < d_0 < \lambda(n)`, and all the other solutions
- can be canonicalzed in a form of `d_0 + k \lambda(n)`.
- ``index`` specifies the `k` notation to yield any possible value
- an RSA private key can have.
- An example of computing any arbitrary RSA private key:
- >>> from sympy.crypto.crypto import rsa_private_key
- >>> rsa_private_key(61, 53, 17, totient='Carmichael', index=0)
- (3233, 413)
- >>> rsa_private_key(61, 53, 17, totient='Carmichael', index=1)
- (3233, 1193)
- >>> rsa_private_key(61, 53, 17, totient='Carmichael', index=2)
- (3233, 1973)
- multipower : bool, optional
- The keyword is identical to the ``multipower`` in
- :meth:`rsa_public_key`.
- Returns
- =======
- (n, d) : int, int
- `n` is a product of any arbitrary number of primes given as
- the argument.
- `d` is the inverse of `e` (mod `\phi(n)`) where `e` is the
- exponent given, and `\phi` is a Euler totient.
- False
- Returned if less than two arguments are given, or `e` is
- not relatively prime to the totient of the modulus.
- Examples
- ========
- >>> from sympy.crypto.crypto import rsa_private_key
- A private key of a two-prime RSA:
- >>> p, q, e = 3, 5, 7
- >>> rsa_private_key(p, q, e)
- (15, 7)
- >>> rsa_private_key(p, q, 30)
- False
- A private key of a multiprime RSA:
- >>> primes = [2, 3, 5, 7, 11, 13]
- >>> e = 7
- >>> args = primes + [e]
- >>> rsa_private_key(*args)
- (30030, 823)
- See Also
- ========
- rsa_public_key
- encipher_rsa
- decipher_rsa
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/RSA_%28cryptosystem%29
- .. [2] https://cacr.uwaterloo.ca/techreports/2006/cacr2006-16.pdf
- .. [3] https://link.springer.com/content/pdf/10.1007/BFb0055738.pdf
- .. [4] https://www.itiis.org/digital-library/manuscript/1381
- """
- return _rsa_key(*args, public=False, private=True, **kwargs)
- def _encipher_decipher_rsa(i, key, factors=None):
- n, d = key
- if not factors:
- return pow(i, d, n)
- def _is_coprime_set(l):
- is_coprime_set = True
- for i in range(len(l)):
- for j in range(i+1, len(l)):
- if gcd(l[i], l[j]) != 1:
- is_coprime_set = False
- break
- return is_coprime_set
- prod = reduce(lambda i, j: i*j, factors)
- if prod == n and _is_coprime_set(factors):
- return _decipher_rsa_crt(i, d, factors)
- return _encipher_decipher_rsa(i, key, factors=None)
- def encipher_rsa(i, key, factors=None):
- r"""Encrypt the plaintext with RSA.
- Parameters
- ==========
- i : integer
- The plaintext to be encrypted for.
- key : (n, e) where n, e are integers
- `n` is the modulus of the key and `e` is the exponent of the
- key. The encryption is computed by `i^e \bmod n`.
- The key can either be a public key or a private key, however,
- the message encrypted by a public key can only be decrypted by
- a private key, and vice versa, as RSA is an asymmetric
- cryptography system.
- factors : list of coprime integers
- This is identical to the keyword ``factors`` in
- :meth:`decipher_rsa`.
- Notes
- =====
- Some specifications may make the RSA not cryptographically
- meaningful.
- For example, `0`, `1` will remain always same after taking any
- number of exponentiation, thus, should be avoided.
- Furthermore, if `i^e < n`, `i` may easily be figured out by taking
- `e` th root.
- And also, specifying the exponent as `1` or in more generalized form
- as `1 + k \lambda(n)` where `k` is an nonnegative integer,
- `\lambda` is a carmichael totient, the RSA becomes an identity
- mapping.
- Examples
- ========
- >>> from sympy.crypto.crypto import encipher_rsa
- >>> from sympy.crypto.crypto import rsa_public_key, rsa_private_key
- Public Key Encryption:
- >>> p, q, e = 3, 5, 7
- >>> puk = rsa_public_key(p, q, e)
- >>> msg = 12
- >>> encipher_rsa(msg, puk)
- 3
- Private Key Encryption:
- >>> p, q, e = 3, 5, 7
- >>> prk = rsa_private_key(p, q, e)
- >>> msg = 12
- >>> encipher_rsa(msg, prk)
- 3
- Encryption using chinese remainder theorem:
- >>> encipher_rsa(msg, prk, factors=[p, q])
- 3
- """
- return _encipher_decipher_rsa(i, key, factors=factors)
- def decipher_rsa(i, key, factors=None):
- r"""Decrypt the ciphertext with RSA.
- Parameters
- ==========
- i : integer
- The ciphertext to be decrypted for.
- key : (n, d) where n, d are integers
- `n` is the modulus of the key and `d` is the exponent of the
- key. The decryption is computed by `i^d \bmod n`.
- The key can either be a public key or a private key, however,
- the message encrypted by a public key can only be decrypted by
- a private key, and vice versa, as RSA is an asymmetric
- cryptography system.
- factors : list of coprime integers
- As the modulus `n` created from RSA key generation is composed
- of arbitrary prime factors
- `n = {p_1}^{k_1}{p_2}^{k_2}\dots{p_n}^{k_n}` where
- `p_1, p_2, \dots, p_n` are distinct primes and
- `k_1, k_2, \dots, k_n` are positive integers, chinese remainder
- theorem can be used to compute `i^d \bmod n` from the
- fragmented modulo operations like
- .. math::
- i^d \bmod {p_1}^{k_1}, i^d \bmod {p_2}^{k_2}, \dots,
- i^d \bmod {p_n}^{k_n}
- or like
- .. math::
- i^d \bmod {p_1}^{k_1}{p_2}^{k_2},
- i^d \bmod {p_3}^{k_3}, \dots ,
- i^d \bmod {p_n}^{k_n}
- as long as every moduli does not share any common divisor each
- other.
- The raw primes used in generating the RSA key pair can be a good
- option.
- Note that the speed advantage of using this is only viable for
- very large cases (Like 2048-bit RSA keys) since the
- overhead of using pure Python implementation of
- :meth:`sympy.ntheory.modular.crt` may overcompensate the
- theoretical speed advantage.
- Notes
- =====
- See the ``Notes`` section in the documentation of
- :meth:`encipher_rsa`
- Examples
- ========
- >>> from sympy.crypto.crypto import decipher_rsa, encipher_rsa
- >>> from sympy.crypto.crypto import rsa_public_key, rsa_private_key
- Public Key Encryption and Decryption:
- >>> p, q, e = 3, 5, 7
- >>> prk = rsa_private_key(p, q, e)
- >>> puk = rsa_public_key(p, q, e)
- >>> msg = 12
- >>> new_msg = encipher_rsa(msg, prk)
- >>> new_msg
- 3
- >>> decipher_rsa(new_msg, puk)
- 12
- Private Key Encryption and Decryption:
- >>> p, q, e = 3, 5, 7
- >>> prk = rsa_private_key(p, q, e)
- >>> puk = rsa_public_key(p, q, e)
- >>> msg = 12
- >>> new_msg = encipher_rsa(msg, puk)
- >>> new_msg
- 3
- >>> decipher_rsa(new_msg, prk)
- 12
- Decryption using chinese remainder theorem:
- >>> decipher_rsa(new_msg, prk, factors=[p, q])
- 12
- See Also
- ========
- encipher_rsa
- """
- return _encipher_decipher_rsa(i, key, factors=factors)
- #################### kid krypto (kid RSA) #############################
- def kid_rsa_public_key(a, b, A, B):
- r"""
- Kid RSA is a version of RSA useful to teach grade school children
- since it does not involve exponentiation.
- Explanation
- ===========
- Alice wants to talk to Bob. Bob generates keys as follows.
- Key generation:
- * Select positive integers `a, b, A, B` at random.
- * Compute `M = a b - 1`, `e = A M + a`, `d = B M + b`,
- `n = (e d - 1)//M`.
- * The *public key* is `(n, e)`. Bob sends these to Alice.
- * The *private key* is `(n, d)`, which Bob keeps secret.
- Encryption: If `p` is the plaintext message then the
- ciphertext is `c = p e \pmod n`.
- Decryption: If `c` is the ciphertext message then the
- plaintext is `p = c d \pmod n`.
- Examples
- ========
- >>> from sympy.crypto.crypto import kid_rsa_public_key
- >>> a, b, A, B = 3, 4, 5, 6
- >>> kid_rsa_public_key(a, b, A, B)
- (369, 58)
- """
- M = a*b - 1
- e = A*M + a
- d = B*M + b
- n = (e*d - 1)//M
- return n, e
- def kid_rsa_private_key(a, b, A, B):
- """
- Compute `M = a b - 1`, `e = A M + a`, `d = B M + b`,
- `n = (e d - 1) / M`. The *private key* is `d`, which Bob
- keeps secret.
- Examples
- ========
- >>> from sympy.crypto.crypto import kid_rsa_private_key
- >>> a, b, A, B = 3, 4, 5, 6
- >>> kid_rsa_private_key(a, b, A, B)
- (369, 70)
- """
- M = a*b - 1
- e = A*M + a
- d = B*M + b
- n = (e*d - 1)//M
- return n, d
- def encipher_kid_rsa(msg, key):
- """
- Here ``msg`` is the plaintext and ``key`` is the public key.
- Examples
- ========
- >>> from sympy.crypto.crypto import (
- ... encipher_kid_rsa, kid_rsa_public_key)
- >>> msg = 200
- >>> a, b, A, B = 3, 4, 5, 6
- >>> key = kid_rsa_public_key(a, b, A, B)
- >>> encipher_kid_rsa(msg, key)
- 161
- """
- n, e = key
- return (msg*e) % n
- def decipher_kid_rsa(msg, key):
- """
- Here ``msg`` is the plaintext and ``key`` is the private key.
- Examples
- ========
- >>> from sympy.crypto.crypto import (
- ... kid_rsa_public_key, kid_rsa_private_key,
- ... decipher_kid_rsa, encipher_kid_rsa)
- >>> a, b, A, B = 3, 4, 5, 6
- >>> d = kid_rsa_private_key(a, b, A, B)
- >>> msg = 200
- >>> pub = kid_rsa_public_key(a, b, A, B)
- >>> pri = kid_rsa_private_key(a, b, A, B)
- >>> ct = encipher_kid_rsa(msg, pub)
- >>> decipher_kid_rsa(ct, pri)
- 200
- """
- n, d = key
- return (msg*d) % n
- #################### Morse Code ######################################
- morse_char = {
- ".-": "A", "-...": "B",
- "-.-.": "C", "-..": "D",
- ".": "E", "..-.": "F",
- "--.": "G", "....": "H",
- "..": "I", ".---": "J",
- "-.-": "K", ".-..": "L",
- "--": "M", "-.": "N",
- "---": "O", ".--.": "P",
- "--.-": "Q", ".-.": "R",
- "...": "S", "-": "T",
- "..-": "U", "...-": "V",
- ".--": "W", "-..-": "X",
- "-.--": "Y", "--..": "Z",
- "-----": "0", ".----": "1",
- "..---": "2", "...--": "3",
- "....-": "4", ".....": "5",
- "-....": "6", "--...": "7",
- "---..": "8", "----.": "9",
- ".-.-.-": ".", "--..--": ",",
- "---...": ":", "-.-.-.": ";",
- "..--..": "?", "-....-": "-",
- "..--.-": "_", "-.--.": "(",
- "-.--.-": ")", ".----.": "'",
- "-...-": "=", ".-.-.": "+",
- "-..-.": "/", ".--.-.": "@",
- "...-..-": "$", "-.-.--": "!"}
- char_morse = {v: k for k, v in morse_char.items()}
- def encode_morse(msg, sep='|', mapping=None):
- """
- Encodes a plaintext into popular Morse Code with letters
- separated by ``sep`` and words by a double ``sep``.
- Examples
- ========
- >>> from sympy.crypto.crypto import encode_morse
- >>> msg = 'ATTACK RIGHT FLANK'
- >>> encode_morse(msg)
- '.-|-|-|.-|-.-.|-.-||.-.|..|--.|....|-||..-.|.-..|.-|-.|-.-'
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Morse_code
- """
- mapping = mapping or char_morse
- assert sep not in mapping
- word_sep = 2*sep
- mapping[" "] = word_sep
- suffix = msg and msg[-1] in whitespace
- # normalize whitespace
- msg = (' ' if word_sep else '').join(msg.split())
- # omit unmapped chars
- chars = set(''.join(msg.split()))
- ok = set(mapping.keys())
- msg = translate(msg, None, ''.join(chars - ok))
- morsestring = []
- words = msg.split()
- for word in words:
- morseword = []
- for letter in word:
- morseletter = mapping[letter]
- morseword.append(morseletter)
- word = sep.join(morseword)
- morsestring.append(word)
- return word_sep.join(morsestring) + (word_sep if suffix else '')
- def decode_morse(msg, sep='|', mapping=None):
- """
- Decodes a Morse Code with letters separated by ``sep``
- (default is '|') and words by `word_sep` (default is '||)
- into plaintext.
- Examples
- ========
- >>> from sympy.crypto.crypto import decode_morse
- >>> mc = '--|---|...-|.||.|.-|...|-'
- >>> decode_morse(mc)
- 'MOVE EAST'
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Morse_code
- """
- mapping = mapping or morse_char
- word_sep = 2*sep
- characterstring = []
- words = msg.strip(word_sep).split(word_sep)
- for word in words:
- letters = word.split(sep)
- chars = [mapping[c] for c in letters]
- word = ''.join(chars)
- characterstring.append(word)
- rv = " ".join(characterstring)
- return rv
- #################### LFSRs ##########################################
- @doctest_depends_on(ground_types=['python', 'gmpy'])
- def lfsr_sequence(key, fill, n):
- r"""
- This function creates an LFSR sequence.
- Parameters
- ==========
- key : list
- A list of finite field elements, `[c_0, c_1, \ldots, c_k].`
- fill : list
- The list of the initial terms of the LFSR sequence,
- `[x_0, x_1, \ldots, x_k].`
- n
- Number of terms of the sequence that the function returns.
- Returns
- =======
- L
- The LFSR sequence defined by
- `x_{n+1} = c_k x_n + \ldots + c_0 x_{n-k}`, for
- `n \leq k`.
- Notes
- =====
- S. Golomb [G]_ gives a list of three statistical properties a
- sequence of numbers `a = \{a_n\}_{n=1}^\infty`,
- `a_n \in \{0,1\}`, should display to be considered
- "random". Define the autocorrelation of `a` to be
- .. math::
- C(k) = C(k,a) = \lim_{N\rightarrow \infty} {1\over N}\sum_{n=1}^N (-1)^{a_n + a_{n+k}}.
- In the case where `a` is periodic with period
- `P` then this reduces to
- .. math::
- C(k) = {1\over P}\sum_{n=1}^P (-1)^{a_n + a_{n+k}}.
- Assume `a` is periodic with period `P`.
- - balance:
- .. math::
- \left|\sum_{n=1}^P(-1)^{a_n}\right| \leq 1.
- - low autocorrelation:
- .. math::
- C(k) = \left\{ \begin{array}{cc} 1,& k = 0,\\ \epsilon, & k \ne 0. \end{array} \right.
- (For sequences satisfying these first two properties, it is known
- that `\epsilon = -1/P` must hold.)
- - proportional runs property: In each period, half the runs have
- length `1`, one-fourth have length `2`, etc.
- Moreover, there are as many runs of `1`'s as there are of
- `0`'s.
- Examples
- ========
- >>> from sympy.crypto.crypto import lfsr_sequence
- >>> from sympy.polys.domains import FF
- >>> F = FF(2)
- >>> fill = [F(1), F(1), F(0), F(1)]
- >>> key = [F(1), F(0), F(0), F(1)]
- >>> lfsr_sequence(key, fill, 10)
- [1 mod 2, 1 mod 2, 0 mod 2, 1 mod 2, 0 mod 2,
- 1 mod 2, 1 mod 2, 0 mod 2, 0 mod 2, 1 mod 2]
- References
- ==========
- .. [G] Solomon Golomb, Shift register sequences, Aegean Park Press,
- Laguna Hills, Ca, 1967
- """
- if not isinstance(key, list):
- raise TypeError("key must be a list")
- if not isinstance(fill, list):
- raise TypeError("fill must be a list")
- p = key[0].modulus()
- F = FF(p)
- s = fill
- k = len(fill)
- L = []
- for i in range(n):
- s0 = s[:]
- L.append(s[0])
- s = s[1:k]
- x = sum(int(key[i]*s0[i]) for i in range(k))
- s.append(F(x))
- return L # use [int(x) for x in L] for int version
- def lfsr_autocorrelation(L, P, k):
- """
- This function computes the LFSR autocorrelation function.
- Parameters
- ==========
- L
- A periodic sequence of elements of `GF(2)`.
- L must have length larger than P.
- P
- The period of L.
- k : int
- An integer `k` (`0 < k < P`).
- Returns
- =======
- autocorrelation
- The k-th value of the autocorrelation of the LFSR L.
- Examples
- ========
- >>> from sympy.crypto.crypto import (
- ... lfsr_sequence, lfsr_autocorrelation)
- >>> from sympy.polys.domains import FF
- >>> F = FF(2)
- >>> fill = [F(1), F(1), F(0), F(1)]
- >>> key = [F(1), F(0), F(0), F(1)]
- >>> s = lfsr_sequence(key, fill, 20)
- >>> lfsr_autocorrelation(s, 15, 7)
- -1/15
- >>> lfsr_autocorrelation(s, 15, 0)
- 1
- """
- if not isinstance(L, list):
- raise TypeError("L (=%s) must be a list" % L)
- P = int(P)
- k = int(k)
- L0 = L[:P] # slices makes a copy
- L1 = L0 + L0[:k]
- L2 = [(-1)**(int(L1[i]) + int(L1[i + k])) for i in range(P)]
- tot = sum(L2)
- return Rational(tot, P)
- def lfsr_connection_polynomial(s):
- """
- This function computes the LFSR connection polynomial.
- Parameters
- ==========
- s
- A sequence of elements of even length, with entries in a finite
- field.
- Returns
- =======
- C(x)
- The connection polynomial of a minimal LFSR yielding s.
- This implements the algorithm in section 3 of J. L. Massey's
- article [M]_.
- Examples
- ========
- >>> from sympy.crypto.crypto import (
- ... lfsr_sequence, lfsr_connection_polynomial)
- >>> from sympy.polys.domains import FF
- >>> F = FF(2)
- >>> fill = [F(1), F(1), F(0), F(1)]
- >>> key = [F(1), F(0), F(0), F(1)]
- >>> s = lfsr_sequence(key, fill, 20)
- >>> lfsr_connection_polynomial(s)
- x**4 + x + 1
- >>> fill = [F(1), F(0), F(0), F(1)]
- >>> key = [F(1), F(1), F(0), F(1)]
- >>> s = lfsr_sequence(key, fill, 20)
- >>> lfsr_connection_polynomial(s)
- x**3 + 1
- >>> fill = [F(1), F(0), F(1)]
- >>> key = [F(1), F(1), F(0)]
- >>> s = lfsr_sequence(key, fill, 20)
- >>> lfsr_connection_polynomial(s)
- x**3 + x**2 + 1
- >>> fill = [F(1), F(0), F(1)]
- >>> key = [F(1), F(0), F(1)]
- >>> s = lfsr_sequence(key, fill, 20)
- >>> lfsr_connection_polynomial(s)
- x**3 + x + 1
- References
- ==========
- .. [M] James L. Massey, "Shift-Register Synthesis and BCH Decoding."
- IEEE Trans. on Information Theory, vol. 15(1), pp. 122-127,
- Jan 1969.
- """
- # Initialization:
- p = s[0].modulus()
- x = Symbol("x")
- C = 1*x**0
- B = 1*x**0
- m = 1
- b = 1*x**0
- L = 0
- N = 0
- while N < len(s):
- if L > 0:
- dC = Poly(C).degree()
- r = min(L + 1, dC + 1)
- coeffsC = [C.subs(x, 0)] + [C.coeff(x**i)
- for i in range(1, dC + 1)]
- d = (int(s[N]) + sum(coeffsC[i]*int(s[N - i])
- for i in range(1, r))) % p
- if L == 0:
- d = int(s[N])*x**0
- if d == 0:
- m += 1
- N += 1
- if d > 0:
- if 2*L > N:
- C = (C - d*((b**(p - 2)) % p)*x**m*B).expand()
- m += 1
- N += 1
- else:
- T = C
- C = (C - d*((b**(p - 2)) % p)*x**m*B).expand()
- L = N + 1 - L
- m = 1
- b = d
- B = T
- N += 1
- dC = Poly(C).degree()
- coeffsC = [C.subs(x, 0)] + [C.coeff(x**i) for i in range(1, dC + 1)]
- return sum(coeffsC[i] % p*x**i for i in range(dC + 1)
- if coeffsC[i] is not None)
- #################### ElGamal #############################
- def elgamal_private_key(digit=10, seed=None):
- r"""
- Return three number tuple as private key.
- Explanation
- ===========
- Elgamal encryption is based on the mathematical problem
- called the Discrete Logarithm Problem (DLP). For example,
- `a^{b} \equiv c \pmod p`
- In general, if ``a`` and ``b`` are known, ``ct`` is easily
- calculated. If ``b`` is unknown, it is hard to use
- ``a`` and ``ct`` to get ``b``.
- Parameters
- ==========
- digit : int
- Minimum number of binary digits for key.
- Returns
- =======
- tuple : (p, r, d)
- p = prime number.
- r = primitive root.
- d = random number.
- Notes
- =====
- For testing purposes, the ``seed`` parameter may be set to control
- the output of this routine. See sympy.core.random._randrange.
- Examples
- ========
- >>> from sympy.crypto.crypto import elgamal_private_key
- >>> from sympy.ntheory import is_primitive_root, isprime
- >>> a, b, _ = elgamal_private_key()
- >>> isprime(a)
- True
- >>> is_primitive_root(b, a)
- True
- """
- randrange = _randrange(seed)
- p = nextprime(2**digit)
- return p, primitive_root(p), randrange(2, p)
- def elgamal_public_key(key):
- r"""
- Return three number tuple as public key.
- Parameters
- ==========
- key : (p, r, e)
- Tuple generated by ``elgamal_private_key``.
- Returns
- =======
- tuple : (p, r, e)
- `e = r**d \bmod p`
- `d` is a random number in private key.
- Examples
- ========
- >>> from sympy.crypto.crypto import elgamal_public_key
- >>> elgamal_public_key((1031, 14, 636))
- (1031, 14, 212)
- """
- p, r, e = key
- return p, r, pow(r, e, p)
- def encipher_elgamal(i, key, seed=None):
- r"""
- Encrypt message with public key.
- Explanation
- ===========
- ``i`` is a plaintext message expressed as an integer.
- ``key`` is public key (p, r, e). In order to encrypt
- a message, a random number ``a`` in ``range(2, p)``
- is generated and the encrypted message is returned as
- `c_{1}` and `c_{2}` where:
- `c_{1} \equiv r^{a} \pmod p`
- `c_{2} \equiv m e^{a} \pmod p`
- Parameters
- ==========
- msg
- int of encoded message.
- key
- Public key.
- Returns
- =======
- tuple : (c1, c2)
- Encipher into two number.
- Notes
- =====
- For testing purposes, the ``seed`` parameter may be set to control
- the output of this routine. See sympy.core.random._randrange.
- Examples
- ========
- >>> from sympy.crypto.crypto import encipher_elgamal, elgamal_private_key, elgamal_public_key
- >>> pri = elgamal_private_key(5, seed=[3]); pri
- (37, 2, 3)
- >>> pub = elgamal_public_key(pri); pub
- (37, 2, 8)
- >>> msg = 36
- >>> encipher_elgamal(msg, pub, seed=[3])
- (8, 6)
- """
- p, r, e = key
- if i < 0 or i >= p:
- raise ValueError(
- 'Message (%s) should be in range(%s)' % (i, p))
- randrange = _randrange(seed)
- a = randrange(2, p)
- return pow(r, a, p), i*pow(e, a, p) % p
- def decipher_elgamal(msg, key):
- r"""
- Decrypt message with private key.
- `msg = (c_{1}, c_{2})`
- `key = (p, r, d)`
- According to extended Eucliden theorem,
- `u c_{1}^{d} + p n = 1`
- `u \equiv 1/{{c_{1}}^d} \pmod p`
- `u c_{2} \equiv \frac{1}{c_{1}^d} c_{2} \equiv \frac{1}{r^{ad}} c_{2} \pmod p`
- `\frac{1}{r^{ad}} m e^a \equiv \frac{1}{r^{ad}} m {r^{d a}} \equiv m \pmod p`
- Examples
- ========
- >>> from sympy.crypto.crypto import decipher_elgamal
- >>> from sympy.crypto.crypto import encipher_elgamal
- >>> from sympy.crypto.crypto import elgamal_private_key
- >>> from sympy.crypto.crypto import elgamal_public_key
- >>> pri = elgamal_private_key(5, seed=[3])
- >>> pub = elgamal_public_key(pri); pub
- (37, 2, 8)
- >>> msg = 17
- >>> decipher_elgamal(encipher_elgamal(msg, pub), pri) == msg
- True
- """
- p, _, d = key
- c1, c2 = msg
- u = pow(c1, -d, p)
- return u * c2 % p
- ################ Diffie-Hellman Key Exchange #########################
- def dh_private_key(digit=10, seed=None):
- r"""
- Return three integer tuple as private key.
- Explanation
- ===========
- Diffie-Hellman key exchange is based on the mathematical problem
- called the Discrete Logarithm Problem (see ElGamal).
- Diffie-Hellman key exchange is divided into the following steps:
- * Alice and Bob agree on a base that consist of a prime ``p``
- and a primitive root of ``p`` called ``g``
- * Alice choses a number ``a`` and Bob choses a number ``b`` where
- ``a`` and ``b`` are random numbers in range `[2, p)`. These are
- their private keys.
- * Alice then publicly sends Bob `g^{a} \pmod p` while Bob sends
- Alice `g^{b} \pmod p`
- * They both raise the received value to their secretly chosen
- number (``a`` or ``b``) and now have both as their shared key
- `g^{ab} \pmod p`
- Parameters
- ==========
- digit
- Minimum number of binary digits required in key.
- Returns
- =======
- tuple : (p, g, a)
- p = prime number.
- g = primitive root of p.
- a = random number from 2 through p - 1.
- Notes
- =====
- For testing purposes, the ``seed`` parameter may be set to control
- the output of this routine. See sympy.core.random._randrange.
- Examples
- ========
- >>> from sympy.crypto.crypto import dh_private_key
- >>> from sympy.ntheory import isprime, is_primitive_root
- >>> p, g, _ = dh_private_key()
- >>> isprime(p)
- True
- >>> is_primitive_root(g, p)
- True
- >>> p, g, _ = dh_private_key(5)
- >>> isprime(p)
- True
- >>> is_primitive_root(g, p)
- True
- """
- p = nextprime(2**digit)
- g = primitive_root(p)
- randrange = _randrange(seed)
- a = randrange(2, p)
- return p, g, a
- def dh_public_key(key):
- r"""
- Return three number tuple as public key.
- This is the tuple that Alice sends to Bob.
- Parameters
- ==========
- key : (p, g, a)
- A tuple generated by ``dh_private_key``.
- Returns
- =======
- tuple : int, int, int
- A tuple of `(p, g, g^a \mod p)` with `p`, `g` and `a` given as
- parameters.s
- Examples
- ========
- >>> from sympy.crypto.crypto import dh_private_key, dh_public_key
- >>> p, g, a = dh_private_key();
- >>> _p, _g, x = dh_public_key((p, g, a))
- >>> p == _p and g == _g
- True
- >>> x == pow(g, a, p)
- True
- """
- p, g, a = key
- return p, g, pow(g, a, p)
- def dh_shared_key(key, b):
- """
- Return an integer that is the shared key.
- This is what Bob and Alice can both calculate using the public
- keys they received from each other and their private keys.
- Parameters
- ==========
- key : (p, g, x)
- Tuple `(p, g, x)` generated by ``dh_public_key``.
- b
- Random number in the range of `2` to `p - 1`
- (Chosen by second key exchange member (Bob)).
- Returns
- =======
- int
- A shared key.
- Examples
- ========
- >>> from sympy.crypto.crypto import (
- ... dh_private_key, dh_public_key, dh_shared_key)
- >>> prk = dh_private_key();
- >>> p, g, x = dh_public_key(prk);
- >>> sk = dh_shared_key((p, g, x), 1000)
- >>> sk == pow(x, 1000, p)
- True
- """
- p, _, x = key
- if 1 >= b or b >= p:
- raise ValueError(filldedent('''
- Value of b should be greater 1 and less
- than prime %s.''' % p))
- return pow(x, b, p)
- ################ Goldwasser-Micali Encryption #########################
- def _legendre(a, p):
- """
- Returns the legendre symbol of a and p
- assuming that p is a prime.
- i.e. 1 if a is a quadratic residue mod p
- -1 if a is not a quadratic residue mod p
- 0 if a is divisible by p
- Parameters
- ==========
- a : int
- The number to test.
- p : prime
- The prime to test ``a`` against.
- Returns
- =======
- int
- Legendre symbol (a / p).
- """
- sig = pow(a, (p - 1)//2, p)
- if sig == 1:
- return 1
- elif sig == 0:
- return 0
- else:
- return -1
- def _random_coprime_stream(n, seed=None):
- randrange = _randrange(seed)
- while True:
- y = randrange(n)
- if gcd(y, n) == 1:
- yield y
- def gm_private_key(p, q, a=None):
- r"""
- Check if ``p`` and ``q`` can be used as private keys for
- the Goldwasser-Micali encryption. The method works
- roughly as follows.
- Explanation
- ===========
- #. Pick two large primes $p$ and $q$.
- #. Call their product $N$.
- #. Given a message as an integer $i$, write $i$ in its bit representation $b_0, \dots, b_n$.
- #. For each $k$,
- if $b_k = 0$:
- let $a_k$ be a random square
- (quadratic residue) modulo $p q$
- such that ``jacobi_symbol(a, p*q) = 1``
- if $b_k = 1$:
- let $a_k$ be a random non-square
- (non-quadratic residue) modulo $p q$
- such that ``jacobi_symbol(a, p*q) = 1``
- returns $\left[a_1, a_2, \dots\right]$
- $b_k$ can be recovered by checking whether or not
- $a_k$ is a residue. And from the $b_k$'s, the message
- can be reconstructed.
- The idea is that, while ``jacobi_symbol(a, p*q)``
- can be easily computed (and when it is equal to $-1$ will
- tell you that $a$ is not a square mod $p q$), quadratic
- residuosity modulo a composite number is hard to compute
- without knowing its factorization.
- Moreover, approximately half the numbers coprime to $p q$ have
- :func:`~.jacobi_symbol` equal to $1$ . And among those, approximately half
- are residues and approximately half are not. This maximizes the
- entropy of the code.
- Parameters
- ==========
- p, q, a
- Initialization variables.
- Returns
- =======
- tuple : (p, q)
- The input value ``p`` and ``q``.
- Raises
- ======
- ValueError
- If ``p`` and ``q`` are not distinct odd primes.
- """
- if p == q:
- raise ValueError("expected distinct primes, "
- "got two copies of %i" % p)
- elif not isprime(p) or not isprime(q):
- raise ValueError("first two arguments must be prime, "
- "got %i of %i" % (p, q))
- elif p == 2 or q == 2:
- raise ValueError("first two arguments must not be even, "
- "got %i of %i" % (p, q))
- return p, q
- def gm_public_key(p, q, a=None, seed=None):
- """
- Compute public keys for ``p`` and ``q``.
- Note that in Goldwasser-Micali Encryption,
- public keys are randomly selected.
- Parameters
- ==========
- p, q, a : int, int, int
- Initialization variables.
- Returns
- =======
- tuple : (a, N)
- ``a`` is the input ``a`` if it is not ``None`` otherwise
- some random integer coprime to ``p`` and ``q``.
- ``N`` is the product of ``p`` and ``q``.
- """
- p, q = gm_private_key(p, q)
- N = p * q
- if a is None:
- randrange = _randrange(seed)
- while True:
- a = randrange(N)
- if _legendre(a, p) == _legendre(a, q) == -1:
- break
- else:
- if _legendre(a, p) != -1 or _legendre(a, q) != -1:
- return False
- return (a, N)
- def encipher_gm(i, key, seed=None):
- """
- Encrypt integer 'i' using public_key 'key'
- Note that gm uses random encryption.
- Parameters
- ==========
- i : int
- The message to encrypt.
- key : (a, N)
- The public key.
- Returns
- =======
- list : list of int
- The randomized encrypted message.
- """
- if i < 0:
- raise ValueError(
- "message must be a non-negative "
- "integer: got %d instead" % i)
- a, N = key
- bits = []
- while i > 0:
- bits.append(i % 2)
- i //= 2
- gen = _random_coprime_stream(N, seed)
- rev = reversed(bits)
- encode = lambda b: next(gen)**2*pow(a, b) % N
- return [ encode(b) for b in rev ]
- def decipher_gm(message, key):
- """
- Decrypt message 'message' using public_key 'key'.
- Parameters
- ==========
- message : list of int
- The randomized encrypted message.
- key : (p, q)
- The private key.
- Returns
- =======
- int
- The encrypted message.
- """
- p, q = key
- res = lambda m, p: _legendre(m, p) > 0
- bits = [res(m, p) * res(m, q) for m in message]
- m = 0
- for b in bits:
- m <<= 1
- m += not b
- return m
- ########### RailFence Cipher #############
- def encipher_railfence(message,rails):
- """
- Performs Railfence Encryption on plaintext and returns ciphertext
- Examples
- ========
- >>> from sympy.crypto.crypto import encipher_railfence
- >>> message = "hello world"
- >>> encipher_railfence(message,3)
- 'horel ollwd'
- Parameters
- ==========
- message : string, the message to encrypt.
- rails : int, the number of rails.
- Returns
- =======
- The Encrypted string message.
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Rail_fence_cipher
- """
- r = list(range(rails))
- p = cycle(r + r[-2:0:-1])
- return ''.join(sorted(message, key=lambda i: next(p)))
- def decipher_railfence(ciphertext,rails):
- """
- Decrypt the message using the given rails
- Examples
- ========
- >>> from sympy.crypto.crypto import decipher_railfence
- >>> decipher_railfence("horel ollwd",3)
- 'hello world'
- Parameters
- ==========
- message : string, the message to encrypt.
- rails : int, the number of rails.
- Returns
- =======
- The Decrypted string message.
- """
- r = list(range(rails))
- p = cycle(r + r[-2:0:-1])
- idx = sorted(range(len(ciphertext)), key=lambda i: next(p))
- res = [''] * len(ciphertext)
- for i, c in zip(idx, ciphertext):
- res[i] = c
- return ''.join(res)
- ################ Blum-Goldwasser cryptosystem #########################
- def bg_private_key(p, q):
- """
- Check if p and q can be used as private keys for
- the Blum-Goldwasser cryptosystem.
- Explanation
- ===========
- The three necessary checks for p and q to pass
- so that they can be used as private keys:
- 1. p and q must both be prime
- 2. p and q must be distinct
- 3. p and q must be congruent to 3 mod 4
- Parameters
- ==========
- p, q
- The keys to be checked.
- Returns
- =======
- p, q
- Input values.
- Raises
- ======
- ValueError
- If p and q do not pass the above conditions.
- """
- if not isprime(p) or not isprime(q):
- raise ValueError("the two arguments must be prime, "
- "got %i and %i" %(p, q))
- elif p == q:
- raise ValueError("the two arguments must be distinct, "
- "got two copies of %i. " %p)
- elif (p - 3) % 4 != 0 or (q - 3) % 4 != 0:
- raise ValueError("the two arguments must be congruent to 3 mod 4, "
- "got %i and %i" %(p, q))
- return p, q
- def bg_public_key(p, q):
- """
- Calculates public keys from private keys.
- Explanation
- ===========
- The function first checks the validity of
- private keys passed as arguments and
- then returns their product.
- Parameters
- ==========
- p, q
- The private keys.
- Returns
- =======
- N
- The public key.
- """
- p, q = bg_private_key(p, q)
- N = p * q
- return N
- def encipher_bg(i, key, seed=None):
- """
- Encrypts the message using public key and seed.
- Explanation
- ===========
- ALGORITHM:
- 1. Encodes i as a string of L bits, m.
- 2. Select a random element r, where 1 < r < key, and computes
- x = r^2 mod key.
- 3. Use BBS pseudo-random number generator to generate L random bits, b,
- using the initial seed as x.
- 4. Encrypted message, c_i = m_i XOR b_i, 1 <= i <= L.
- 5. x_L = x^(2^L) mod key.
- 6. Return (c, x_L)
- Parameters
- ==========
- i
- Message, a non-negative integer
- key
- The public key
- Returns
- =======
- Tuple
- (encrypted_message, x_L)
- Raises
- ======
- ValueError
- If i is negative.
- """
- if i < 0:
- raise ValueError(
- "message must be a non-negative "
- "integer: got %d instead" % i)
- enc_msg = []
- while i > 0:
- enc_msg.append(i % 2)
- i //= 2
- enc_msg.reverse()
- L = len(enc_msg)
- r = _randint(seed)(2, key - 1)
- x = r**2 % key
- x_L = pow(int(x), int(2**L), int(key))
- rand_bits = []
- for _ in range(L):
- rand_bits.append(x % 2)
- x = x**2 % key
- encrypt_msg = [m ^ b for (m, b) in zip(enc_msg, rand_bits)]
- return (encrypt_msg, x_L)
- def decipher_bg(message, key):
- """
- Decrypts the message using private keys.
- Explanation
- ===========
- ALGORITHM:
- 1. Let, c be the encrypted message, y the second number received,
- and p and q be the private keys.
- 2. Compute, r_p = y^((p+1)/4 ^ L) mod p and
- r_q = y^((q+1)/4 ^ L) mod q.
- 3. Compute x_0 = (q(q^-1 mod p)r_p + p(p^-1 mod q)r_q) mod N.
- 4. From, recompute the bits using the BBS generator, as in the
- encryption algorithm.
- 5. Compute original message by XORing c and b.
- Parameters
- ==========
- message
- Tuple of encrypted message and a non-negative integer.
- key
- Tuple of private keys.
- Returns
- =======
- orig_msg
- The original message
- """
- p, q = key
- encrypt_msg, y = message
- public_key = p * q
- L = len(encrypt_msg)
- p_t = ((p + 1)/4)**L
- q_t = ((q + 1)/4)**L
- r_p = pow(int(y), int(p_t), int(p))
- r_q = pow(int(y), int(q_t), int(q))
- x = (q * invert(q, p) * r_p + p * invert(p, q) * r_q) % public_key
- orig_bits = []
- for _ in range(L):
- orig_bits.append(x % 2)
- x = x**2 % public_key
- orig_msg = 0
- for (m, b) in zip(encrypt_msg, orig_bits):
- orig_msg = orig_msg * 2
- orig_msg += (m ^ b)
- return orig_msg
|