Skip to content

Commit ff6b4ad

Browse files
committed
fix(cryptography): validate GOST verification inputs
1 parent 15b0860 commit ff6b4ad

2 files changed

Lines changed: 84 additions & 17 deletions

File tree

cryptography/curve.go

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,27 @@ func (c curve) mul(p point, k *big.Int) point {
5959
}
6060
return out
6161
}
62+
func (c curve) validPoint(p point) bool {
63+
if p.inf || p.x == nil || p.y == nil {
64+
return false
65+
}
66+
if p.x.Sign() < 0 || p.x.Cmp(c.p) >= 0 {
67+
return false
68+
}
69+
if p.y.Sign() < 0 || p.y.Cmp(c.p) >= 0 {
70+
return false
71+
}
72+
var left, right big.Int
73+
left.Mul(p.y, p.y)
74+
left.Mod(&left, c.p)
75+
right.Mul(p.x, p.x)
76+
right.Add(&right, c.a)
77+
right.Mul(&right, p.x)
78+
right.Add(&right, c.b)
79+
right.Mod(&right, c.p)
80+
return left.Cmp(&right) == 0 && c.mul(p, c.q).inf
81+
}
82+
6283
func (c curve) verify(public, digest, r, s []byte) bool {
6384
if len(public) != (c.p.BitLen()+7)/8*2 || len(r) != (c.p.BitLen()+7)/8 || len(s) != len(r) {
6485
return false
@@ -68,14 +89,20 @@ func (c curve) verify(public, digest, r, s []byte) bool {
6889
if rr.Sign() <= 0 || rr.Cmp(c.q) >= 0 || ss.Sign() <= 0 || ss.Cmp(c.q) >= 0 {
6990
return false
7091
}
71-
h := new(big.Int).Mod(new(big.Int).SetBytes(digest), c.q)
72-
inv := new(big.Int).ModInverse(h, c.q)
92+
pub := point{new(big.Int).SetBytes(public[:len(public)/2]), new(big.Int).SetBytes(public[len(public)/2:]), false}
93+
if !c.validPoint(pub) {
94+
return false
95+
}
96+
e := new(big.Int).Mod(new(big.Int).SetBytes(digest), c.q)
97+
if e.Sign() == 0 {
98+
e.SetInt64(1)
99+
}
100+
inv := new(big.Int).ModInverse(e, c.q)
73101
if inv == nil {
74102
return false
75103
}
76104
z1 := new(big.Int).Mod(new(big.Int).Mul(ss, inv), c.q)
77105
z2 := new(big.Int).Mod(new(big.Int).Mul(new(big.Int).Sub(c.q, rr), inv), c.q)
78-
pub := point{new(big.Int).SetBytes(public[:len(public)/2]), new(big.Int).SetBytes(public[len(public)/2:]), false}
79106
base := point{new(big.Int).Set(c.gx), new(big.Int).Set(c.gy), false}
80107
out := c.add(c.mul(base, z1), c.mul(pub, z2))
81108
if out.inf {

cryptography/curve_test.go

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,74 @@ import (
55
"testing"
66
)
77

8-
func TestGOST256VerificationMatchesRFC7091(t *testing.T) {
9-
testCurve := newCurve([6]string{
8+
func rfc7091TestCurve() curve {
9+
return newCurve([6]string{
1010
"7",
1111
"5FBFF498AA938CE739B8E022FBAFEF40563F6E6A3472FC2A514C0CE9DAE23B7E",
1212
"8000000000000000000000000000000000000000000000000000000000000431",
1313
"8000000000000000000000000000000150FE8A1892976154C59CFC193ACCF5B3",
1414
"2",
1515
"8E2A8A0E65147D4BD6316030E16D19C85C97F0A9CA267122B96ABBCEA7E8FC8",
1616
})
17-
decode := func(name, value string) []byte {
18-
t.Helper()
19-
decoded, err := hex.DecodeString(value)
20-
if err != nil {
21-
t.Fatalf("decode %s: %v", name, err)
22-
}
23-
return decoded
17+
}
18+
19+
func decodeGOSTVector(t *testing.T, name, value string) []byte {
20+
t.Helper()
21+
decoded, err := hex.DecodeString(value)
22+
if err != nil {
23+
t.Fatalf("decode %s: %v", name, err)
2424
}
25+
return decoded
26+
}
2527

28+
func TestGOST256VerificationMatchesRFC7091(t *testing.T) {
2629
// RFC 7091, Section 7, publishes the curve, digest scalar, public key,
2730
// and signature independently of this implementation.
28-
public := decode("public key",
31+
public := decodeGOSTVector(t, "public key",
2932
"7F2B49E270DB6D90D8595BEC458B50C58585BA1D4E9B788F6689DBD8E56FD80B"+
3033
"26F1B489D6701DD185C8413A977B3CBBAF64D1C593D26627DFFB101A87FF77DA")
31-
digest := decode("digest", "2DFBC1B372D89A1188C09C52E0EEC61FCE52032AB1022E8E67ECE6672B043EE5")
32-
r := decode("r", "41AA28D2F1AB148280CD9ED56FEDA41974053554A42767B83AD043FD39DC0493")
33-
s := decode("s", "01456C64BA4642A1653C235A98A60249BCD6D3F746B631DF928014F6C5BF9C40")
34+
digest := decodeGOSTVector(t, "digest", "2DFBC1B372D89A1188C09C52E0EEC61FCE52032AB1022E8E67ECE6672B043EE5")
35+
r := decodeGOSTVector(t, "r", "41AA28D2F1AB148280CD9ED56FEDA41974053554A42767B83AD043FD39DC0493")
36+
s := decodeGOSTVector(t, "s", "01456C64BA4642A1653C235A98A60249BCD6D3F746B631DF928014F6C5BF9C40")
3437

35-
if !testCurve.verify(public, digest, r, s) {
38+
if !rfc7091TestCurve().verify(public, digest, r, s) {
3639
t.Fatal("GOST R 34.10-2012 RFC 7091 vector verification failed")
3740
}
3841
}
42+
43+
func TestGOSTZeroDigestScalarBecomesOne(t *testing.T) {
44+
public := decodeGOSTVector(t, "public key",
45+
"7F2B49E270DB6D90D8595BEC458B50C58585BA1D4E9B788F6689DBD8E56FD80B"+
46+
"26F1B489D6701DD185C8413A977B3CBBAF64D1C593D26627DFFB101A87FF77DA")
47+
digest := make([]byte, 32)
48+
r := decodeGOSTVector(t, "r", "41AA28D2F1AB148280CD9ED56FEDA41974053554A42767B83AD043FD39DC0493")
49+
// RFC 7091's published d, k, and r with the Section 6.1 e = 1 rule.
50+
s := decodeGOSTVector(t, "s", "2101DCCCABE45DF9FEB8BAE91FB31A8872687A181C23587C3274CB3F88B4650C")
51+
52+
if !rfc7091TestCurve().verify(public, digest, r, s) {
53+
t.Fatal("GOST R 34.10-2012 zero digest scalar verification failed")
54+
}
55+
}
56+
57+
func TestGOSTRejectsOffCurvePublicKey(t *testing.T) {
58+
public := make([]byte, 64)
59+
digest := make([]byte, 32)
60+
r := make([]byte, 32)
61+
s := make([]byte, 32)
62+
digest[31], r[31], s[31] = 1, 1, 1
63+
64+
if curve256.verify(public, digest, r, s) {
65+
t.Fatal("GOST verification accepted the off-curve public point (0, 0)")
66+
}
67+
}
68+
69+
func TestGOSTConfiguredBasePointsAreValid(t *testing.T) {
70+
for name, configured := range map[string]curve{"256": curve256, "512": curve512} {
71+
t.Run(name, func(t *testing.T) {
72+
base := point{x: configured.gx, y: configured.gy}
73+
if !configured.validPoint(base) {
74+
t.Fatal("configured base point is not in the declared subgroup")
75+
}
76+
})
77+
}
78+
}

0 commit comments

Comments
 (0)