Skip to content

Commit f7475a0

Browse files
FiloSottilegopherbot
authored andcommitted
crypto/internal/fips140/bigmod: add Nat.InverseVarTime
Will be needed for RSA key generation. We now require Modulus to be > 1 because we don't want to worry about 1 being out of range. There is no use for a Modulus of 1 anyway, and we already return an error from NewModulus. Ported from https://cs.opensource.google/boringssl/boringssl/+/master:crypto/fipsmodule/bn/gcd_extra.cc.inc;drc=5813c2c10c73d800f1b0d890a7d74ff973abbffc. Updates #69799 For #69536 Change-Id: I9850bcc461565b23fa7186a09c65355f7da3e5ba Reviewed-on: https://go-review.googlesource.com/c/go/+/632415 Auto-Submit: Filippo Valsorda <[email protected]> Reviewed-by: Daniel McCarney <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Roland Shoemaker <[email protected]> Reviewed-by: Russ Cox <[email protected]>
1 parent 22b5c14 commit f7475a0

File tree

3 files changed

+325
-4
lines changed

3 files changed

+325
-4
lines changed

src/crypto/internal/fips140/bigmod/nat.go

Lines changed: 130 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -473,15 +473,16 @@ func minusInverseModW(x uint) uint {
473473
return -y
474474
}
475475

476-
// NewModulus creates a new Modulus from a slice of big-endian bytes.
476+
// NewModulus creates a new Modulus from a slice of big-endian bytes. The
477+
// modulus must be greater than one.
477478
//
478479
// The number of significant bits and whether the modulus is even is leaked
479480
// through timing side-channels.
480481
func NewModulus(b []byte) (*Modulus, error) {
481482
m := &Modulus{}
482483
m.nat = NewNat().resetToBytes(b)
483-
if len(m.nat.limbs) == 0 {
484-
return nil, errors.New("modulus must be > 0")
484+
if m.nat.IsZero() == yes || m.nat.IsOne() == yes {
485+
return nil, errors.New("modulus must be > 1")
485486
}
486487
m.leading = _W - bitLen(m.nat.limbs[len(m.nat.limbs)-1])
487488
if m.nat.IsOdd() == 1 {
@@ -963,3 +964,129 @@ func (out *Nat) ExpShortVarTime(x *Nat, e uint, m *Modulus) *Nat {
963964
}
964965
return out.montgomeryReduction(m)
965966
}
967+
968+
// InverseVarTime calculates x = a⁻¹ mod m and returns (x, true) if a is
969+
// invertible. Otherwise, InverseVarTime returns (x, false) and x is not
970+
// modified.
971+
//
972+
// a must be reduced modulo m, but doesn't need to have the same size. The
973+
// output will be resized to the size of m and overwritten.
974+
func (x *Nat) InverseVarTime(a *Nat, m *Modulus) (*Nat, bool) {
975+
// This is the extended binary GCD algorithm described in the Handbook of
976+
// Applied Cryptography, Algorithm 14.61, adapted by BoringSSL to bound
977+
// coefficients and avoid negative numbers. For more details and proof of
978+
// correctness, see https://github.com/mit-plv/fiat-crypto/pull/333/files.
979+
//
980+
// Following the proof linked in the PR above, the changes are:
981+
//
982+
// 1. Negate [B] and [C] so they are positive. The invariant now involves a
983+
// subtraction.
984+
// 2. If step 2 (both [x] and [y] are even) runs, abort immediately. This
985+
// algorithm only cares about [x] and [y] relatively prime.
986+
// 3. Subtract copies of [x] and [y] as needed in step 6 (both [u] and [v]
987+
// are odd) so coefficients stay in bounds.
988+
// 4. Replace the [u >= v] check with [u > v]. This changes the end
989+
// condition to [v = 0] rather than [u = 0]. This saves an extra
990+
// subtraction due to which coefficients were negated.
991+
// 5. Rename x and y to a and n, to capture that one is a modulus.
992+
// 6. Rearrange steps 4 through 6 slightly. Merge the loops in steps 4 and
993+
// 5 into the main loop (step 7's goto), and move step 6 to the start of
994+
// the loop iteration, ensuring each loop iteration halves at least one
995+
// value.
996+
//
997+
// Note this algorithm does not handle either input being zero.
998+
999+
if a.IsZero() == yes {
1000+
return x, false
1001+
}
1002+
if a.IsOdd() == no && !m.odd {
1003+
// a and m are not coprime, as they are both even.
1004+
return x, false
1005+
}
1006+
1007+
u := NewNat().set(a).ExpandFor(m)
1008+
v := m.Nat()
1009+
1010+
A := NewNat().reset(len(m.nat.limbs))
1011+
A.limbs[0] = 1
1012+
B := NewNat().reset(len(a.limbs))
1013+
C := NewNat().reset(len(m.nat.limbs))
1014+
D := NewNat().reset(len(a.limbs))
1015+
D.limbs[0] = 1
1016+
1017+
// Before and after each loop iteration, the following hold:
1018+
//
1019+
// u = A*a - B*m
1020+
// v = D*m - C*a
1021+
// 0 < u <= a
1022+
// 0 <= v <= m
1023+
// 0 <= A < m
1024+
// 0 <= B <= a
1025+
// 0 <= C < m
1026+
// 0 <= D <= a
1027+
//
1028+
// After each loop iteration, u and v only get smaller, and at least one of
1029+
// them shrinks by at least a factor of two.
1030+
for {
1031+
// If both u and v are odd, subtract the smaller from the larger.
1032+
// If u = v, we need to subtract from v to hit the modified exit condition.
1033+
if u.IsOdd() == yes && v.IsOdd() == yes {
1034+
if v.cmpGeq(u) == no {
1035+
u.sub(v)
1036+
A.Add(C, m)
1037+
B.Add(D, &Modulus{nat: a})
1038+
} else {
1039+
v.sub(u)
1040+
C.Add(A, m)
1041+
D.Add(B, &Modulus{nat: a})
1042+
}
1043+
}
1044+
1045+
// Exactly one of u and v is now even.
1046+
if u.IsOdd() == v.IsOdd() {
1047+
panic("bigmod: internal error: u and v are not in the expected state")
1048+
}
1049+
1050+
// Halve the even one and adjust the corresponding coefficient.
1051+
if u.IsOdd() == no {
1052+
rshift1(u, 0)
1053+
if A.IsOdd() == yes || B.IsOdd() == yes {
1054+
rshift1(A, A.add(m.nat))
1055+
rshift1(B, B.add(a))
1056+
} else {
1057+
rshift1(A, 0)
1058+
rshift1(B, 0)
1059+
}
1060+
} else { // v.IsOdd() == no
1061+
rshift1(v, 0)
1062+
if C.IsOdd() == yes || D.IsOdd() == yes {
1063+
rshift1(C, C.add(m.nat))
1064+
rshift1(D, D.add(a))
1065+
} else {
1066+
rshift1(C, 0)
1067+
rshift1(D, 0)
1068+
}
1069+
}
1070+
1071+
if v.IsZero() == yes {
1072+
if u.IsOne() == no {
1073+
return x, false
1074+
}
1075+
return x.set(A), true
1076+
}
1077+
}
1078+
}
1079+
1080+
func rshift1(a *Nat, carry uint) {
1081+
size := len(a.limbs)
1082+
aLimbs := a.limbs[:size]
1083+
1084+
for i := range size {
1085+
aLimbs[i] >>= 1
1086+
if i+1 < size {
1087+
aLimbs[i] |= aLimbs[i+1] << (_W - 1)
1088+
} else {
1089+
aLimbs[i] |= carry << (_W - 1)
1090+
}
1091+
}
1092+
}

src/crypto/internal/fips140/bigmod/nat_test.go

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
package bigmod
66

77
import (
8+
"bufio"
89
"bytes"
910
cryptorand "crypto/rand"
11+
"encoding/hex"
1012
"fmt"
1113
"math/big"
1214
"math/bits"
1315
"math/rand"
16+
"os"
1417
"reflect"
1518
"slices"
1619
"strings"
@@ -632,7 +635,7 @@ func BenchmarkExp(b *testing.B) {
632635
}
633636

634637
func TestNewModulus(t *testing.T) {
635-
expected := "modulus must be > 0"
638+
expected := "modulus must be > 1"
636639
_, err := NewModulus([]byte{})
637640
if err == nil || err.Error() != expected {
638641
t.Errorf("NewModulus(0) got %q, want %q", err, expected)
@@ -645,6 +648,14 @@ func TestNewModulus(t *testing.T) {
645648
if err == nil || err.Error() != expected {
646649
t.Errorf("NewModulus(0) got %q, want %q", err, expected)
647650
}
651+
_, err = NewModulus([]byte{1})
652+
if err == nil || err.Error() != expected {
653+
t.Errorf("NewModulus(1) got %q, want %q", err, expected)
654+
}
655+
_, err = NewModulus([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1})
656+
if err == nil || err.Error() != expected {
657+
t.Errorf("NewModulus(1) got %q, want %q", err, expected)
658+
}
648659
}
649660

650661
func makeTestValue(nbits int) []uint {
@@ -683,3 +694,71 @@ func TestAddMulVVWSized(t *testing.T) {
683694
})
684695
}
685696
}
697+
698+
func TestInverse(t *testing.T) {
699+
f, err := os.Open("testdata/mod_inv_tests.txt")
700+
if err != nil {
701+
t.Fatal(err)
702+
}
703+
704+
var ModInv, A, M string
705+
var lineNum int
706+
scanner := bufio.NewScanner(f)
707+
for scanner.Scan() {
708+
lineNum++
709+
line := scanner.Text()
710+
if len(line) == 0 || line[0] == '#' {
711+
continue
712+
}
713+
714+
k, v, _ := strings.Cut(line, " = ")
715+
switch k {
716+
case "ModInv":
717+
ModInv = v
718+
case "A":
719+
A = v
720+
case "M":
721+
M = v
722+
723+
t.Run(fmt.Sprintf("line %d", lineNum), func(t *testing.T) {
724+
m, err := NewModulus(decodeHex(t, M))
725+
if err != nil {
726+
t.Skip("modulus <= 1")
727+
}
728+
a, err := NewNat().SetBytes(decodeHex(t, A), m)
729+
if err != nil {
730+
t.Fatal(err)
731+
}
732+
733+
got, ok := NewNat().InverseVarTime(a, m)
734+
if !ok {
735+
t.Fatal("not invertible")
736+
}
737+
exp, err := NewNat().SetBytes(decodeHex(t, ModInv), m)
738+
if err != nil {
739+
t.Fatal(err)
740+
}
741+
if got.Equal(exp) != 1 {
742+
t.Errorf("%v != %v", got, exp)
743+
}
744+
})
745+
default:
746+
t.Fatalf("unknown key %q on line %d", k, lineNum)
747+
}
748+
}
749+
if err := scanner.Err(); err != nil {
750+
t.Fatal(err)
751+
}
752+
}
753+
754+
func decodeHex(t *testing.T, s string) []byte {
755+
t.Helper()
756+
if len(s)%2 != 0 {
757+
s = "0" + s
758+
}
759+
b, err := hex.DecodeString(s)
760+
if err != nil {
761+
t.Fatalf("failed to decode hex %q: %v", s, err)
762+
}
763+
return b
764+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# ModInv tests.
2+
#
3+
# These test vectors satisfy ModInv * A = 1 (mod M) and 0 <= ModInv < M.
4+
5+
ModInv = 00
6+
A = 00
7+
M = 01
8+
9+
ModInv = 00
10+
A = 01
11+
M = 01
12+
13+
ModInv = 00
14+
A = 02
15+
M = 01
16+
17+
ModInv = 00
18+
A = 03
19+
M = 01
20+
21+
ModInv = 64
22+
A = 54
23+
M = e3
24+
25+
ModInv = 13
26+
A = 2b
27+
M = 30
28+
29+
ModInv = 2f
30+
A = 30
31+
M = 37
32+
33+
ModInv = 4
34+
A = 13
35+
M = 4b
36+
37+
ModInv = 1c47
38+
A = cd4
39+
M = 6a21
40+
41+
ModInv = 2b97
42+
A = 8e7
43+
M = 49c0
44+
45+
ModInv = 29b9
46+
A = fcb
47+
M = 3092
48+
49+
ModInv = a83
50+
A = 14bf
51+
M = 41ae
52+
53+
ModInv = 18f15fe1
54+
A = 11b5d53e
55+
M = 322e92a1
56+
57+
ModInv = 32f9453b
58+
A = 8af6df6
59+
M = 33d45eb7
60+
61+
ModInv = d696369
62+
A = c5f89dd5
63+
M = fc09c17c
64+
65+
ModInv = 622839d8
66+
A = 60c2526
67+
M = 74200493
68+
69+
ModInv = fb5a8aee7bbc4ef
70+
A = 24ebd835a70be4e2
71+
M = 9c7256574e0c5e93
72+
73+
ModInv = 846bc225402419c
74+
A = 23026003ab1fbdb
75+
M = 1683cbe32779c59b
76+
77+
ModInv = 5ff84f63a78982f9
78+
A = 4a2420dc733e1a0f
79+
M = a73c6bfabefa09e6
80+
81+
ModInv = 133e74d28ef42b43
82+
A = 2e9511ae29cdd41
83+
M = 15234df99f19fcda
84+
85+
ModInv = 46ae1fabe9521e4b99b198fc8439609023aa69be2247c0d1e27c2a0ea332f9c5
86+
A = 6331fec5f01014046788c919ed50dc86ac7a80c085f1b6f645dd179c0f0dc9cd
87+
M = 8ef409de82318259a8655a39293b1e762fa2cc7e0aeb4c59713a1e1fff6af640
88+
89+
ModInv = 444ccea3a7b21677dd294d34de53cc8a5b51e69b37782310a00fc6bcc975709b
90+
A = 679280bd880994c08322143a4ea8a0825d0466fda1bb6b3eb86fc8e90747512b
91+
M = e4fecab84b365c63a0dab4244ce3f921a9c87ec64d69a2031939f55782e99a2e
92+
93+
ModInv = 1ac7d7a03ceec5f690f567c9d61bf3469c078285bcc5cf00ac944596e887ca17
94+
A = 1593ef32d9c784f5091bdff952f5c5f592a3aed6ba8ea865efa6d7df87be1805
95+
M = 1e276882f90c95e0c1976eb079f97af075445b1361c02018d6bd7191162e67b2
96+
97+
ModInv = 639108b90dfe946f498be21303058413bbb0e59d0bd6a6115788705abd0666d6
98+
A = 9258d6238e4923d120b2d1033573ffcac691526ad0842a3b174dccdbb79887bd
99+
M = ce62909c39371d463aaba3d4b72ea6da49cb9b529e39e1972ef3ccd9a66fe08f
100+
101+
ModInv = aebde7654cb17833a106231c4b9e2f519140e85faee1bfb4192830f03f385e773c0f4767e93e874ffdc3b7a6b7e6a710e5619901c739ee8760a26128e8c91ef8cf761d0e505d8b28ae078d17e6071c372893bb7b72538e518ebc57efa70b7615e406756c49729b7c6e74f84aed7a316b6fa748ff4b9f143129d29dad1bff98bb
102+
A = a29dacaf5487d354280fdd2745b9ace4cd50f2bde41d0ee529bf26a1913244f708085452ff32feab19a7418897990da46a0633f7c8375d583367319091bbbe069b0052c5e48a7daac9fb650db5af768cd2508ec3e2cda7456d4b9ce1c39459627a8b77e038b826cd7e326d0685b0cd0cb50f026f18300dae9f5fd42aa150ee8b
103+
M = d686f9b86697313251685e995c09b9f1e337ddfaa050bd2df15bf4ca1dc46c5565021314765299c434ea1a6ec42bf92a29a7d1ffff599f4e50b79a82243fb24813060580c770d4c1140aeb2ab2685007e948b6f1f62e8001a0545619477d498132c907774479f6d95899e6251e7136f79ab6d3b7c82e4aca421e7d22fe7db19c
104+
105+
ModInv = 1ec872f4f20439e203597ca4de9d1296743f95781b2fe85d5def808558bbadef02a46b8955f47c83e1625f8bb40228eab09cad2a35c9ad62ab77a30e3932872959c5898674162da244a0ec1f68c0ed89f4b0f3572bfdc658ad15bf1b1c6e1176b0784c9935bd3ff1f49bb43753eacee1d8ca1c0b652d39ec727da83984fe3a0f
106+
A = 2e527b0a1dc32460b2dd94ec446c692989f7b3c7451a5cbeebf69fc0ea9c4871fbe78682d5dc5b66689f7ed889b52161cd9830b589a93d21ab26dbede6c33959f5a0f0d107169e2daaac78bac8cf2d41a1eb1369cb6dc9e865e73bb2e51b886f4e896082db199175e3dde0c4ed826468f238a77bd894245d0918efc9ca84f945
107+
M = b13133a9ebe0645f987d170c077eea2aa44e85c9ab10386d02867419a590cb182d9826a882306c212dbe75225adde23f80f5b37ca75ed09df20fc277cc7fbbfac8d9ef37a50f6b68ea158f5447283618e64e1426406d26ea85232afb22bf546c75018c1c55cb84c374d58d9d44c0a13ba88ac2e387765cb4c3269e3a983250fa
108+
109+
ModInv = 30ffa1876313a69de1e4e6ee132ea1d3a3da32f3b56f5cfb11402b0ad517dce605cf8e91d69fa375dd887fa8507bd8a28b2d5ce745799126e86f416047709f93f07fbd88918a047f13100ea71b1d48f6fc6d12e5c917646df3041b302187af641eaedf4908abc36f12c204e1526a7d80e96e302fb0779c28d7da607243732f26
110+
A = 31157208bde6b85ebecaa63735947b3b36fa351b5c47e9e1c40c947339b78bf96066e5dbe21bb42629e6fcdb81f5f88db590bfdd5f4c0a6a0c3fc6377e5c1fd8235e46e291c688b6d6ecfb36604891c2a7c9cbcc58c26e44b43beecb9c5044b58bb58e35de3cf1128f3c116534fe4e421a33f83603c3df1ae36ec88092f67f2a
111+
M = 53408b23d6cb733e6c9bc3d1e2ea2286a5c83cc4e3e7470f8af3a1d9f28727f5b1f8ae348c1678f5d1105dc3edf2de64e65b9c99545c47e64b770b17c8b4ef5cf194b43a0538053e87a6b95ade1439cebf3d34c6aa72a11c1497f58f76011e16c5be087936d88aba7a740113120e939e27bd3ddcb6580c2841aa406566e33c35
112+
113+
ModInv = 87355002f305c81ba0dc97ca2234a2bc02528cefde38b94ac5bd95efc7bf4c140899107fff47f0df9e3c6aa70017ebc90610a750f112cd4f475b9c76b204a953444b4e7196ccf17e93fdaed160b7345ca9b397eddf9446e8ea8ee3676102ce70eaafbe9038a34639789e6f2f1e3f352638f2e8a8f5fc56aaea7ec705ee068dd5
114+
A = 42a25d0bc96f71750f5ac8a51a1605a41b506cca51c9a7ecf80cad713e56f70f1b4b6fa51cbb101f55fd74f318adefb3af04e0c8a7e281055d5a40dd40913c0e1211767c5be915972c73886106dc49325df6c2df49e9eea4536f0343a8e7d332c6159e4f5bdb20d89f90e67597c4a2a632c31b2ef2534080a9ac61f52303990d
115+
M = d3d3f95d50570351528a76ab1e806bae1968bd420899bdb3d87c823fac439a4354c31f6c888c939784f18fe10a95e6d203b1901caa18937ba6f8be033af10c35fc869cf3d16bef479f280f53b3499e645d0387554623207ca4989e5de00bfeaa5e9ab56474fc60dd4967b100e0832eaaf2fcb2ef82a181567057b880b3afef62

0 commit comments

Comments
 (0)