From: Sergey Matveev Date: Sat, 26 Nov 2016 09:22:27 +0000 (+0300) Subject: VKO 34.10-2012 support. DigestSizeX incompatible changes X-Git-Tag: 2.0~4 X-Git-Url: http://www.git.cypherpunks.ru/?p=gogost.git;a=commitdiff_plain;h=92e0d038de014c8b3eebc0beba3ef9fe906dd8e3 VKO 34.10-2012 support. DigestSizeX incompatible changes * DigestSizeX is renamed to ModeX because it is not related to digest size, but parameters and key sizes * PrivateKey.KEK is renamed to PrivateKey.KEK2001 and KEK2012256/KEK2012512 are added too * KEK functions take big.Int UKM value. Use NewUKM to unmarshal raw binary UKM --- diff --git a/README b/README index 1f4e6ba..98dc76b 100644 --- a/README +++ b/README @@ -10,7 +10,8 @@ GOST is GOvernment STandard of Russian Federation (and Soviet Union). * GOST R 34.10-2001 (RFC 5832) public key signature function * GOST R 34.10-2012 (RFC 7091) public key signature function * various 34.10 curve parameters included -* VKO GOST R 34.10-2001 Diffie-Hellman function (RFC 4357) +* VKO GOST R 34.10-2001 key agreement function (RFC 4357) +* VKO GOST R 34.10-2012 key agreement function (RFC 7836) * GOST R 34.12-2015 128-bit block cipher Кузнечик (Kuznechik) (RFC 7801) * GOST R 34.13-2015 padding methods diff --git a/src/cypherpunks.ru/gogost/gost3410/2001_test.go b/src/cypherpunks.ru/gogost/gost3410/2001_test.go index 21553b4..3c13122 100644 --- a/src/cypherpunks.ru/gogost/gost3410/2001_test.go +++ b/src/cypherpunks.ru/gogost/gost3410/2001_test.go @@ -19,7 +19,6 @@ package gost3410 import ( "bytes" "crypto/rand" - "encoding/hex" "testing" "testing/quick" ) @@ -64,7 +63,7 @@ func TestRFCVectors(t *testing.T) { if err != nil { t.FailNow() } - prv, err := NewPrivateKey(c, DigestSize2001, priv) + prv, err := NewPrivateKey(c, Mode2001, priv) if err != nil { t.FailNow() } @@ -97,7 +96,7 @@ func TestRandom2001(t *testing.T) { f := func(data [31]byte, digest [32]byte) bool { prv, err := NewPrivateKey( c, - DigestSize2001, + Mode2001, append([]byte{0xde}, data[:]...), ) if err != nil { @@ -108,7 +107,7 @@ func TestRandom2001(t *testing.T) { return false } pubRaw := pub.Raw() - pub, err = NewPublicKey(c, DigestSize2001, pubRaw) + pub, err = NewPublicKey(c, Mode2001, pubRaw) if err != nil { return false } @@ -129,7 +128,7 @@ func TestRandom2001(t *testing.T) { func BenchmarkSign2001(b *testing.B) { c, _ := NewCurveFromParams(CurveParamsGostR34102001Test) - prv, err := GenPrivateKey(c, DigestSize2001, rand.Reader) + prv, err := GenPrivateKey(c, Mode2001, rand.Reader) if err != nil { b.FailNow() } @@ -143,7 +142,7 @@ func BenchmarkSign2001(b *testing.B) { func BenchmarkVerify2001(b *testing.B) { c, _ := NewCurveFromParams(CurveParamsGostR34102001Test) - prv, err := GenPrivateKey(c, DigestSize2001, rand.Reader) + prv, err := GenPrivateKey(c, Mode2001, rand.Reader) if err != nil { b.FailNow() } @@ -162,45 +161,3 @@ func BenchmarkVerify2001(b *testing.B) { pub.VerifyDigest(digest, sign) } } - -func TestVKO(t *testing.T) { - c, _ := NewCurveFromParams(CurveParamsGostR34102001Test) - ukm, _ := hex.DecodeString("33a252f825be7251") - prvRaw1, _ := hex.DecodeString("1df129e43dab345b68f6a852f4162dc69f36b2f84717d08755cc5c44150bf928") - prvRaw2, _ := hex.DecodeString("5b9356c6474f913f1e83885ea0edd5df1a43fd9d799d219093241157ac9ed473") - kek, _ := hex.DecodeString("ee4618a0dbb10cb31777b4b86a53d9e7ef6cb3e400101410f0c0f2af46c494a6") - prv1, _ := NewPrivateKey(c, DigestSize2001, prvRaw1) - prv2, _ := NewPrivateKey(c, DigestSize2001, prvRaw2) - pub1, _ := prv1.PublicKey() - pub2, _ := prv2.PublicKey() - kek1, _ := prv1.KEK(pub2, ukm) - kek2, _ := prv2.KEK(pub1, ukm) - if bytes.Compare(kek1, kek2) != 0 { - t.FailNow() - } - if bytes.Compare(kek1, kek) != 0 { - t.FailNow() - } -} - -func TestRandomVKO(t *testing.T) { - c, _ := NewCurveFromParams(CurveParamsGostR34102001Test) - f := func(prvRaw1 [32]byte, prvRaw2 [32]byte, ukm [8]byte) bool { - prv1, err := NewPrivateKey(c, DigestSize2001, prvRaw1[:]) - if err != nil { - return false - } - prv2, err := NewPrivateKey(c, DigestSize2001, prvRaw2[:]) - if err != nil { - return false - } - pub1, _ := prv1.PublicKey() - pub2, _ := prv2.PublicKey() - kek1, _ := prv1.KEK(pub2, ukm[:]) - kek2, _ := prv2.KEK(pub1, ukm[:]) - return bytes.Compare(kek1, kek2) == 0 - } - if err := quick.Check(f, nil); err != nil { - t.Error(err) - } -} diff --git a/src/cypherpunks.ru/gogost/gost3410/2012_test.go b/src/cypherpunks.ru/gogost/gost3410/2012_test.go index fc2d61a..2e82847 100644 --- a/src/cypherpunks.ru/gogost/gost3410/2012_test.go +++ b/src/cypherpunks.ru/gogost/gost3410/2012_test.go @@ -146,7 +146,7 @@ func TestGCL3Vectors(t *testing.T) { if err != nil { t.FailNow() } - prv, err := NewPrivateKey(c, DigestSize2012, priv) + prv, err := NewPrivateKey(c, Mode2012, priv) if err != nil { t.FailNow() } @@ -179,7 +179,7 @@ func TestRandom2012(t *testing.T) { f := func(data [31]byte, digest [64]byte) bool { prv, err := NewPrivateKey( c, - DigestSize2012, + Mode2012, append([]byte{0xde}, data[:]...), ) if err != nil { @@ -190,7 +190,7 @@ func TestRandom2012(t *testing.T) { return false } pubRaw := pub.Raw() - pub, err = NewPublicKey(c, DigestSize2012, pubRaw) + pub, err = NewPublicKey(c, Mode2012, pubRaw) if err != nil { return false } @@ -211,7 +211,7 @@ func TestRandom2012(t *testing.T) { func BenchmarkSign2012(b *testing.B) { c, _ := NewCurveFromParams(CurveParamsGostR34102012TC26ParamSetA) - prv, err := GenPrivateKey(c, DigestSize2012, rand.Reader) + prv, err := GenPrivateKey(c, Mode2012, rand.Reader) if err != nil { b.FailNow() } @@ -225,7 +225,7 @@ func BenchmarkSign2012(b *testing.B) { func BenchmarkVerify2012(b *testing.B) { c, _ := NewCurveFromParams(CurveParamsGostR34102012TC26ParamSetA) - prv, err := GenPrivateKey(c, DigestSize2012, rand.Reader) + prv, err := GenPrivateKey(c, Mode2012, rand.Reader) if err != nil { b.FailNow() } diff --git a/src/cypherpunks.ru/gogost/gost3410/doc.go b/src/cypherpunks.ru/gogost/gost3410/doc.go index 2c7a398..1d8e4ad 100644 --- a/src/cypherpunks.ru/gogost/gost3410/doc.go +++ b/src/cypherpunks.ru/gogost/gost3410/doc.go @@ -1,2 +1,5 @@ -// GOST R 34.10-2001 (RFC 5832) and 34.10-2012 (RFC 7091) signature algorithm. +// GOST R 34.10-2001 (RFC 5832), +// GOST R 34.10-2012 (RFC 7091) signature algorithms and +// VKO GOST R 34.10-2001 (RFC 4357), +// VKO GOST R 34.10-2012 (RFC 7836) key agreement algorithms. package gost3410 diff --git a/src/cypherpunks.ru/gogost/gost3410/params.go b/src/cypherpunks.ru/gogost/gost3410/params.go index 052acc6..fea7645 100644 --- a/src/cypherpunks.ru/gogost/gost3410/params.go +++ b/src/cypherpunks.ru/gogost/gost3410/params.go @@ -16,14 +16,14 @@ package gost3410 -type DigestSize uint8 +type Mode int // Curve params: p, q, a, b, bx, by type CurveParams [6][]byte var ( - DigestSize2001 DigestSize = 32 - DigestSize2012 DigestSize = 64 + Mode2001 Mode = Mode(32) + Mode2012 Mode = Mode(64) CurveParamsGostR34102001cc CurveParams = CurveParams([6][]byte{ {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/src/cypherpunks.ru/gogost/gost3410/private.go b/src/cypherpunks.ru/gogost/gost3410/private.go index 709e61b..d4c5c1e 100644 --- a/src/cypherpunks.ru/gogost/gost3410/private.go +++ b/src/cypherpunks.ru/gogost/gost3410/private.go @@ -20,60 +20,57 @@ import ( "errors" "io" "math/big" - - "cypherpunks.ru/gogost/gost28147" - "cypherpunks.ru/gogost/gost341194" ) type PrivateKey struct { - c *Curve - ds int - key *big.Int + c *Curve + mode Mode + key *big.Int } -func NewPrivateKey(curve *Curve, ds DigestSize, raw []byte) (*PrivateKey, error) { - key := make([]byte, len(raw)) +func NewPrivateKey(curve *Curve, mode Mode, raw []byte) (*PrivateKey, error) { + if len(raw) != int(mode) { + errors.New("Invalid private key length") + } + key := make([]byte, int(mode)) copy(key, raw) reverse(key) k := bytes2big(key) if k.Cmp(zero) == 0 { - return nil, errors.New("zero private key") + return nil, errors.New("Zero private key") } - return &PrivateKey{curve, int(ds), k}, nil + return &PrivateKey{curve, mode, k}, nil } -func GenPrivateKey(curve *Curve, ds DigestSize, rand io.Reader) (*PrivateKey, error) { - raw := make([]byte, int(ds)) +func GenPrivateKey(curve *Curve, mode Mode, rand io.Reader) (*PrivateKey, error) { + raw := make([]byte, int(mode)) if _, err := io.ReadFull(rand, raw); err != nil { return nil, err } - return NewPrivateKey(curve, ds, raw) + return NewPrivateKey(curve, mode, raw) } -func (pk *PrivateKey) Raw() []byte { - raw := pad(pk.key.Bytes(), pk.ds) +func (prv *PrivateKey) Raw() []byte { + raw := pad(prv.key.Bytes(), int(prv.mode)) reverse(raw) return raw } -func (pk *PrivateKey) PublicKey() (*PublicKey, error) { - x, y, err := pk.c.Exp(pk.key, pk.c.Bx, pk.c.By) +func (prv *PrivateKey) PublicKey() (*PublicKey, error) { + x, y, err := prv.c.Exp(prv.key, prv.c.Bx, prv.c.By) if err != nil { return nil, err } - return &PublicKey{pk.c, pk.ds, x, y}, nil + return &PublicKey{prv.c, prv.mode, x, y}, nil } -func (pk *PrivateKey) SignDigest(digest []byte, rand io.Reader) ([]byte, error) { - if len(digest) != pk.ds { - return nil, errors.New("Invalid input digest length") - } +func (prv *PrivateKey) SignDigest(digest []byte, rand io.Reader) ([]byte, error) { e := bytes2big(digest) - e.Mod(e, pk.c.Q) + e.Mod(e, prv.c.Q) if e.Cmp(zero) == 0 { e = big.NewInt(1) } - kRaw := make([]byte, pk.ds) + kRaw := make([]byte, int(prv.mode)) var err error var k *big.Int var r *big.Int @@ -84,53 +81,27 @@ Retry: return nil, err } k = bytes2big(kRaw) - k.Mod(k, pk.c.Q) + k.Mod(k, prv.c.Q) if k.Cmp(zero) == 0 { goto Retry } - r, _, err = pk.c.Exp(k, pk.c.Bx, pk.c.By) + r, _, err = prv.c.Exp(k, prv.c.Bx, prv.c.By) if err != nil { return nil, err } - r.Mod(r, pk.c.Q) + r.Mod(r, prv.c.Q) if r.Cmp(zero) == 0 { goto Retry } - d.Mul(pk.key, r) + d.Mul(prv.key, r) k.Mul(k, e) s.Add(d, k) - s.Mod(s, pk.c.Q) + s.Mod(s, prv.c.Q) if s.Cmp(zero) == 0 { goto Retry } - return append(pad(s.Bytes(), pk.ds), pad(r.Bytes(), pk.ds)...), nil -} - -// Make Diffie-Hellman computation. Key Encryption Key calculation. -// UKM is user keying material, also called VKO-factor, 8-bytes long. -// It is based on RFC 4357 VKO GOST R 34.10-2001 with little-endian hash -// output. -func (pk *PrivateKey) KEK(pub *PublicKey, ukm []byte) ([]byte, error) { - if len(ukm) != 8 { - return nil, errors.New("UKM must be 8 bytes long") - } - keyX, keyY, err := pk.c.Exp(pk.key, pub.x, pub.y) - if err != nil { - return nil, err - } - t := make([]byte, DigestSize2001) - copy(t[int(DigestSize2001)-len(ukm):], ukm) - keyX, keyY, err = pk.c.Exp(bytes2big(t), keyX, keyY) - if err != nil { - return nil, err - } - h := gost341194.New(&gost28147.GostR3411_94_CryptoProParamSet) - copy(t, pad(keyX.Bytes(), int(DigestSize2001))) - reverse(t) - h.Write(t) - copy(t, pad(keyY.Bytes(), int(DigestSize2001))) - reverse(t) - h.Write(t) - t = h.Sum(t[:0]) - return t, nil + return append( + pad(s.Bytes(), int(prv.mode)), + pad(r.Bytes(), int(prv.mode))..., + ), nil } diff --git a/src/cypherpunks.ru/gogost/gost3410/public.go b/src/cypherpunks.ru/gogost/gost3410/public.go index e886502..c3d1dce 100644 --- a/src/cypherpunks.ru/gogost/gost3410/public.go +++ b/src/cypherpunks.ru/gogost/gost3410/public.go @@ -22,84 +22,84 @@ import ( ) type PublicKey struct { - c *Curve - ds int - x *big.Int - y *big.Int + c *Curve + mode Mode + x *big.Int + y *big.Int } -func NewPublicKey(curve *Curve, ds DigestSize, raw []byte) (*PublicKey, error) { - if len(raw) != 2*int(ds) { +func NewPublicKey(curve *Curve, mode Mode, raw []byte) (*PublicKey, error) { + if len(raw) != 2*int(mode) { return nil, errors.New("Invalid public key length") } - key := make([]byte, 2*int(ds)) + key := make([]byte, 2*int(mode)) copy(key, raw) reverse(key) return &PublicKey{ curve, - int(ds), - bytes2big(key[int(ds) : 2*int(ds)]), - bytes2big(key[:int(ds)]), + mode, + bytes2big(key[int(mode) : 2*int(mode)]), + bytes2big(key[:int(mode)]), }, nil } -func (pk *PublicKey) Raw() []byte { - raw := append(pad(pk.y.Bytes(), pk.ds), pad(pk.x.Bytes(), pk.ds)...) +func (pub *PublicKey) Raw() []byte { + raw := append( + pad(pub.y.Bytes(), int(pub.mode)), + pad(pub.x.Bytes(), int(pub.mode))..., + ) reverse(raw) return raw } -func (pk *PublicKey) VerifyDigest(digest, signature []byte) (bool, error) { - if len(digest) != pk.ds { - return false, errors.New("Invalid input digest length") - } - if len(signature) != 2*pk.ds { +func (pub *PublicKey) VerifyDigest(digest, signature []byte) (bool, error) { + if len(signature) != 2*int(pub.mode) { return false, errors.New("Invalid signature length") } - s := bytes2big(signature[:pk.ds]) - r := bytes2big(signature[pk.ds:]) - if r.Cmp(zero) <= 0 || r.Cmp(pk.c.Q) >= 0 || s.Cmp(zero) <= 0 || s.Cmp(pk.c.Q) >= 0 { + s := bytes2big(signature[:pub.mode]) + r := bytes2big(signature[pub.mode:]) + if r.Cmp(zero) <= 0 || r.Cmp(pub.c.Q) >= 0 || s.Cmp(zero) <= 0 || s.Cmp(pub.c.Q) >= 0 { return false, nil } e := bytes2big(digest) - e.Mod(e, pk.c.Q) + e.Mod(e, pub.c.Q) if e.Cmp(zero) == 0 { e = big.NewInt(1) } v := big.NewInt(0) - v.ModInverse(e, pk.c.Q) + v.ModInverse(e, pub.c.Q) z1 := big.NewInt(0) z2 := big.NewInt(0) z1.Mul(s, v) - z1.Mod(z1, pk.c.Q) + z1.Mod(z1, pub.c.Q) z2.Mul(r, v) - z2.Mod(z2, pk.c.Q) - z2.Sub(pk.c.Q, z2) - p1x, p1y, err := pk.c.Exp(z1, pk.c.Bx, pk.c.By) + z2.Mod(z2, pub.c.Q) + z2.Sub(pub.c.Q, z2) + p1x, p1y, err := pub.c.Exp(z1, pub.c.Bx, pub.c.By) if err != nil { return false, err } - q1x, q1y, err := pk.c.Exp(z2, pk.x, pk.y) + q1x, q1y, err := pub.c.Exp(z2, pub.x, pub.y) if err != nil { return false, err } lm := big.NewInt(0) lm.Sub(q1x, p1x) if lm.Cmp(zero) < 0 { - lm.Add(lm, pk.c.P) + lm.Add(lm, pub.c.P) } - lm.ModInverse(lm, pk.c.P) + lm.ModInverse(lm, pub.c.P) z1.Sub(q1y, p1y) lm.Mul(lm, z1) - lm.Mod(lm, pk.c.P) + lm.Mod(lm, pub.c.P) lm.Mul(lm, lm) - lm.Mod(lm, pk.c.P) + lm.Mod(lm, pub.c.P) lm.Sub(lm, p1x) lm.Sub(lm, q1x) - lm.Mod(lm, pk.c.P) + lm.Mod(lm, pub.c.P) if lm.Cmp(zero) < 0 { - lm.Add(lm, pk.c.P) + lm.Add(lm, pub.c.P) } - lm.Mod(lm, pk.c.Q) + lm.Mod(lm, pub.c.Q) return lm.Cmp(r) == 0, nil } diff --git a/src/cypherpunks.ru/gogost/gost3410/ukm.go b/src/cypherpunks.ru/gogost/gost3410/ukm.go new file mode 100644 index 0000000..4295164 --- /dev/null +++ b/src/cypherpunks.ru/gogost/gost3410/ukm.go @@ -0,0 +1,28 @@ +// GoGOST -- Pure Go GOST cryptographic functions library +// Copyright (C) 2015-2016 Sergey Matveev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package gost3410 + +import ( + "math/big" +) + +func NewUKM(raw []byte) *big.Int { + t := make([]byte, len(raw)) + copy(t, raw) + reverse(t) + return bytes2big(t) +} diff --git a/src/cypherpunks.ru/gogost/gost3410/vko.go b/src/cypherpunks.ru/gogost/gost3410/vko.go new file mode 100644 index 0000000..b8d53d2 --- /dev/null +++ b/src/cypherpunks.ru/gogost/gost3410/vko.go @@ -0,0 +1,34 @@ +// GoGOST -- Pure Go GOST cryptographic functions library +// Copyright (C) 2015-2016 Sergey Matveev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package gost3410 + +import ( + "math/big" +) + +func (prv *PrivateKey) KEK(pub *PublicKey, ukm *big.Int) ([]byte, error) { + keyX, keyY, err := prv.c.Exp(prv.key, pub.x, pub.y) + if err != nil { + return nil, err + } + keyX, keyY, err = prv.c.Exp(ukm, keyX, keyY) + if err != nil { + return nil, err + } + pk := PublicKey{prv.c, prv.mode, keyX, keyY} + return pk.Raw(), nil +} diff --git a/src/cypherpunks.ru/gogost/gost3410/vko2001.go b/src/cypherpunks.ru/gogost/gost3410/vko2001.go new file mode 100644 index 0000000..85197a1 --- /dev/null +++ b/src/cypherpunks.ru/gogost/gost3410/vko2001.go @@ -0,0 +1,40 @@ +// GoGOST -- Pure Go GOST cryptographic functions library +// Copyright (C) 2015-2016 Sergey Matveev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package gost3410 + +import ( + "errors" + "math/big" + + "cypherpunks.ru/gogost/gost28147" + "cypherpunks.ru/gogost/gost341194" +) + +// RFC 4357 VKO GOST R 34.10-2001 key agreement function. +// UKM is user keying material, also called VKO-factor. +func (prv *PrivateKey) KEK2001(pub *PublicKey, ukm *big.Int) ([]byte, error) { + if prv.mode != Mode2001 { + return nil, errors.New("KEK2001 can not be used in Mode2012") + } + key, err := prv.KEK(pub, ukm) + if err != nil { + return nil, err + } + h := gost341194.New(&gost28147.GostR3411_94_CryptoProParamSet) + h.Write(key) + return h.Sum(key[:0]), nil +} diff --git a/src/cypherpunks.ru/gogost/gost3410/vko2001_test.go b/src/cypherpunks.ru/gogost/gost3410/vko2001_test.go new file mode 100644 index 0000000..90a0855 --- /dev/null +++ b/src/cypherpunks.ru/gogost/gost3410/vko2001_test.go @@ -0,0 +1,68 @@ +// GoGOST -- Pure Go GOST cryptographic functions library +// Copyright (C) 2015-2016 Sergey Matveev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package gost3410 + +import ( + "bytes" + "encoding/hex" + "testing" + "testing/quick" +) + +func TestVKO2001(t *testing.T) { + c, _ := NewCurveFromParams(CurveParamsGostR34102001Test) + ukmRaw, _ := hex.DecodeString("5172be25f852a233") + ukm := NewUKM(ukmRaw) + prvRaw1, _ := hex.DecodeString("1df129e43dab345b68f6a852f4162dc69f36b2f84717d08755cc5c44150bf928") + prvRaw2, _ := hex.DecodeString("5b9356c6474f913f1e83885ea0edd5df1a43fd9d799d219093241157ac9ed473") + kek, _ := hex.DecodeString("ee4618a0dbb10cb31777b4b86a53d9e7ef6cb3e400101410f0c0f2af46c494a6") + prv1, _ := NewPrivateKey(c, Mode2001, prvRaw1) + prv2, _ := NewPrivateKey(c, Mode2001, prvRaw2) + pub1, _ := prv1.PublicKey() + pub2, _ := prv2.PublicKey() + kek1, _ := prv1.KEK2001(pub2, ukm) + kek2, _ := prv2.KEK2001(pub1, ukm) + if bytes.Compare(kek1, kek2) != 0 { + t.FailNow() + } + if bytes.Compare(kek1, kek) != 0 { + t.FailNow() + } +} + +func TestRandomVKO2001(t *testing.T) { + c, _ := NewCurveFromParams(CurveParamsGostR34102001Test) + f := func(prvRaw1 [32]byte, prvRaw2 [32]byte, ukmRaw [8]byte) bool { + prv1, err := NewPrivateKey(c, Mode2001, prvRaw1[:]) + if err != nil { + return false + } + prv2, err := NewPrivateKey(c, Mode2001, prvRaw2[:]) + if err != nil { + return false + } + pub1, _ := prv1.PublicKey() + pub2, _ := prv2.PublicKey() + ukm := NewUKM(ukmRaw[:]) + kek1, _ := prv1.KEK2001(pub2, ukm) + kek2, _ := prv2.KEK2001(pub1, ukm) + return bytes.Compare(kek1, kek2) == 0 + } + if err := quick.Check(f, nil); err != nil { + t.Error(err) + } +} diff --git a/src/cypherpunks.ru/gogost/gost3410/vko2012.go b/src/cypherpunks.ru/gogost/gost3410/vko2012.go new file mode 100644 index 0000000..d12b51b --- /dev/null +++ b/src/cypherpunks.ru/gogost/gost3410/vko2012.go @@ -0,0 +1,55 @@ +// GoGOST -- Pure Go GOST cryptographic functions library +// Copyright (C) 2015-2016 Sergey Matveev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package gost3410 + +import ( + "errors" + "math/big" + + "cypherpunks.ru/gogost/gost34112012256" + "cypherpunks.ru/gogost/gost34112012512" +) + +// RFC 7836 VKO GOST R 34.10-2012 256-bit key agreement function. +// UKM is user keying material, also called VKO-factor. +func (prv *PrivateKey) KEK2012256(pub *PublicKey, ukm *big.Int) ([]byte, error) { + if prv.mode != Mode2012 { + return nil, errors.New("KEK2012 can not be used in Mode2001") + } + key, err := prv.KEK(pub, ukm) + if err != nil { + return nil, err + } + h := gost34112012256.New() + h.Write(key) + return h.Sum(key[:0]), nil +} + +// RFC 7836 VKO GOST R 34.10-2012 512-bit key agreement function. +// UKM is user keying material, also called VKO-factor. +func (prv *PrivateKey) KEK2012512(pub *PublicKey, ukm *big.Int) ([]byte, error) { + if prv.mode != Mode2012 { + return nil, errors.New("KEK2012 can not be used in Mode2001") + } + key, err := prv.KEK(pub, ukm) + if err != nil { + return nil, err + } + h := gost34112012512.New() + h.Write(key) + return h.Sum(key[:0]), nil +} diff --git a/src/cypherpunks.ru/gogost/gost3410/vko2012_test.go b/src/cypherpunks.ru/gogost/gost3410/vko2012_test.go new file mode 100644 index 0000000..4d5630e --- /dev/null +++ b/src/cypherpunks.ru/gogost/gost3410/vko2012_test.go @@ -0,0 +1,116 @@ +// GoGOST -- Pure Go GOST cryptographic functions library +// Copyright (C) 2015-2016 Sergey Matveev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package gost3410 + +import ( + "bytes" + "encoding/hex" + "testing" + "testing/quick" +) + +func TestVKO2012256(t *testing.T) { + c, _ := NewCurveFromParams(CurveParamsGostR34102012TC26ParamSetA) + ukmRaw, _ := hex.DecodeString("1d80603c8544c727") + ukm := NewUKM(ukmRaw) + prvRawA, _ := hex.DecodeString("c990ecd972fce84ec4db022778f50fcac726f46708384b8d458304962d7147f8c2db41cef22c90b102f2968404f9b9be6d47c79692d81826b32b8daca43cb667") + pubRawA, _ := hex.DecodeString("aab0eda4abff21208d18799fb9a8556654ba783070eba10cb9abb253ec56dcf5d3ccba6192e464e6e5bcb6dea137792f2431f6c897eb1b3c0cc14327b1adc0a7914613a3074e363aedb204d38d3563971bd8758e878c9db11403721b48002d38461f92472d40ea92f9958c0ffa4c93756401b97f89fdbe0b5e46e4a4631cdb5a") + prvRawB, _ := hex.DecodeString("48c859f7b6f11585887cc05ec6ef1390cfea739b1a18c0d4662293ef63b79e3b8014070b44918590b4b996acfea4edfbbbcccc8c06edd8bf5bda92a51392d0db") + pubRawB, _ := hex.DecodeString("192fe183b9713a077253c72c8735de2ea42a3dbc66ea317838b65fa32523cd5efca974eda7c863f4954d1147f1f2b25c395fce1c129175e876d132e94ed5a65104883b414c9b592ec4dc84826f07d0b6d9006dda176ce48c391e3f97d102e03bb598bf132a228a45f7201aba08fc524a2d77e43a362ab022ad4028f75bde3b79") + pubA, _ := NewPublicKey(c, Mode2012, pubRawA) + pubB, _ := NewPublicKey(c, Mode2012, pubRawB) + kek, _ := hex.DecodeString("c9a9a77320e2cc559ed72dce6f47e2192ccea95fa648670582c054c0ef36c221") + prvA, _ := NewPrivateKey(c, Mode2012, prvRawA) + prvB, _ := NewPrivateKey(c, Mode2012, prvRawB) + kekA, _ := prvA.KEK2012256(pubB, ukm) + kekB, _ := prvB.KEK2012256(pubA, ukm) + if bytes.Compare(kekA, kekB) != 0 { + t.FailNow() + } + if bytes.Compare(kekA, kek) != 0 { + t.FailNow() + } +} + +func TestRandomVKO2012256(t *testing.T) { + c, _ := NewCurveFromParams(CurveParamsGostR34102012TC26ParamSetA) + f := func(prvRaw1 [64]byte, prvRaw2 [64]byte, ukmRaw [8]byte) bool { + prv1, err := NewPrivateKey(c, Mode2012, prvRaw1[:]) + if err != nil { + return false + } + prv2, err := NewPrivateKey(c, Mode2012, prvRaw2[:]) + if err != nil { + return false + } + pub1, _ := prv1.PublicKey() + pub2, _ := prv2.PublicKey() + ukm := NewUKM(ukmRaw[:]) + kek1, _ := prv1.KEK2012256(pub2, ukm) + kek2, _ := prv2.KEK2012256(pub1, ukm) + return bytes.Compare(kek1, kek2) == 0 + } + if err := quick.Check(f, nil); err != nil { + t.Error(err) + } +} + +func TestVKO2012512(t *testing.T) { + c, _ := NewCurveFromParams(CurveParamsGostR34102012TC26ParamSetA) + ukmRaw, _ := hex.DecodeString("1d80603c8544c727") + ukm := NewUKM(ukmRaw) + prvRawA, _ := hex.DecodeString("c990ecd972fce84ec4db022778f50fcac726f46708384b8d458304962d7147f8c2db41cef22c90b102f2968404f9b9be6d47c79692d81826b32b8daca43cb667") + pubRawA, _ := hex.DecodeString("aab0eda4abff21208d18799fb9a8556654ba783070eba10cb9abb253ec56dcf5d3ccba6192e464e6e5bcb6dea137792f2431f6c897eb1b3c0cc14327b1adc0a7914613a3074e363aedb204d38d3563971bd8758e878c9db11403721b48002d38461f92472d40ea92f9958c0ffa4c93756401b97f89fdbe0b5e46e4a4631cdb5a") + prvRawB, _ := hex.DecodeString("48c859f7b6f11585887cc05ec6ef1390cfea739b1a18c0d4662293ef63b79e3b8014070b44918590b4b996acfea4edfbbbcccc8c06edd8bf5bda92a51392d0db") + pubRawB, _ := hex.DecodeString("192fe183b9713a077253c72c8735de2ea42a3dbc66ea317838b65fa32523cd5efca974eda7c863f4954d1147f1f2b25c395fce1c129175e876d132e94ed5a65104883b414c9b592ec4dc84826f07d0b6d9006dda176ce48c391e3f97d102e03bb598bf132a228a45f7201aba08fc524a2d77e43a362ab022ad4028f75bde3b79") + pubA, _ := NewPublicKey(c, Mode2012, pubRawA) + pubB, _ := NewPublicKey(c, Mode2012, pubRawB) + kek, _ := hex.DecodeString("79f002a96940ce7bde3259a52e015297adaad84597a0d205b50e3e1719f97bfa7ee1d2661fa9979a5aa235b558a7e6d9f88f982dd63fc35a8ec0dd5e242d3bdf") + prvA, _ := NewPrivateKey(c, Mode2012, prvRawA) + prvB, _ := NewPrivateKey(c, Mode2012, prvRawB) + kekA, _ := prvA.KEK2012512(pubB, ukm) + kekB, _ := prvB.KEK2012512(pubA, ukm) + if bytes.Compare(kekA, kekB) != 0 { + t.FailNow() + } + if bytes.Compare(kekA, kek) != 0 { + t.FailNow() + } +} + +func TestRandomVKO2012512(t *testing.T) { + c, _ := NewCurveFromParams(CurveParamsGostR34102012TC26ParamSetA) + f := func(prvRaw1 [64]byte, prvRaw2 [64]byte, ukmRaw [8]byte) bool { + prv1, err := NewPrivateKey(c, Mode2012, prvRaw1[:]) + if err != nil { + return false + } + prv2, err := NewPrivateKey(c, Mode2012, prvRaw2[:]) + if err != nil { + return false + } + pub1, _ := prv1.PublicKey() + pub2, _ := prv2.PublicKey() + ukm := NewUKM(ukmRaw[:]) + kek1, _ := prv1.KEK2012512(pub2, ukm) + kek2, _ := prv2.KEK2012512(pub1, ukm) + return bytes.Compare(kek1, kek2) == 0 + } + if err := quick.Check(f, nil); err != nil { + t.Error(err) + } +} diff --git a/www.texi b/www.texi index 4f1f22c..f305cbf 100644 --- a/www.texi +++ b/www.texi @@ -36,8 +36,10 @@ Currently supported algorithms are: (@url{https://tools.ietf.org/html/rfc7091.html, RFC 7091}) public key signature function @item various 34.10 curve parameters included -@item VKO GOST R 34.10-2001 Diffie-Hellman function +@item VKO GOST R 34.10-2001 key agreement function (@url{https://tools.ietf.org/html/rfc4357.html, RFC 4357}) +@item VKO GOST R 34.10-2012 key agreement function + (@url{https://tools.ietf.org/html/rfc7836.html, RFC 7836}) @item GOST R 34.12-2015 128-bit block cipher Кузнечик (Kuznechik) (@url{https://tools.ietf.org/html/rfc7801.html, RFC 7801}) @item GOST R 34.13-2015 padding methods