Skip to content

Instantly share code, notes, and snippets.

@coruus
Last active August 29, 2015 14:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save coruus/da494f6b77f65946332b to your computer and use it in GitHub Desktop.
Save coruus/da494f6b77f65946332b to your computer and use it in GitHub Desktop.
Sleeveless primes
"""Author: dlg
License: CC0
"""
from __future__ import division, print_function
pseudoms = [(80, 65, 3), (81, 205, 3), (82, 57, 3), (83, 97, 3), (84, 69, 3), (85, 61, 3), (86, 41, 3), (87,
129, 3), (89, 1, 3), (90, 33, 3), (91, 45, 3), (92, 149, 3), (93, 25, 3), (94, 105, 3), (95, 37, 3),
(96, 17, 3), (97, 141, 3), (98, 65, 3), (99, 145, 3), (100, 153, 3), (101, 69, 3), (102, 33, 3),
(103, 97, 3), (104, 17, 3), (105, 13, 3), (106, 117, 3), (107, 1, 3), (108, 137, 3), (109, 165, 3),
(110, 21, 3), (111, 37, 3), (112, 189, 3), (113, 133, 3), (114, 53, 3), (115, 85, 3), (116, 69, 3),
(118, 5, 3), (119, 69, 3), (121, 73, 3), (122, 113, 3), (123, 241, 3), (124, 165, 3), (125, 9, 3),
(126, 137, 3), (127, 1, 3), (128, 173, 3), (129, 25, 3), (130, 5, 3), (131, 69, 3), (134, 45, 3),
(135, 45, 3), (136, 113, 3), (137, 13, 3), (138, 105, 3), (140, 57, 3), (141, 9, 3), (143, 69, 3),
(146, 153, 3), (147, 145, 3), (148, 197, 3), (149, 33, 3), (150, 5, 3), (152, 17, 3), (153, 69, 3),
(155, 49, 3), (156, 173, 3), (157, 133, 3), (158, 213, 3), (159, 241, 3), (160, 57, 3), (162, 101,
3), (163, 69, 3), (165, 25, 3), (166, 5, 3), (170, 153, 3), (174, 17, 3), (175, 229, 3), (176, 233,
3), (178, 41, 3), (179, 49, 3), (180, 189, 3), (181, 165, 3), (182, 161, 3), (184, 33, 3), (187, 85,
3), (188, 125, 3), (189, 25, 3), (190, 33, 3), (191, 69, 3), (192, 237, 3), (194, 33, 3), (197, 169,
3), (198, 17, 3), (199, 49, 3), (200, 117, 3), (203, 237, 3), (204, 249, 3), (205, 81, 3), (206, 5,
3), (207, 157, 3), (209, 33, 3), (210, 65, 3), (212, 29, 3), (213, 61, 3), (214, 185, 3), (215, 157,
3), (217, 61, 3), (218, 33, 3), (219, 121, 3), (220, 77, 3), (221, 133, 3), (222, 117, 3), (225, 49,
3), (226, 5, 3), (228, 93, 3), (230, 77, 3), (231, 165, 3), (235, 181, 3), (236, 209, 3), (237, 181,
3), (238, 161, 3), (243, 9, 3), (244, 189, 3), (247, 81, 3), (248, 237, 3), (251, 9, 3), (252, 129,
3), (254, 245, 3), (256, 189, 3), (257, 93, 3), (260, 149, 3), (265, 49, 3), (266, 213, 3), (268,
77, 3), (269, 241, 3), (270, 53, 3), (271, 169, 3), (272, 237, 3), (273, 205, 3), (275, 129, 3),
(276, 89, 3), (277, 181, 3), (278, 93, 3), (279, 69, 3), (280, 105, 3), (282, 93, 3), (283, 45, 3),
(284, 173, 3), (285, 9, 3), (286, 165, 3), (292, 197, 3), (294, 177, 3), (299, 69, 3), (300, 153,
3), (303, 121, 3), (308, 189, 3), (310, 77, 3), (311, 45, 3), (314, 113, 3), (316, 57, 3), (317, 33,
3), (318, 165, 3), (320, 197, 3), (321, 9, 3), (322, 185, 3), (323, 141, 3), (324, 177, 3), (326,
101, 3), (331, 61, 3), (336, 17, 3), (341, 229, 3), (342, 65, 3), (346, 45, 3), (348, 117, 3), (350,
113, 3), (351, 61, 3), (354, 153, 3), (355, 49, 3), (356, 173, 3), (363, 97, 3), (365, 169, 3),
(369, 25, 3), (372, 177, 3), (374, 65, 3), (376, 57, 3), (380, 65, 3), (382, 105, 3), (388, 45, 3),
(389, 21, 3), (390, 137, 3), (391, 105, 3), (393, 93, 3), (397, 81, 3), (402, 53, 3), (407, 157, 3),
(411, 205, 3), (413, 21, 3), (414, 17, 3), (415, 45, 3), (419, 69, 3), (422, 101, 3), (428, 65, 3),
(431, 201, 3), (434, 201, 3), (435, 97, 3), (436, 117, 3), (437, 93, 3), (439, 169, 3), (440, 33,
3), (444, 17, 3), (446, 77, 3), (449, 241, 3), (454, 57, 3), (455, 217, 3), (458, 57, 3), (460, 77,
3), (463, 157, 3), (468, 17, 3), (472, 209, 3), (475, 129, 3), (476, 153, 3), (484, 117, 3), (487,
57, 3), (488, 17, 3), (489, 21, 3), (491, 97, 3), (492, 129, 3), (494, 125, 3), (501, 45, 3), (506,
45, 3), (515, 225, 3), (521, 1, 3), (522, 117, 3), (528, 65, 3), (533, 153, 3), (536, 149, 3), (537,
9, 3), (538, 53, 3), (541, 81, 3), (543, 157, 3), (550, 5, 3), (561, 153, 3), (563, 9, 3), (565, 45,
3), (566, 113, 3), (573, 193, 3), (574, 105, 3), (577, 81, 3), (580, 57, 3), (583, 81, 3), (585, 81,
3), (586, 201, 3), (589, 181, 3), (594, 165, 3), (595, 81, 3), (596, 117, 3), (600, 149, 3), (601,
229, 3), (606, 153, 3), (607, 1, 3), (611, 217, 3), (615, 201, 3), (617, 213, 3), (619, 45, 3),
(622, 201, 3), (624, 117, 3), (627, 217, 3), (634, 141, 3), (641, 73, 3), (656, 153, 3), (664, 17,
3), (666, 93, 3), (667, 241, 3), (668, 209, 3), (670, 161, 3), (673, 141, 3), (678, 41, 3), (683,
45, 3), (692, 65, 3), (695, 49, 3), (699, 9, 3), (701, 169, 3), (704, 245, 3), (706, 5, 3), (708,
149, 3), (714, 221, 3), (716, 153, 3), (717, 25, 3), (721, 61, 3), (725, 33, 3), (728, 77, 3), (729,
9, 3), (743, 37, 3), (744, 173, 3), (745, 225, 3), (748, 153, 3), (750, 161, 3), (751, 165, 3),
(757, 141, 3), (760, 173, 3), (786, 137, 3), (789, 201, 3), (793, 229, 3), (800, 105, 3), (801, 205,
3), (807, 189, 3), (808, 17, 3), (810, 5, 3), (813, 73, 3), (815, 61, 3), (823, 97, 3), (824, 209,
3), (825, 61, 3), (829, 165, 3), (830, 153, 3), (840, 213, 3), (841, 201, 3), (848, 17, 3), (869,
21, 3), (872, 177, 3), (876, 117, 3), (880, 113, 3), (884, 189, 3), (886, 45, 3), (887, 69, 3),
(889, 49, 3), (891, 201, 3), (896, 213, 3), (907, 49, 3), (909, 153, 3), (914, 185, 3), (917, 189,
3), (919, 241, 3), (920, 185, 3), (931, 129, 3), (933, 133, 3), (939, 205, 3), (949, 81, 3), (950,
117, 3), (953, 229, 3), (961, 129, 3), (963, 109, 3), (967, 69, 3), (970, 237, 3), (972, 185, 3),
(977, 169, 3), (986, 77, 3), (993, 45, 3), (1003, 241, 3), (1007, 61, 3), (1008, 33, 3), (1018, 57,
3), (1024, 105, 3)]
pseudoms = [p for p in pseudoms if p[1] < 32]
print("Pseudomersennes:\np = 2^n - c, c < 256 && p%4==3, !E(c' < c && is_prime(2^n-c'))")
for p in pseudoms:
print(" 2^{:<3} - {:2}".format(p[0], p[1]))
ridinghoods = [(90, 45, 3), (100, 50, 3), (114, 57, 3), (216, 108, 3), (322, 161, 3), (416, 208, 3), (448, 224,
3), (450, 225, 3), (480, 240, 3), (708, 354, 3)]
print("Ridinghoods:\np = 2^n - 2^n/2 - 1")
for p in ridinghoods:
print(" 2^{:<3} - 2^{:<3} - 1".format(p[0], p[1]))
print("#pseudo = {}".format(len(pseudoms)))
print("#riding = {}".format(len(ridinghoods)))
T = "bits={:3}, #limbs={:2}, bpl={:.1f}"
from math import ceil
M = "2^{:<3} - {:3}"
R = "2^{:<3} - 2^{:<3} - 1"
primes = [(p[0], R.format(p[0], p[1])) for p in ridinghoods]
primes += [(p[0], M.format(p[0], p[1])) for p in pseudoms]
primes = sorted(primes)
T = " {:20} #limbs={:2} len={:3}"
lastl = 100
n, c = 1, 1,
out = []
for p in reversed(primes):
n, s = p
l = ceil(n / 58.)
bytelen = int(ceil((n-3)/8.))
if l < lastl:
out.append(T.format(s, int(l), bytelen))
lastl = l
print("Pareto frontier: n, #limbs")
print('\n'.join(reversed(out)))
T = " {:20} #limbs={:2} qrlen={:3} len={:3}"
lastl = 100
lastlen = 1000
n, c = 1, 1,
out = []
for p in reversed(primes):
n, s = p
l = ceil(n / 58.)
bytelen = int(ceil((n-3)/64.))
if bytelen < lastlen:
out.append(T.format(s, int(l), bytelen*8, int(ceil((n-3)/8))))
lastlen = bytelen
print("Pareto frontier: n, ceil(n / 64)")
print('\n'.join(reversed(out)))
T = " {:20} len={:3}"
lastl = 100
lastlen = 1000
n, c = 1, 1,
out = []
for p in reversed(primes):
n, s = p
l = ceil(n / 58.)
bytelen = int(ceil((n-3)/8.))
if bytelen < lastlen:
out.append(T.format(s, bytelen))
lastlen = bytelen
print("Pareto frontier: n, bytelen")
print('\n'.join(reversed(out)))
T = " {:20} #limbs/n={:2}"
lastl = 100
n, c = 1, 1,
out = []
for p in reversed(primes):
n, s = p
l = ceil(n / 58.)
l = n / (l*58.)
if l < lastl:
out.append(T.format(s, l, bytelen))
lastl = l
print("Pareto frontier: n, #limbs")
print('\n'.join(reversed(out)))
bd = {i: [] for i in range(128)}
for p in primes:
n, s = p
bytelen = int(ceil((n-3)/8))
eff = n / (ceil(n/58.)*58.)
bd[bytelen].append((eff, s))
bd = {i: sorted(v)[::-1][0] for i, v in bd.items() if v}
bd = {i: v for i, v in bd.items() if v[0] > 0.9}
qd = {i: [] for i in range(128)}
for p in primes:
n, s = p
bytelen = int(ceil((n-3)/64))
eff = n / (ceil(n/58.)*58.)
qd[bytelen].append((eff, s))
qd = {i: sorted(v)[::-1][0] for i, v in qd.items() if v}
print("quad")
for it in sorted(qd.items()):
(l, (eff, s)) = it
print("{:3}*8: {:20} ({:.2f})".format(l, s, eff))
print("byte")
for it in sorted(bd.items()):
(l, (eff, s)) = it
print("{:5}: {:20} ({:.2f})".format(l, s, eff))

Introduction

So. Motivated by prior posts by Michael Hamburg and Daniel Bernstein, I made up a list of "good primes" for ECC.

Criteria for primes

General criteria: 80 <= n = ceil(log2(p)) <= 1024.1 The prime must be 3 mod 4, so that all Montgomery curves are isogenous to an untwisted Edwards curve. (Note: this criterion excludes 2^255-19, which is 1 mod 4.)

I considered all ridinghood primes and all Crandall primes with c < 64 in this range; there are 10 ridinghood primes and 52 Crandall primes with minimal c in this range.

(The choice of nail-length is debatable, of course, as well as considering Ridinghoods that require irrational/split radices relatively quickly.)

The (very messy) script I used to generate these results can be found at: https://gist.github.com/coruus/da494f6b77f65946332b

Results

I calculated the Pareto frontiers2 for security strength versus three quantities:

  • number of nailed limbs, as ceil(n / 58)
  • number of quadwords in a compressed representation, ceil((n-3)/64)
  • number of bytes in a compressed representation, ceil((n-3)/8)

Pareto frontier for strength versus number of 58-bit limbs

Pareto frontier: n, #limbs=ceil(n/58)

2^114 - 2^57  - 1     #limbs= 2 len= 14
2^174 -  17           #limbs= 3 len= 22
2^226 -   5           #limbs= 4 len= 28
2^285 -   9           #limbs= 5 len= 36
2^336 -  17           #limbs= 6 len= 42
2^389 -  21           #limbs= 7 len= 49
2^450 - 2^225 - 1     #limbs= 8 len= 56
2^521 -   1           #limbs= 9 len= 65
2^563 -   9           #limbs=10 len= 70
2^607 -   1           #limbs=11 len= 76
2^664 -  17           #limbs=12 len= 83
2^729 -   9           #limbs=13 len= 91
2^810 -   5           #limbs=14 len=101

These results lend support to djb's assertion that top performance is a fairly rigid criterion, taking the number of limbs as a rough proxy for performance.

#limbs is obviously a very rough proxy; is there a decent generic formula for number of ops for Crandalls and Ridinghoods?

Pareto frontier for strength versus quad-aligned length

Pareto frontier: n, ceil((n-3) / 64)
2^130 -   5          #limbs= 3 qrlen= 16 len= 16
2^189 -  25          #limbs= 4 qrlen= 24 len= 24
2^251 -   9          #limbs= 5 qrlen= 32 len= 31
2^322 - 2^161 - 1    #limbs= 6 qrlen= 40 len= 40
2^369 -  25          #limbs= 7 qrlen= 48 len= 46
2^450 - 2^225 - 1    #limbs= 8 qrlen= 56 len= 56
2^489 -  21          #limbs= 9 qrlen= 64 len= 61
2^563 -   9          #limbs=10 qrlen= 72 len= 70
2^607 -   1          #limbs=11 qrlen= 80 len= 76
2^706 -   5          #limbs=13 qrlen= 88 len= 88
2^729 -   9          #limbs=13 qrlen= 96 len= 91
2^810 -   5          #limbs=14 qrlen=104 len=101

This is the Pareto frontier for storing Edward curve keys, assuming a compressed representation, and an 8-byte alignment requirement.

The ridinghood 2^450 - 2^225 - 1 comes out well here as well; I'm curious whether it is any slower than 2^448-2^224-1.

Pareto frontier for strength versus length

Pareto frontier: n, ceil((n-3)/8)
2^90  - 2^45  - 1     len= 11
2^96  -  17           len= 12
2^107 -   1           len= 13
2^114 - 2^57  - 1     len= 14
2^118 -   5           len= 15
2^130 -   5           len= 16
2^137 -  13           len= 17
2^141 -   9           len= 18
2^152 -  17           len= 19
2^166 -   5           len= 21
2^174 -  17           len= 22
2^189 -  25           len= 24
2^198 -  17           len= 25
2^206 -   5           len= 26
2^216 - 2^108 - 1     len= 27
2^226 -   5           len= 28
2^243 -   9           len= 30
2^251 -   9           len= 31
2^285 -   9           len= 36
2^322 - 2^161 - 1     len= 40
2^336 -  17           len= 42
2^369 -  25           len= 46
2^389 -  21           len= 49
2^416 - 2^208 - 1     len= 52
2^450 - 2^225 - 1     len= 56
2^468 -  17           len= 59
2^480 - 2^240 - 1     len= 60
2^489 -  21           len= 61
2^521 -   1           len= 65
2^537 -   9           len= 67
2^550 -   5           len= 69
2^563 -   9           len= 70
2^607 -   1           len= 76
2^664 -  17           len= 83
2^699 -   9           len= 87
2^706 -   5           len= 88
2^708 - 2^354 - 1     len= 89
2^717 -  25           len= 90
2^729 -   9           len= 91
2^810 -   5           len=101

This is the Pareto frontier for Edwards curve key bytelengths, assuming a compressed representation, and no alignment requirement. (As, e.g., is common for internet protocols.)

This obviously constrains choice of primes relatively little; still, there are only 40 good byte-lengths.

Efficiency of used limbs, by byte-length

And, finally, a list based on the efficiency with which 58-bit limbs are used for a given quadword-length:

len: prime eff=(n/(ceil(n/58)*58), eff >= 0.9
2*8: 2^114 - 2^57  - 1    (0.98)
3*8: 2^174 -  17          (1.00)
4*8: 2^226 -   5          (0.97)
5*8: 2^285 -   9          (0.98)
6*8: 2^336 -  17          (0.97)
7*8: 2^450 - 2^225 - 1    (0.97)
8*8: 2^489 -  21          (0.94)
9*8: 2^521 -   1          (1.00)

And bytelength:

len: prime eff=(n/(ceil(n/58)*58), eff >= 0.9
13: 2^107 -   1          (0.92)
14: 2^114 - 2^57  - 1    (0.98)
21: 2^166 -   5          (0.95)
22: 2^174 -  17          (1.00)
27: 2^216 - 2^108 - 1    (0.93)
28: 2^226 -   5          (0.97)
36: 2^285 -   9          (0.98)
40: 2^322 - 2^161 - 1    (0.93)
42: 2^336 -  17          (0.97)
46: 2^369 -  25          (0.91)
49: 2^389 -  21          (0.96)
56: 2^450 - 2^225 - 1    (0.97)
60: 2^480 - 2^240 - 1    (0.92)
61: 2^489 -  21          (0.94)
65: 2^521 -   1          (1.00)

Again, further support for performance being a fairly rigid criterion.

Appendix

The complete list of Crandall and Hamburg primes considered:

Ridinghoods: p = 2^n - 2^n/2 - 1 2^90 - 2^45 - 1 2^100 - 2^50 - 1 2^114 - 2^57 - 1 2^216 - 2^108 - 1 2^322 - 2^161 - 1 2^416 - 2^208 - 1 2^448 - 2^224 - 1 2^450 - 2^225 - 1 2^480 - 2^240 - 1 2^708 - 2^354 - 1

Crandalls: p = 2^n - c, c < 256 && p%4==3, !E(c' < c && is_prime(2^n-c')) 2^89 - 1 2^93 - 25 2^96 - 17 2^104 - 17 2^105 - 13 2^107 - 1 2^110 - 21 2^118 - 5 2^125 - 9 2^127 - 1 2^129 - 25 2^130 - 5 2^137 - 13 2^141 - 9 2^150 - 5 2^152 - 17 2^165 - 25 2^166 - 5 2^174 - 17 2^189 - 25 2^198 - 17 2^206 - 5 2^212 - 29 2^226 - 5 2^243 - 9 2^251 - 9 2^285 - 9 2^321 - 9 2^336 - 17 2^369 - 25 2^389 - 21 2^413 - 21 2^414 - 17 2^444 - 17 2^468 - 17 2^488 - 17 2^489 - 21 2^521 - 1 2^537 - 9 2^550 - 5 2^563 - 9 2^607 - 1 2^664 - 17 2^699 - 9 2^706 - 5 2^717 - 25 2^729 - 9 2^808 - 17 2^810 - 5 2^848 - 17 2^869 - 21

Footnotes

  1. For regular ECC: need b >> 160; for hyperelliptic crypto: need b > 80; for some exotic stuff (using, e.g., large cofactors or constructed curves), the bitlengths > 521 may be of some use.

  2. Pareto frontier here means the hull of a two-dimensional cost function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment