1
2
3
4
5 package rsa
6
7 import (
8 "bufio"
9 "crypto/internal/fips140/bigmod"
10 "encoding/hex"
11 "fmt"
12 "math/big"
13 "os"
14 "strings"
15 "testing"
16 )
17
18 func TestMillerRabin(t *testing.T) {
19 f, err := os.Open("testdata/miller_rabin_tests.txt")
20 if err != nil {
21 t.Fatal(err)
22 }
23
24 var expected bool
25 var W, B string
26 var lineNum int
27 scanner := bufio.NewScanner(f)
28 for scanner.Scan() {
29 lineNum++
30 line := scanner.Text()
31 if len(line) == 0 || line[0] == '#' {
32 continue
33 }
34
35 k, v, _ := strings.Cut(line, " = ")
36 switch k {
37 case "Result":
38 switch v {
39 case "Composite":
40 expected = millerRabinCOMPOSITE
41 case "PossiblyPrime":
42 expected = millerRabinPOSSIBLYPRIME
43 default:
44 t.Fatalf("unknown result %q on line %d", v, lineNum)
45 }
46 case "W":
47 W = v
48 case "B":
49 B = v
50
51 t.Run(fmt.Sprintf("line %d", lineNum), func(t *testing.T) {
52 if len(W)%2 != 0 {
53 W = "0" + W
54 }
55 for len(B) < len(W) {
56 B = "0" + B
57 }
58
59
60 w := decodeHex(t, W)
61 if len(w) == 0 || w[len(w)-1]%4 != 3 {
62 t.Skip("skipping test with W not congruent to 3 mod 4")
63 }
64
65 mr, err := millerRabinSetup(w)
66 if err != nil {
67 t.Logf("W = %s", W)
68 t.Logf("B = %s", B)
69 t.Fatalf("failed to set up Miller-Rabin test: %v", err)
70 }
71
72 result, err := millerRabinIteration(mr, decodeHex(t, B))
73 if err != nil {
74 t.Logf("W = %s", W)
75 t.Logf("B = %s", B)
76 t.Fatalf("failed to run Miller-Rabin test: %v", err)
77 }
78
79 if result != expected {
80 t.Logf("W = %s", W)
81 t.Logf("B = %s", B)
82 t.Fatalf("unexpected result: got %v, want %v", result, expected)
83 }
84 })
85 default:
86 t.Fatalf("unknown key %q on line %d", k, lineNum)
87 }
88 }
89 if err := scanner.Err(); err != nil {
90 t.Fatal(err)
91 }
92 }
93
94 func TestTotient(t *testing.T) {
95 f, err := os.Open("testdata/gcd_lcm_tests.txt")
96 if err != nil {
97 t.Fatal(err)
98 }
99
100 var GCD, A, B, LCM string
101 var lineNum int
102 scanner := bufio.NewScanner(f)
103 for scanner.Scan() {
104 lineNum++
105 line := scanner.Text()
106 if len(line) == 0 || line[0] == '#' {
107 continue
108 }
109
110 k, v, _ := strings.Cut(line, " = ")
111 switch k {
112 case "GCD":
113 GCD = v
114 case "A":
115 A = v
116 case "B":
117 B = v
118 case "LCM":
119 LCM = v
120
121 t.Run(fmt.Sprintf("line %d", lineNum), func(t *testing.T) {
122 if A == "0" || B == "0" {
123 t.Skip("skipping test with zero input")
124 }
125 if LCM == "1" {
126 t.Skip("skipping test with LCM=1")
127 }
128
129 p, _ := bigmod.NewModulus(addOne(decodeHex(t, A)))
130 a, _ := bigmod.NewNat().SetBytes(decodeHex(t, A), p)
131 q, _ := bigmod.NewModulus(addOne(decodeHex(t, B)))
132 b, _ := bigmod.NewNat().SetBytes(decodeHex(t, B), q)
133
134 gcd, err := bigmod.NewNat().GCDVarTime(a, b)
135
136 if err == nil {
137 if got := strings.TrimLeft(hex.EncodeToString(gcd.Bytes(p)), "0"); got != GCD {
138 t.Fatalf("unexpected GCD: got %s, want %s", got, GCD)
139 }
140 }
141
142
143 if a.Bits()[0]%4 != 2 || b.Bits()[0]%4 != 2 {
144 t.Skip("skipping test with invalid input for totient")
145 }
146
147 lcm, err := totient(p, q)
148 if new(big.Int).SetBytes(decodeHex(t, GCD)).BitLen() > 32 {
149 if err != errDivisorTooLarge {
150 t.Fatalf("expected divisor too large error, got %v", err)
151 }
152 t.Skip("GCD too large")
153 }
154 if err != nil {
155 t.Fatalf("failed to calculate totient: %v", err)
156 }
157 if got := strings.TrimLeft(hex.EncodeToString(lcm.Nat().Bytes(lcm)), "0"); got != LCM {
158 t.Fatalf("unexpected LCM: got %s, want %s", got, LCM)
159 }
160 })
161 default:
162 t.Fatalf("unknown key %q on line %d", k, lineNum)
163 }
164 }
165 if err := scanner.Err(); err != nil {
166 t.Fatal(err)
167 }
168 }
169
170 func addOne(b []byte) []byte {
171 x := new(big.Int).SetBytes(b)
172 x.Add(x, big.NewInt(1))
173 return x.Bytes()
174 }
175
176 func decodeHex(t *testing.T, s string) []byte {
177 t.Helper()
178 if len(s)%2 != 0 {
179 s = "0" + s
180 }
181 b, err := hex.DecodeString(s)
182 if err != nil {
183 t.Fatalf("failed to decode hex %q: %v", s, err)
184 }
185 return b
186 }
187
View as plain text