-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathset-8-challenges.txt
2072 lines (1490 loc) · 73.8 KB
/
set-8-challenges.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
Welcome back, Cryptopals! It's the long-awaited eighth series!
Please don't share these. Instead, have interested parties mail
set8.cryptopals@gmail.com with subject "Crazy Flamboyant for the Rap
Enjoyment".
This set focuses on abstract algebra, including DH, GCM, and (most
importantly) elliptic curve cryptography.
Fair warning - it's really tough! There's a ton of content here, and
it's more demanding than anything we've released so far. By the time
you're done, you will have written an ad hoc, informally-specified,
bug-ridden, slow implementation of one percent of SageMath.
Problems:
57. Diffie-Hellman Revisited: Small Subgroup Confinement
58. Pollard's Method for Catching Kangaroos
59. Elliptic Curve Diffie-Hellman and Invalid-Curve Attacks
60. Single-Coordinate Ladders and Insecure Twists
61. Duplicate-Signature Key Selection in ECDSA (and RSA)
62. Key-Recovery Attacks on ECDSA with Biased Nonces
63. Key-Recovery Attacks on GCM with Repeated Nonces
64. Key-Recovery Attacks on GCM with a Truncated MAC
There's also a short bibliography of interesting papers and useful
links at the end.
BONUS: There is a secret ninth problem in this set! To unlock it,
submit your completed solutions to the first eight problem to
set8.cryptopals@gmail.com with subject "Shackling the Masses with
Drastic Rap Tactics".
Special thanks for this section go to Dan Bernstein, Tanja Lange, and
Watson Ladd, all of whom gave us great advice on problem selection.
// ------------------------------------------------------------
57. Diffie-Hellman Revisited: Subgroup-Confinement Attacks
This set is going to focus on elliptic curves. But before we get to
that, we're going to kick things off with some classic Diffie-Hellman.
Trust me, it's gonna make sense later.
Let's get right into it. First, build your typical Diffie-Hellman key
agreement: Alice and Bob exchange public keys and derive the same
shared secret. Then Bob sends Alice some message with a MAC over
it. Easy as pie.
Use these parameters:
p =
7199773997391911030609999317773941274322764333428698921736339643928346453700085358802973900485592910475480089726140708102474957429903531369589969318716771
g =
4565356397095740655436854503483826832136106141639563487732438195343690437606117828318042418238184896212352329118608100083187535033402010599512641674644143
The generator g has order q:
q = 236234353446506858198510045061214171961
"Order" is a new word, but it just means g^q = 1 mod p. You might
notice that q is a prime, just like p. This isn't mere chance: in
fact, we chose q and p together such that q divides p-1 (the order or
size of the group itself) evenly. This guarantees that an element g of
order q will exist. (In fact, there will be q-1 such elements.)
Back to the protocol. Alice and Bob should choose their secret keys as
random integers mod q. There's no point in choosing them mod p; since
g has order q, the numbers will just start repeating after that. You
can prove this to yourself by verifying g^x mod p = g^(x + k*q) mod p
for any x and k.
The rest is the same as before.
How can we attack this protocol? Remember what we said before about
order: the fact that q divides p-1 guarantees the existence of
elements of order q. What if there are smaller divisors of p-1?
Spoiler alert: there are. I chose j = (p-1) / q to have many small
factors because I want you to be happy. Find them by factoring j,
which is:
j =
30477252323177606811760882179058908038824640750610513771646768011063128035873508507547741559514324673960576895059570
You don't need to factor it all the way. Just find a bunch of factors
smaller than, say, 2^16. There should be plenty. (Friendly tip: maybe
avoid any repeated factors. They only complicate things.)
Got 'em? Good. Now, we can use these to recover Bob's secret key using
the Pohlig-Hellman algorithm for discrete logarithms. Here's how:
1. Take one of the small factors j. Call it r. We want to find an
element h of order r. To find it, do:
h := rand(1, p)^((p-1)/r) mod p
If h = 1, try again.
2. You're Eve. Send Bob h as your public key. Note that h is not a
valid public key! There is no x such that h = g^x mod p. But Bob
doesn't know that.
3. Bob will compute:
K := h^x mod p
Where x is his secret key and K is the output shared secret. Bob
then sends back (m, t), with:
m := "crazy flamboyant for the rap enjoyment"
t := MAC(K, m)
4. We (Eve) can't compute K, because h isn't actually a valid public
key. But we're not licked yet.
Remember how we saw that g^x starts repeating when x > q? h has the
same property with r. This means there are only r possible values
of K that Bob could have generated. We can recover K by doing a
brute-force search over these values until t = MAC(K, m).
Now we know Bob's secret key x mod r.
5. Repeat steps 1 through 4 many times. Eventually you will know:
x = b1 mod r1
x = b2 mod r2
x = b3 mod r3
...
Once (r1*r2*...*rn) > q, you'll have enough information to
reassemble Bob's secret key using the Chinese Remainder Theorem.
// ------------------------------------------------------------
58. Pollard's Method for Catching Kangaroos
The last problem was a little contrived. It only worked because I
helpfully foisted those broken group parameters on Alice and
Bob. While real-world groups may include some small subgroups, it's
improbable to find this many in a randomly generated group.
So what if we can only recover some fraction of the Bob's secret key?
It feels like there should be some way to use that knowledge to
recover the rest. And there is: Pollard's kangaroo algorithm.
This is a generic attack for computing a discrete logarithm (or
"index") known to lie within a certain contiguous range [a, b]. It has
a work factor approximately the square root of the size of the range.
The basic strategy is to try to find a collision between two
pseudorandom sequences of elements. One will start from an element of
known index, and the other will start from the element y whose index
we want to find.
It's important to understand how these sequences are
generated. Basically, we just define some function f mapping group
elements (like the generator g, or a public key y) to scalars (a
secret exponent, like x), i.e.:
f(y) = <some x>
Don't worry about how f is implemented for now. Just know that it's a
function mapping where we are (some y) to the next jump we're going to
take (some x). And it's deterministic: for a given y, it should always
return the same x.
Then we do a loop like this:
y := y * g^f(y)
The key thing here is that the next step we take is a function whose
sole input is the current element. This means that if our two
sequences ever happen to visit the same element y, they'll proceed in
lockstep from there.
Okay, let's get a bit more specific. I mentioned we're going to
generate two sequences this way. The first is our control
sequence. This is the tame kangaroo in Pollard's example. We do
something like this:
xT := 0
yT := g^b
for i in 1..N:
xT := xT + f(yT)
yT := yT * g^f(yT)
Recall that b is the upper bound on the index of y. So we're starting
the tame kangaroo's run at the very end of that range. Then we just
take N leaps and accumulate our total distance traveled in xT. At the
end of the loop, yT = g^(b + xT). This will be important later.
Note that this algorithm doesn't require us to build a big look-up
table a la Shanks' baby-step giant-step, so its space complexity is
constant. That's kinda neat.
Now: let's catch that wild kangaroo. We'll do a similar loop, this
time starting from y. Our hope is that at some point we'll collide
with the tame kangaroo's path. If we do, we'll eventually end up at
the same place. So on each iteration, we'll check if we're there.
xW := 0
yW := y
while xW < b - a + xT:
xW := xW + f(yW)
yW := yW * g^f(yW)
if yW = yT:
return b + xT - xW
Take a moment to puzzle out the loop condition. What that relation is
checking is whether we've gone past yT and missed it. In other words,
that we didn't collide. This is a probabilistic algorithm, so it's not
guaranteed to work.
Make sure also that you understand the return statement. If you think
through how we came to the final values for yW and yT, it should be
clear that this value is the index of the input y.
There are a couple implementation details we've glossed over -
specifically the function f and the iteration count N. I do something
like this:
f(y) = 2^(y mod k)
For some k, which you can play around with. Making k bigger will allow
you to take bigger leaps in each loop iteration.
N is then derived from f - take the mean of all possible outputs of f
and multiply it by a small constant, e.g. 4. You can make the constant
bigger to better your chances of finding a collision at the (obvious)
cost of extra computation. The reason N needs to depend on f is that f
governs the size of the jumps we can make. If the jumps are bigger, we
need a bigger runway to land on, or else we risk leaping past it.
Implement Pollard's kangaroo algorithm. Here are some (less
accommodating) group parameters:
p =
11470374874925275658116663507232161402086650258453896274534991676898999262641581519101074740642369848233294239851519212341844337347119899874391456329785623
q = 335062023296420808191071248367701059461
j =
34233586850807404623475048381328686211071196701374230492615844865929237417097514638999377942356150481334217896204702
g =
622952335333961296978159266084741085889881358738459939978290179936063635566740258555167783009058567397963466103140082647486611657350811560630587013183357
And here's a sample y:
y =
7760073848032689505395005705677365876654629189298052775754597607446617558600394076764814236081991643094239886772481052254010323780165093955236429914607119
The index of y is in the range [0, 2^20]. Find it with the kangaroo
algorithm.
Wait, that's small enough to brute force. Here's one whose index is in
[0, 2^40]:
y =
9388897478013399550694114614498790691034187453089355259602614074132918843899833277397448144245883225611726912025846772975325932794909655215329941809013733
Find that one, too. It might take a couple minutes.
~~ later ~~
Enough about kangaroos, let's get back to Bob. Suppose we know Bob's
secret key x = n mod r for some r < q. It's actually not totally
obvious how to apply this algorithm to get the rest! Because we only
have:
x = n mod r
Which means:
x = n + m*r
For some unknown m. This relation defines a set of values that are
spread out at intervals of r, but Pollard's kangaroo requires a
continuous range!
Actually, this isn't a big deal. Because check it out - we can just
apply the following transformations:
x = n + m*r
y = g^x = g^(n + m*r)
y = g^n * g^(m*r)
y' = y * g^-n = g^(m*r)
g' = g^r
y' = (g')^m
Now simply search for the index m of y' to the base element g'. Notice
that we have a rough bound for m: [0, (q-1)/r]. After you find m, you
can plug it into your existing knowledge of x to recover the rest of
the secret.
Take the above group parameters and generate a key pair for Bob. Use
your subgroup-confinement attack from the last problem to recover as
much of Bob's secret as you can. You'll be able to get a good chunk of
it, but not the whole thing. Then use the kangaroo algorithm to run
down the remaining bits.
// ------------------------------------------------------------
59. Elliptic Curve Diffie-Hellman and Invalid-Curve Attacks
I'm not going to show you any graphs - if you want to see one, you can
find them in, like, every other elliptic curve tutorial on the
internet. Personally, I've never been able to gain much insight from
them.
They're also really hard to draw in ASCII.
The key thing to understand about elliptic curves is that they're a
setting analogous in many ways to one we're more familiar with, the
multiplicative integers mod p. So if we learn how certain primitive
operations are defined, we can reason about them using a lot of tools
we already have in our utility belts.
Let's dig in. An elliptic curve E is just an equation like this:
y^2 = x^3 + a*x + b
The choice of the a and b coefficients defines the curve.
The elements in our group are going to be (x, y) coordinates
satisfying the curve equation. Now, there are infinitely many pairs
like that on the curve, but we only want to think about some of
them. We'll trim our set of points down by considering the curve in
the context of a finite field.
For the moment, it's not too important to know what a finite field
is. You can basically just think of it as "integers mod p" with all
the usual operations you expect: multiplication, division (via modular
inversion), addition, and subtraction.
We'll use the notation GF(p) to talk about a finite field of size
p. (The "GF" is for "Galois field", another name for a finite field.)
When we take a curve E over field GF(p) (written E(GF(p))), what we're
saying is that only points with both x and y in GF(p) are valid.
For example, (3, 6) might be a valid point in E(GF(7)), but it
wouldn't be a valid point in E(GF(5)); 6 is not a member of GF(5).
(3, 4.7) wouldn't be a valid point on either curve, since 4.7 is not
an integer and thus not a member of either field.
What about (3, -1)? This one is on the curve, but remember we're in
some GF(p). So in GF(7), -1 is actually 6. That means (3, -1) and (3,
6) are the same point. In GF(5), -1 is 4, so blah blah blah you get
what I'm saying.
Okay: if these points are going to form a group analogous to the
multiplicative integers mod p, we need to have an analogous set of
primitive functions to work with them.
1. In the multiplicative integers mod p, we combined two elements by
multiplying them together and taking the remainder modulo p.
We combine elliptic curve points by adding them. We'll talk about
what that means in a hot second.
2. We used 1 as a multiplicative identity: y * 1 = y for all y.
On an elliptic curve, we define the identity O as an abstract
"point at infinity" that doesn't map to any actual (x, y)
pair. This might feel like a bit of a hack, but it works.
On the curve, we have the straightforward rule that P + O = P for
all P.
In your code, you can just write something like O := object(),
since it only ever gets used in pointer comparisons. Or you can use
some sentinel coordinate that doesn't satisfy the curve equation;
(0, 1) is popular.
3. We had a modinv function to invert an integer mod p. This acted as
a stand-in for division. Given y, it finds x such that y * x = 1.
Inversion is way easier in elliptic curves. Just flip the sign on
y, and remember that we're in GF(p):
invert((x, y)) = (x, -y) = (x, p-y)
Just like with multiplicative inverses, we have this rule on
elliptic curves:
P + (-P) = P + invert(P) = O
Incidentally, these primitives, along with a finite set of elements,
are all we need to define a finite cyclic group, which is all we need
to define the Diffie-Hellman function. Not important to understand the
abstract jargon, just FYI.
Let's talk about addition. Here it is:
function add(P1, P2):
if P1 = O:
return P2
if P2 = O:
return P1
if P1 = invert(P2):
return O
x1, y1 := P1
x2, y2 := P2
if P1 = P2:
m := (3*x1^2 + a) / 2*y1
else:
m := (y2 - y1) / (x2 - x1)
x3 := m^2 - x1 - x2
y3 := m*(x1 - x3) - y1
return (x3, y3)
The first three checks are simple - they pretty much just implement
the rules we have for the identity and inversion.
After that we, uh, use math. You can read more about that part
elsewhere, if you're interested. It's not too important to us, but it
(sort of) makes sense in the context of those graphs I'm not showing
you.
There's one more thing we need. In the multiplicative integers, we
expressed repeated multiplication as exponentiation, e.g.:
y * y * y * y * y = y^5
We implemented this using a modexp function that walked the bits of
the exponent with a square-and-multiply inner loop.
On elliptic curves, we'll use scalar multiplication to express
repeated addition, e.g.:
P + P + P + P + P = 5*P
Don't be confused by the shared notation: scalar multiplication is not
analogous to multiplication in the integers. It's analogous to
exponentiation.
Your scalarmult function will look pretty much exactly the same as
your modexp function, except with the primitives swapped out.
Actually, you wanna hear something great? You could define a generic
scale function parameterized over a group that works as a drop-in
implementation for both. Like this:
function scale(x, k):
result := identity
while k > 0:
if odd(k):
result := combine(result, x)
x := combine(x, x)
k := k >> 1
return result
The combine function would delegate to modular multiplication or
elliptic curve point depending on the group. It's kind of like the
definition of a group constitutes a kind of interface, and we have
these two different implementations we can swap out freely.
To extend this metaphor, here's a generic Diffie-Hellman:
function generate_keypair():
secret := random(1, baseorder)
public := scale(base, secret)
return (secret, public)
function compute_secret(peer_public, self_secret):
return scale(peer_public, self_secret)
Simplicity itself! The base and baseorder attributes map to g and q in
the multiplicative integer setting. It's pretty much the same on a
curve: we'll have a base point G and its order n such that:
n*G = O
The fact that these two settings share so many similarities (and can
even share a naive implementation) is great news. It means we already
have a lot of the tools we need to reason about (and attack) elliptic
curves!
Let's put this newfound knowledge into action. Implement a set of
functions up to and including elliptic curve scalar
multiplication. (Remember that all computations are in GF(p), i.e. mod
p.) You can use this curve:
y^2 = x^3 - 95051*x + 11279326
Over GF(233970423115425145524320034830162017933). Use this base point:
(182, 85518893674295321206118380980485522083)
It has order 29246302889428143187362802287225875743.
Oh yeah, order. Finding the order of an elliptic curve group turns out
to be a bit tricky, so just trust me when I tell you this one has
order 233970423115425145498902418297807005944. That factors to 2^3 *
29246302889428143187362802287225875743.
Note: it's totally possible to pick an elliptic curve group whose
order is just a straight-up prime number. This would mean that every
point on the curve (except the identity) would have the same order,
since the group order would have no other divisors. The NIST P-curves
are like this.
Our curve has almost-prime order. There's just that small cofactor of
2^3, which is beneficial for reasons we'll cover later. Don't worry
about it for now.
If your implementation works correctly, it should be easy to verify:
remember that multiplying the base point by its order should yield the
group identity.
Implement ECDH and verify that you can do a handshake correctly. In
this case, Alice and Bob's secrets will be scalars modulo the base
point order and their public elements will be points. If you
implemented the primitives correctly, everything should "just work".
Next, reconfigure your protocol from #57 to use it.
Can we apply the subgroup-confinement attacks from #57 in this
setting? At first blush, it seems like it will be pretty difficult,
since the cofactor is so small. We can recover, like, three bits by
sending a point with order 8, but that's about it. There just aren't
enough small-order points on the curve.
How about not on the curve?
Wait, what? Yeah, points *not* on the curve. Look closer at our
combine function. Notice anything missing? The b parameter of the
curve is not accounted for anywhere. This is because we have four
inputs to the calculation: the curve parameters (a, b) and the point
coordinates (x, y). Given any three, you can calculate the fourth. In
other words, we don't need b because b is already baked into every
valid (x, y) pair.
There's a dangerous assumption there: namely, that the peer will
submit a valid (x, y) pair. If Eve can submit an invalid pair, that
really opens up her play: now she can pick points from any curve that
differs only in its b parameter. All she has to do is find some curves
with small subgroups and cherry-pick a few points of small
order. Alice will unwittingly compute the shared secret on the wrong
curve and leak a few bits of her private key in the process.
How do we find suitable curves? Well, remember that I mentioned
counting points on elliptic curves is tricky. If you're very brave,
you can implement Schoof-Elkies-Atkins. Or you can use a computer
algebra system like SageMath. Or you can just use these curves I
generated for you:
y^2 = x^3 - 95051*x + 210
y^2 = x^3 - 95051*x + 504
y^2 = x^3 - 95051*x + 727
They have orders:
233970423115425145550826547352470124412
233970423115425145544350131142039591210
233970423115425145545378039958152057148
They should have a fair few small factors between them. So: find some
points of small order and send them to Alice. You can use the same
trick from before to find points of some prime order r. Suppose the
group has order q. Pick some random point and multiply by q/r. If you
land on the identity, start over.
It might not be immediately obvious how to choose random points, but
you can just pick an x and calculate y. This will require you to
implement a modular square root algorithm; use Tonelli-Shanks, it's
pretty straightforward.
Implement the key-recovery attack from #57 using small-order points
from invalid curves.
// ------------------------------------------------------------
60. Single-Coordinate Ladders and Insecure Twists
All our hard work is about to pay some dividends. Here's a list of
cool-kids jargon you'll be able to deploy after completing this
challenge:
* Montgomery curve
* single-coordinate ladder
* isomorphism
* birational equivalence
* quadratic twist
* trace of Frobenius
Not that you'll understand it all; you won't. But you'll at least be
able to silence crypto-dilettantes on Twitter.
Now, to the task at hand. In the last problem, we implemented ECDH
using a short Weierstrass curve form, like this:
y^2 = x^3 + a*x + b
For a long time, this has been the most popular curve form. The NIST
P-curves standardized in the 90s look like this. It's what you'll see
first in most elliptic curve tutorials (including this one).
We can do a lot better. Meet the Montgomery curve:
B*v^2 = u^3 + A*u^2 + u
Although it's almost as old as the Weierstrass form, it's been buried
in the literature until somewhat recently. The Montgomery curve has a
killer feature in the form of a simple and efficient algorithm to
compute scalar multiplication: the Montgomery ladder.
Here's the ladder:
function ladder(u, k):
u2, w2 := (1, 0)
u3, w3 := (u, 1)
for i in reverse(range(bitlen(p))):
b := 1 & (k >> i)
u2, u3 := cswap(u2, u3, b)
w2, w3 := cswap(w2, w3, b)
u3, w3 := ((u2*u3 - w2*w3)^2,
u * (u2*w3 - w2*u3)^2)
u2, w2 := ((u2^2 - w2^2)^2,
4*u2*w2 * (u2^2 + A*u2*w2 + w2^2))
u2, u3 := cswap(u2, u3, b)
w2, w3 := cswap(w2, w3, b)
return u2 * w2^(p-2)
You are not expected to understand this.
No, really! Most people don't understand it. Instead, they visit the
Explicit-Formulas Database (https://www.hyperelliptic.org/EFD/), the
one-stop shop for state-of-the-art ECC implementation techniques. It's
like cheat codes for elliptic curves. Worth visiting for the
bibliography alone.
With that said, we should try to demystify this a little bit. Here's
the CliffsNotes:
1. Points on a Montgomery curve are (u, v) pairs, but this function
only takes u as an input. Given *just* the u coordinate of a point
P, this function computes *just* the u coordinate of k*P. Since we
only care about u, this is a single-coordinate ladder.
2. So what the heck is w? It's part of an alternate point
representation. Instead of a coordinate u, we have a coordinate
u/w. Think of it as a way to defer expensive division (read:
inversion) operations until the very end.
3. cswap is a function that swaps its first two arguments (or not)
depending on whether its third argument is one or zero. Choosy
implementers choose arithmetic implementations of cswap, not
branching ones.
4. The core of the inner loop is a differential addition followed by a
doubling operation. Differential addition means we can add two
points P and Q only if we already know P - Q. We'll take this
difference to be the input u and maintain it as an invariant
throughout the ladder. Indeed, our two initial points are:
u2, w2 := (1, 0)
u3, w3 := (u, 1)
Representing the identity and the input u.
5. The return statement performs the modular inversion using a trick
due to Fermat's Little Theorem:
a^p = a mod p
a^(p-1) = 1 mod p
a^(p-2) = a^-1 mod p
6. A consequence of the Montgomery ladder is that we conflate (u, v)
and (u, -v). But this encoding also conflates zero and
infinity. Both are represented as zero. Note that the usual
exceptional case where w = 0 is handled gracefully: our trick for
doing the inversion with exponentiation outputs zero as expected.
This is fine: we're still working in a subgroup of prime order.
Go ahead and implement the ladder. Remember that all computations are
in GF(233970423115425145524320034830162017933).
Oh yeah, the curve parameters. You might be thinking that since we're
switching to a new curve format, we also need to pick out a whole new
curve. But you'd be totally wrong! It turns out that some short
Weierstrass curves can be converted into Montgomery curves.
This is because all finite cyclic groups with an equal number of
elements share a kind of equivalence we call "isomorphism". It makes
sense, if you think about it - if the order is the same, all the same
subgroups will be present, and in the same proportions.
So all we need to do is:
1. Find a Montgomery curve with an equal order to our curve.
2. Figure out how to map points back and forth between curves.
You can perform this conversion algebraically. But it's kind of a
pain, so here you go:
v^2 = u^3 + 534*u^2 + u
Through cunning and foresight, I have chosen this curve specifically
to have a really simple map between Weierstrass and Montgomery
forms. Here it is:
u = x - 178
v = y
Which makes our base point:
(4, 85518893674295321206118380980485522083)
Or, you know. Just 4.
Anyway, implement the ladder. Verify ladder(4, n) = 0. Map some points
back and forth between your Weierstrass and Montgomery representations
and verify them.
One nice thing about the Montgomery ladder is its lack of special
cases. Specifically, no special handling of: P1 = O; P2 = O; P1 = P2;
or P1 = -P2. Contrast that with our Weierstrass addition function and
its battalion of ifs.
And there's a security benefit, too: by ignoring the v coordinate, we
take away a lot of leeway from the attacker. Recall that the ability
to choose arbitrary (x, y) pairs let them cherry-pick points from any
curve they can think of. The single-coordinate ladder robs the
attacker of that freedom.
But hang on a tick! Give this a whirl:
ladder(76600469441198017145391791613091732004, 11)
What the heck? What's going on here?
Let's do a quick sanity check. Here's the curve equation again:
v^2 = u^3 + 534*u^2 + u
Plug in u and take the square root to recover v.
You should detect that something is quite wrong. This u does not
represent a point on our curve! Not every u does.
This means that even though we can only submit one coordinate, we
still have a little bit of leeway to find invalid
points. Specifically, an input u such that u^3 + 534*u^2 + u is not a
quadratic residue can never represent a point on our curve. So where
the heck are we?
The other curve we're on is a sister curve called a "quadratic twist",
or simply "the twist". There is actually a whole family of quadratic
twists to our curve, but they're all isomorphic to each
other. Remember that that means they have the same number of points,
the same subgroups, etc. So it doesn't really matter which particular
twist we use; in fact, we don't even need to pick one.
We're mostly interested in the subgroups present on the twist, which
means we need to know how many points it contains. Fortunately, it
turns out to be easier to count the combined set of points on the
curve and its twist at the same time. Let's do it:
1. For every nonzero u up to the modulus p, if u^3 + A*u^2 + u is a
square in GF(p), there are two points on the original curve.
2. If the above sum is a nonsquare in GF(p), there are two points on
the twisted curve.
It should be clear that these add up to 2*(p-1) points in total, since
there are p-1 nonzero integers in GF(p) and two points for each. Let's
continue:
3. Both the original curve and its twist have a point (0, 0). This is
just a regular point, not the group identity.
4. Both the original curve and its twist have an abstract point at
infinity which serves as the group identity.
So we have 2*p + 2 points across both curves. Since we already know
how many points are on the original curve, we can easily calculate the
order of the twist.
If Alice chose a curve with an insecure twist, i.e. one with a
partially smooth order, then some doors open back up for Eve. She can
choose low-order points on the twisted curve, send them to Alice, and
perform the invalid-curve attack as before.
The only caveat is that she won't be able to recover the full secret
using off-curve points, only a fraction of it. But we know how to
handle that.
So:
1. Calculate the order of the twist and find its small factors. This
one should have a bunch under 2^24.
2. Find points with those orders. This is simple:
a. Choose a random u mod p and verify that u^3 + A*u^2 + u is a
nonsquare in GF(p).
b. Call the order of the twist n. To find an element of order q,
calculate ladder(u, n/q).
3. Send these points to Alice to recover portions of her secret.
4. When you've exhausted all the small subgroups in the twist, recover
the remainder of Alice's secret with the kangaroo attack.
HINT: You may come to notice that k*u = -k*u, resulting in a
combinatorial explosion of potential CRT outputs. Try sending extra
queries to narrow the range of possibilities.
// ------------------------------------------------------------
61. Duplicate-Signature Key Selection in ECDSA (and RSA)
Suppose you have a message-signature pair. If I give you a public key
that verifies the signature, can you trust that I'm the author?
You shouldn't. It turns out to be pretty easy to solve this problem
across a variety of digital signature schemes. If you have a little
flexibility in choosing your public key, that is.
Let's consider the case of ECDSA.
First, implement ECDSA. If you still have your old DSA implementation
lying around, this should be straightforward. All the same, here's a
refresher if you need it:
function sign(m, d):
k := random_scalar(1, n)
r := (k * G).x
s := (H(m) + d*r) * k^-1
return (r, s)
function verify(m, (r, s), Q):
u1 := H(m) * s^-1
u2 := r * s^-1
R := u1*G + u2*Q
return r = R.x
Remember that all the scalar operations are mod n, the order of the
base point G. (d, Q) is the signer's key pair. H(m) is a hash of the
message.
Note that the verification function requires arbitrary point
addition. This means your Montgomery ladder (which only performs
scalar multiplication) won't work here. This is no big deal; just fall
back to your old Weierstrass imlpementation.
Once you've got this implemented, generate a key pair for Alice and
use it to sign some message m.
It would be tough for Eve to find a Q' to verify this signature if all
the domain parameters are fixed. But the domain parameters might not
be fixed - some protocols let the user specify them as part of their
public key.
Let's rearrange some terms. Consider this equality:
R = u1*G + u2*Q
Let's do some regrouping:
R = u1*G + u2*(d*G)
R = (u1 + u2*d)*G
Consider R, u1, and u2 to be fixed. That leaves Alice's secret d and
the base point G. Since we don't know d, we'll need to choose a new
pair of values for which the equality holds. We can do it by starting
from the secret and working backwards.
1. Choose a random d' mod n.
2. Calculate t := u1 + u2*d'.
3. Calculate G' := t^-1 * R.
4. Calculate Q' := d' * G'.
5. Eve's public key is Q' with domain parameters (E(GF(p)), n, G').
E(GF(p)) is the elliptic curve Alice originally chose.
Note that Eve's public key is totally valid: both the base point and
her public point are members of the subgroup of prime order n. Since
E(GF(p)) and n are unchanged from Alice's public key, they should pass
the same validation rules.
Assuming the role of Eve, derive a public key and domain parameters to
verify Alice's signature over the message.
Let's do the same thing with RSA. Same setup: we have some message and
a signature over it. How do we craft a public key to verify the
signature?
Well, first let's refresh ourselves on RSA. Signature verification
looks like this:
s^e = pad(m) mod N
Where (m, s) is the message-signature pair and (e, N) is Alice's
public key.
So what we're really looking for is the pair (e', N') to make that
equality hold up. If this is starting to look a little familiar, it
should: what we're doing here is looking for the discrete logarithm of
pad(m) with base s.
We know discrete logarithms are easy to solve with Pohlig-Hellman in
groups with many small subgroups. And the choice of group is up to us,
so we can't fail!
But we should exercise some care. If we choose our primes incorrectly,
the discrete logarithm won't exist.
Okay, check the method:
1. Pick a prime p. Here are some conditions for p:
a. p-1 should be smooth. How smooth is up to you, but you will need
to find discrete logarithms in each of these subgroups. You can
use something like Shanks or Pollard's rho to compute these in
square-root time.
b. s shouldn't be in any subgroup that pad(m) is not in. If it is,
the discrete logarithm won't exist. The simplest thing to do is
make sure they're both primitive roots. To check if an element g
is a primitive root mod p, check that:
g^((p-1)/q) != 1 mod p
For every factor q of p-1.
2. Now pick a prime q. Ensure the same conditions as before, but add these:
a. Don't reuse any factors of p-1 other than 2. It's possible to
make this work with repeated factors, but it's a huge
headache. Better just to avoid it.
b. Make sure p*q is greater than Alice's modulus N. This is just to
make sure the signature and padded message will fit under your
new modulus.
3. Use Pohlig-Hellman to derive ep = e' mod p and eq = e' mod q.
4. Use the Chinese Remainder Theorem to put ep and eq together:
e' = crt([ep, eq], [p-1, q-1])
5. Your public modulus is N' = p * q.
6. You can derive d' in the normal fashion.
Easy as pie. e' will be a lot larger than the typical public exponent,
but that's still legal.
Since RSA signing and decryption are equivalent operations, you can
use this same technique for other surprising results. Try generating a
random (or chosen) ciphertext and creating a key to decrypt it to a
plaintext of your choice!
// ------------------------------------------------------------
62. Key-Recovery Attacks on ECDSA with Biased Nonces
Back in set 6 we saw how "nonce" is kind of a misnomer for the k value
in DSA. It's really more like an ephemeral key. And distressingly, the
security of your long-term private key hinges on it.
Nonce disclosure? Congrats, you just coughed up your secret key.
Predictable nonce? Ditto.
Even by repeating a nonce you lose everything.
How far can we take this? Turns out, pretty far: even a slight bias in
nonce generation is enough for an attacker to recover your private
key. Let's see how.
First, let's clarify what we mean by a "biased" nonce. All we really
need for this attack is knowledge of a few bits of the nonce. For
simplicity, let's say the low byte of each nonce is zero. So take
whatever code you were using for nonce generation and just mask off
the eight least significant bits.
How does this help us? Let's review the signing algorithm:
function sign(m, d):
k := random_scalar(1, q)
r := (k * G).x
s := (H(m) + d*r) * k^-1