Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/crypto/util.py
4045 views
1
"""
2
Utility Functions for Cryptography
3
4
Miscellaneous utility functions for cryptographic purposes.
5
6
AUTHORS:
7
8
- Minh Van Nguyen (2009-12): initial version with the following functions:
9
``ascii_integer``, ``ascii_to_bin``, ``bin_to_ascii``, ``has_blum_prime``,
10
``is_blum_prime``, ``least_significant_bits``, ``random_blum_prime``.
11
"""
12
13
###########################################################################
14
# Copyright (c) 2009, 2010 Minh Van Nguyen <[email protected]>
15
#
16
# This program is free software; you can redistribute it and/or modify
17
# it under the terms of the GNU General Public License as published by
18
# the Free Software Foundation; either version 2 of the License, or
19
# (at your option) any later version.
20
#
21
# This program is distributed in the hope that it will be useful,
22
# but WITHOUT ANY WARRANTY; without even the implied warranty of
23
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
# GNU General Public License for more details.
25
#
26
# http://www.gnu.org/licenses/
27
###########################################################################
28
29
from sage.monoids.string_monoid import BinaryStrings
30
from sage.rings.arith import is_prime
31
from sage.rings.arith import lcm
32
from sage.rings.arith import primes
33
from sage.rings.arith import random_prime
34
from sage.rings.integer import Integer
35
from sage.rings.finite_rings.integer_mod import Mod as mod
36
37
def ascii_integer(B):
38
r"""
39
Return the ASCII integer corresponding to the binary string ``B``.
40
41
INPUT:
42
43
- ``B`` -- a non-empty binary string or a non-empty list of bits. The
44
number of bits in ``B`` must be 8.
45
46
OUTPUT:
47
48
- The ASCII integer corresponding to the 8-bit block ``B``.
49
50
EXAMPLES:
51
52
The ASCII integers of some binary strings::
53
54
sage: from sage.crypto.util import ascii_integer
55
sage: bin = BinaryStrings()
56
sage: B = bin.encoding("A"); B
57
01000001
58
sage: ascii_integer(B)
59
65
60
sage: B = bin.encoding("C"); list(B)
61
[0, 1, 0, 0, 0, 0, 1, 1]
62
sage: ascii_integer(list(B))
63
67
64
sage: ascii_integer("01000100")
65
68
66
sage: ascii_integer([0, 1, 0, 0, 0, 1, 0, 1])
67
69
68
69
TESTS:
70
71
The input ``B`` must be a non-empty string or a non-empty list of bits::
72
73
sage: from sage.crypto.util import ascii_integer
74
sage: ascii_integer("")
75
Traceback (most recent call last):
76
...
77
ValueError: B must consist of 8 bits.
78
sage: ascii_integer([])
79
Traceback (most recent call last):
80
...
81
ValueError: B must consist of 8 bits.
82
83
The input ``B`` must be an 8-bit string or a list of 8 bits::
84
85
sage: from sage.crypto.util import ascii_integer
86
sage: ascii_integer("101")
87
Traceback (most recent call last):
88
...
89
ValueError: B must consist of 8 bits.
90
sage: ascii_integer([1, 0, 1, 1])
91
Traceback (most recent call last):
92
...
93
ValueError: B must consist of 8 bits.
94
"""
95
if len(B) != 8:
96
raise ValueError("B must consist of 8 bits.")
97
L = map(lambda x: int(str(x)), list(B))
98
return sum([L[7], L[6]*2, L[5]*4, L[4]*8,
99
L[3]*16, L[2]*32, L[1]*64, L[0]*128])
100
101
def ascii_to_bin(A):
102
r"""
103
Return the binary representation of the ASCII string ``A``.
104
105
INPUT:
106
107
- ``A`` -- a string or list of ASCII characters.
108
109
OUTPUT:
110
111
- The binary representation of ``A``.
112
113
ALGORITHM:
114
115
Let `A = a_0 a_1 \cdots a_{n-1}` be an ASCII string, where each `a_i` is
116
an ASCII character. Let `c_i` be the ASCII integer corresponding to `a_i`
117
and let `b_i` be the binary representation of `c_i`. The binary
118
representation `B` of `A` is `B = b_0 b_1 \cdots b_{n-1}`.
119
120
EXAMPLES:
121
122
The binary representation of some ASCII strings::
123
124
sage: from sage.crypto.util import ascii_to_bin
125
sage: ascii_to_bin("A")
126
01000001
127
sage: ascii_to_bin("Abc123")
128
010000010110001001100011001100010011001000110011
129
130
The empty string is different from the string with one space character.
131
For the empty string and the empty list, this function returns the same
132
result::
133
134
sage: from sage.crypto.util import ascii_to_bin
135
sage: ascii_to_bin("")
136
<BLANKLINE>
137
sage: ascii_to_bin(" ")
138
00100000
139
sage: ascii_to_bin([])
140
<BLANKLINE>
141
142
This function also accepts a list of ASCII characters. You can also pass
143
in a list of strings::
144
145
sage: from sage.crypto.util import ascii_to_bin
146
sage: ascii_to_bin(["A", "b", "c", "1", "2", "3"])
147
010000010110001001100011001100010011001000110011
148
sage: ascii_to_bin(["A", "bc", "1", "23"])
149
010000010110001001100011001100010011001000110011
150
151
TESTS:
152
153
For a list of ASCII characters or strings, do not mix characters or
154
strings with integers::
155
156
sage: from sage.crypto.util import ascii_to_bin
157
sage: ascii_to_bin(["A", "b", "c", 1, 2, 3])
158
Traceback (most recent call last):
159
...
160
TypeError: sequence item 3: expected string, sage.rings.integer.Integer found
161
sage: ascii_to_bin(["Abc", 1, 2, 3])
162
Traceback (most recent call last):
163
...
164
TypeError: sequence item 1: expected string, sage.rings.integer.Integer found
165
"""
166
bin = BinaryStrings()
167
return bin.encoding("".join(list(A)))
168
169
def bin_to_ascii(B):
170
r"""
171
Return the ASCII representation of the binary string ``B``.
172
173
INPUT:
174
175
- ``B`` -- a non-empty binary string or a non-empty list of bits. The
176
number of bits in ``B`` must be a multiple of 8.
177
178
OUTPUT:
179
180
- The ASCII string corresponding to ``B``.
181
182
ALGORITHM:
183
184
Consider a block of bits `B = b_0 b_1 \cdots b_{n-1}` where each
185
sub-block `b_i` is a binary string of length 8. Then the total number
186
of bits is a multiple of 8 and is given by `8n`. Let `c_i` be the
187
integer representation of `b_i`. We can consider `c_i` as the integer
188
representation of an ASCII character. Then the ASCII representation
189
`A` of `B` is `A = a_0 a_1 \cdots a_{n-1}`.
190
191
EXAMPLES:
192
193
Convert some ASCII strings to their binary representations and recover
194
the ASCII strings from the binary representations::
195
196
sage: from sage.crypto.util import ascii_to_bin
197
sage: from sage.crypto.util import bin_to_ascii
198
sage: A = "Abc"
199
sage: B = ascii_to_bin(A); B
200
010000010110001001100011
201
sage: bin_to_ascii(B)
202
'Abc'
203
sage: bin_to_ascii(B) == A
204
True
205
206
::
207
208
sage: A = "123 \" #"
209
sage: B = ascii_to_bin(A); B
210
00110001001100100011001100100000001000100010000000100011
211
sage: bin_to_ascii(B)
212
'123 " #'
213
sage: bin_to_ascii(B) == A
214
True
215
216
This function also accepts strings and lists of bits::
217
218
sage: from sage.crypto.util import bin_to_ascii
219
sage: bin_to_ascii("010000010110001001100011")
220
'Abc'
221
sage: bin_to_ascii([0, 1, 0, 0, 0, 0, 0, 1])
222
'A'
223
224
TESTS:
225
226
The number of bits in ``B`` must be non-empty and a multiple of 8::
227
228
sage: from sage.crypto.util import bin_to_ascii
229
sage: bin_to_ascii("")
230
Traceback (most recent call last):
231
...
232
ValueError: B must be a non-empty binary string.
233
sage: bin_to_ascii([])
234
Traceback (most recent call last):
235
...
236
ValueError: B must be a non-empty binary string.
237
sage: bin_to_ascii(" ")
238
Traceback (most recent call last):
239
...
240
ValueError: The number of bits in B must be a multiple of 8.
241
sage: bin_to_ascii("101")
242
Traceback (most recent call last):
243
...
244
ValueError: The number of bits in B must be a multiple of 8.
245
sage: bin_to_ascii([1, 0, 1])
246
Traceback (most recent call last):
247
...
248
ValueError: The number of bits in B must be a multiple of 8.
249
"""
250
# sanity checks
251
n = len(B)
252
if n == 0:
253
raise ValueError("B must be a non-empty binary string.")
254
if mod(n, 8) != 0:
255
raise ValueError("The number of bits in B must be a multiple of 8.")
256
# perform conversion to ASCII string
257
b = map(lambda x: int(str(x)), list(B))
258
A = []
259
# the number of 8-bit blocks
260
k = n / 8
261
for i in xrange(k):
262
# Convert from 8-bit string to ASCII integer. Then convert the
263
# ASCII integer to the corresponding ASCII character.
264
A.append(chr(ascii_integer(b[8*i: 8*(i+1)])))
265
return "".join(A)
266
267
def carmichael_lambda(n):
268
r"""
269
Return the Carmichael function of a positive integer ``n``.
270
271
The Carmichael function of `n`, denoted `\lambda(n)`, is the smallest
272
positive integer `k` such that `a^k \equiv 1 \pmod{n}` for all
273
`a \in \ZZ/n\ZZ` satisfying `\gcd(a, n) = 1`. Thus, `\lambda(n) = k`
274
is the exponent of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`.
275
276
INPUT:
277
278
- ``n`` -- a positive integer.
279
280
OUTPUT:
281
282
- The Carmichael function of ``n``.
283
284
ALGORITHM:
285
286
If `n = 2, 4` then `\lambda(n) = \varphi(n)`. Let `p \geq 3` be an odd
287
prime and let `k` be a positive integer. Then
288
`\lambda(p^k) = p^{k - 1}(p - 1) = \varphi(p^k)`. If `k \geq 3`, then
289
`\lambda(2^k) = 2^{k - 2}`. Now consider the case where `n > 3` is
290
composite and let `n = p_1^{k_1} p_2^{k_2} \cdots p_t^{k_t}` be the
291
prime factorization of `n`. Then
292
293
.. MATH::
294
295
\lambda(n)
296
= \lambda(p_1^{k_1} p_2^{k_2} \cdots p_t^{k_t})
297
= \text{lcm}(\lambda(p_1^{k_1}), \lambda(p_2^{k_2}), \dots, \lambda(p_t^{k_t}))
298
299
EXAMPLES:
300
301
The Carmichael function of all positive integers up to and including 10::
302
303
sage: from sage.crypto.util import carmichael_lambda
304
sage: map(carmichael_lambda, [1..10])
305
[1, 1, 2, 2, 4, 2, 6, 2, 6, 4]
306
307
The Carmichael function of the first ten primes::
308
309
sage: map(carmichael_lambda, primes_first_n(10))
310
[1, 2, 4, 6, 10, 12, 16, 18, 22, 28]
311
312
Cases where the Carmichael function is equivalent to the Euler phi
313
function::
314
315
sage: carmichael_lambda(2) == euler_phi(2)
316
True
317
sage: carmichael_lambda(4) == euler_phi(4)
318
True
319
sage: p = random_prime(1000, lbound=3, proof=True)
320
sage: k = randint(1, 1000)
321
sage: carmichael_lambda(p^k) == euler_phi(p^k)
322
True
323
324
A case where `\lambda(n) \neq \varphi(n)`::
325
326
sage: k = randint(1, 1000)
327
sage: carmichael_lambda(2^k) == 2^(k - 2)
328
True
329
sage: carmichael_lambda(2^k) == 2^(k - 2) == euler_phi(2^k)
330
False
331
332
Verifying the current implementation of the Carmichael function using
333
another implemenation. The other implementation that we use for
334
verification is an exhaustive search for the exponent of the
335
multiplicative group `(\ZZ/n\ZZ)^{\ast}`. ::
336
337
sage: from sage.crypto.util import carmichael_lambda
338
sage: n = randint(1, 500)
339
sage: c = carmichael_lambda(n)
340
sage: def coprime(n):
341
... return [i for i in xrange(n) if gcd(i, n) == 1]
342
...
343
sage: def znpower(n, k):
344
... L = coprime(n)
345
... return map(power_mod, L, [k]*len(L), [n]*len(L))
346
...
347
sage: def my_carmichael(n):
348
... for k in xrange(1, n):
349
... L = znpower(n, k)
350
... ones = [1] * len(L)
351
... T = [L[i] == ones[i] for i in xrange(len(L))]
352
... if all(T):
353
... return k
354
...
355
sage: c == my_carmichael(n)
356
True
357
358
Carmichael's theorem states that `a^{\lambda(n)} \equiv 1 \pmod{n}`
359
for all elements `a` of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`.
360
Here, we verify Carmichael's theorem. ::
361
362
sage: from sage.crypto.util import carmichael_lambda
363
sage: n = randint(1, 1000)
364
sage: c = carmichael_lambda(n)
365
sage: ZnZ = IntegerModRing(n)
366
sage: M = ZnZ.list_of_elements_of_multiplicative_group()
367
sage: ones = [1] * len(M)
368
sage: P = [power_mod(a, c, n) for a in M]
369
sage: P == ones
370
True
371
372
TESTS:
373
374
The input ``n`` must be a positive integer::
375
376
sage: from sage.crypto.util import carmichael_lambda
377
sage: carmichael_lambda(0)
378
Traceback (most recent call last):
379
...
380
ValueError: Input n must be a positive integer.
381
sage: carmichael_lambda(randint(-10, 0))
382
Traceback (most recent call last):
383
...
384
ValueError: Input n must be a positive integer.
385
386
Bug reported in trac #8283::
387
388
sage: from sage.crypto.util import carmichael_lambda
389
sage: type(carmichael_lambda(16))
390
<type 'sage.rings.integer.Integer'>
391
392
REFERENCES:
393
394
.. [Carmichael2010] Carmichael function,
395
http://en.wikipedia.org/wiki/Carmichael_function
396
"""
397
n = Integer(n)
398
# sanity check
399
if n < 1:
400
raise ValueError("Input n must be a positive integer.")
401
402
L = n.factor()
403
t = []
404
405
# first get rid of the prime factor 2
406
if n & 1 == 0:
407
e = L[0][1]
408
L = L[1:] # now, n = 2**e * L.value()
409
if e < 3: # for 1 <= k < 3, lambda(2**k) = 2**(k - 1)
410
e = e - 1
411
else: # for k >= 3, lambda(2**k) = 2**(k - 2)
412
e = e - 2
413
t.append(1 << e) # 2**e
414
415
# then other prime factors
416
t += [p**(k - 1) * (p - 1) for p, k in L]
417
418
# finish the job
419
return lcm(t)
420
421
def has_blum_prime(lbound, ubound):
422
"""
423
Determine whether or not there is a Blum prime within the specified closed
424
interval.
425
426
INPUT:
427
428
- ``lbound`` -- positive integer; the lower bound on how small a
429
Blum prime can be. The lower bound must be distinct from the upper
430
bound.
431
432
- ``ubound`` -- positive integer; the upper bound on how large a
433
Blum prime can be. The lower bound must be distinct from the upper
434
bound.
435
436
OUTPUT:
437
438
- ``True`` if there is a Blum prime ``p`` such that
439
``lbound <= p <= ubound``. ``False`` otherwise.
440
441
ALGORITHM:
442
443
Let `L` and `U` be distinct positive integers. Let `P` be the set of all
444
odd primes `p` such that `L \leq p \leq U`. Our main focus is on Blum
445
primes, i.e. odd primes that are congruent to 3 modulo 4, so we assume
446
that the lower bound `L > 2`. The closed interval `[L, U]` has a Blum
447
prime if and only if the set `P` has a Blum prime.
448
449
EXAMPLES:
450
451
Testing for the presence of Blum primes within some closed intervals.
452
The interval `[4, 100]` has a Blum prime, the smallest such prime being
453
7. The interval `[24, 28]` has no primes, hence no Blum primes. ::
454
455
sage: from sage.crypto.util import has_blum_prime
456
sage: from sage.crypto.util import is_blum_prime
457
sage: has_blum_prime(4, 100)
458
True
459
sage: for n in xrange(4, 100):
460
... if is_blum_prime(n):
461
... print n
462
... break
463
...
464
7
465
sage: has_blum_prime(24, 28)
466
False
467
468
TESTS:
469
470
Both the lower and upper bounds must be greater than 2::
471
472
sage: from sage.crypto.util import has_blum_prime
473
sage: has_blum_prime(2, 3)
474
Traceback (most recent call last):
475
...
476
ValueError: Both the lower and upper bounds must be > 2.
477
sage: has_blum_prime(3, 2)
478
Traceback (most recent call last):
479
...
480
ValueError: Both the lower and upper bounds must be > 2.
481
sage: has_blum_prime(2, 2)
482
Traceback (most recent call last):
483
...
484
ValueError: Both the lower and upper bounds must be > 2.
485
486
The lower and upper bounds must be distinct from each other::
487
488
sage: has_blum_prime(3, 3)
489
Traceback (most recent call last):
490
...
491
ValueError: The lower and upper bounds must be distinct.
492
493
The lower bound must be less than the upper bound::
494
495
sage: has_blum_prime(4, 3)
496
Traceback (most recent call last):
497
...
498
ValueError: The lower bound must be less than the upper bound.
499
"""
500
# sanity checks
501
if (lbound < 3) or (ubound < 3):
502
raise ValueError("Both the lower and upper bounds must be > 2.")
503
if lbound == ubound:
504
raise ValueError("The lower and upper bounds must be distinct.")
505
if lbound > ubound:
506
raise ValueError("The lower bound must be less than the upper bound.")
507
# now test for presence of a Blum prime
508
for p in primes(lbound, ubound + 1):
509
if mod(p, 4).lift() == 3:
510
return True
511
return False
512
513
def is_blum_prime(n):
514
r"""
515
Determine whether or not ``n`` is a Blum prime.
516
517
INPUT:
518
519
- ``n`` a positive prime.
520
521
OUTPUT:
522
523
- ``True`` if ``n`` is a Blum prime; ``False`` otherwise.
524
525
Let `n` be a positive prime. Then `n` is a Blum prime if `n` is
526
congruent to 3 modulo 4, i.e. `n \equiv 3 \pmod{4}`.
527
528
EXAMPLES:
529
530
Testing some integers to see if they are Blum primes::
531
532
sage: from sage.crypto.util import is_blum_prime
533
sage: from sage.crypto.util import random_blum_prime
534
sage: is_blum_prime(101)
535
False
536
sage: is_blum_prime(7)
537
True
538
sage: p = random_blum_prime(10**3, 10**5)
539
sage: is_blum_prime(p)
540
True
541
"""
542
if n < 0:
543
return False
544
if is_prime(n):
545
if mod(n, 4).lift() == 3:
546
return True
547
else:
548
return False
549
else:
550
return False
551
552
def least_significant_bits(n, k):
553
r"""
554
Return the ``k`` least significant bits of ``n``.
555
556
INPUT:
557
558
- ``n`` -- an integer.
559
560
- ``k`` -- a positive integer.
561
562
OUTPUT:
563
564
- The ``k`` least significant bits of the integer ``n``. If ``k=1``,
565
then return the parity bit of the integer ``n``. Let `b` be the
566
binary representation of ``n``, where `m` is the length of the
567
binary string `b`. If `k \geq m`, then return the binary
568
representation of ``n``.
569
570
EXAMPLES:
571
572
Obtain the parity bits of some integers::
573
574
sage: from sage.crypto.util import least_significant_bits
575
sage: least_significant_bits(0, 1)
576
[0]
577
sage: least_significant_bits(2, 1)
578
[0]
579
sage: least_significant_bits(3, 1)
580
[1]
581
sage: least_significant_bits(-2, 1)
582
[0]
583
sage: least_significant_bits(-3, 1)
584
[1]
585
586
Obtain the 4 least significant bits of some integers::
587
588
sage: least_significant_bits(101, 4)
589
[0, 1, 0, 1]
590
sage: least_significant_bits(-101, 4)
591
[0, 1, 0, 1]
592
sage: least_significant_bits(124, 4)
593
[1, 1, 0, 0]
594
sage: least_significant_bits(-124, 4)
595
[1, 1, 0, 0]
596
597
The binary representation of 123::
598
599
sage: n = 123; b = n.binary(); b
600
'1111011'
601
sage: least_significant_bits(n, len(b))
602
[1, 1, 1, 1, 0, 1, 1]
603
"""
604
return map(int, list(n.binary()[-k:]))
605
606
def random_blum_prime(lbound, ubound, ntries=100):
607
r"""
608
A random Blum prime within the specified bounds.
609
610
Let `p` be a positive prime. Then `p` is a Blum prime if `p` is
611
congruent to 3 modulo 4, i.e. `p \equiv 3 \pmod{4}`.
612
613
INPUT:
614
615
- ``lbound`` -- positive integer; the lower bound on how small a
616
random Blum prime `p` can be. So we have
617
``0 < lbound <= p <= ubound``. The lower bound must be distinct from
618
the upper bound.
619
620
- ``ubound`` -- positive integer; the upper bound on how large a
621
random Blum prime `p` can be. So we have
622
``0 < lbound <= p <= ubound``. The lower bound must be distinct
623
from the upper bound.
624
625
- ``ntries`` -- (default: ``100``) the number of attempts to generate
626
a random Blum prime. If ``ntries`` is a positive integer, then
627
perform that many attempts at generating a random Blum prime. This
628
might or might not result in a Blum prime.
629
630
OUTPUT:
631
632
- A random Blum prime within the specified lower and upper bounds.
633
634
.. NOTE::
635
636
Beware that there might not be any primes between the lower and
637
upper bounds. So make sure that these two bounds are
638
"sufficiently" far apart from each other for there to be primes
639
congruent to 3 modulo 4. In particular, there should be at least
640
two distinct Blum primes within the specified bounds.
641
642
EXAMPLES:
643
644
Choose a random prime and check that it is a Blum prime::
645
646
sage: from sage.crypto.util import random_blum_prime
647
sage: p = random_blum_prime(10**4, 10**5)
648
sage: is_prime(p)
649
True
650
sage: mod(p, 4) == 3
651
True
652
653
TESTS:
654
655
Make sure that there is at least one Blum prime between the lower and
656
upper bounds. In the following example, we have ``lbound=24`` and
657
``ubound=30`` with 29 being the only prime within those bounds. But 29
658
is not a Blum prime. ::
659
660
sage: from sage.crypto.util import random_blum_prime
661
sage: random_blum_prime(24, 30, ntries=10)
662
Traceback (most recent call last):
663
...
664
ValueError: No Blum primes within the specified closed interval.
665
sage: random_blum_prime(24, 28)
666
Traceback (most recent call last):
667
...
668
ValueError: No Blum primes within the specified closed interval.
669
"""
670
# sanity check
671
if not has_blum_prime(lbound, ubound):
672
raise ValueError("No Blum primes within the specified closed interval.")
673
# Now we know that there is a Blum prime within the closed interval
674
# [lbound, ubound]. Pick one such prime at random.
675
p = random_prime(ubound, lbound=lbound, proof=True)
676
n = 1
677
while mod(p, 4) != 3:
678
p = random_prime(ubound, lbound=lbound, proof=True)
679
n += 1
680
if n > ntries:
681
raise ValueError("Maximum number of attempts exceeded.")
682
return p
683
684