]> Cypherpunks.ru repositories - gogost.git/commitdiff
VKO 34.10-2012 support. DigestSizeX incompatible changes
authorSergey Matveev <stargrave@stargrave.org>
Sat, 26 Nov 2016 09:22:27 +0000 (12:22 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Sat, 26 Nov 2016 09:42:01 +0000 (12:42 +0300)
* 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

14 files changed:
README
src/cypherpunks.ru/gogost/gost3410/2001_test.go
src/cypherpunks.ru/gogost/gost3410/2012_test.go
src/cypherpunks.ru/gogost/gost3410/doc.go
src/cypherpunks.ru/gogost/gost3410/params.go
src/cypherpunks.ru/gogost/gost3410/private.go
src/cypherpunks.ru/gogost/gost3410/public.go
src/cypherpunks.ru/gogost/gost3410/ukm.go [new file with mode: 0644]
src/cypherpunks.ru/gogost/gost3410/vko.go [new file with mode: 0644]
src/cypherpunks.ru/gogost/gost3410/vko2001.go [new file with mode: 0644]
src/cypherpunks.ru/gogost/gost3410/vko2001_test.go [new file with mode: 0644]
src/cypherpunks.ru/gogost/gost3410/vko2012.go [new file with mode: 0644]
src/cypherpunks.ru/gogost/gost3410/vko2012_test.go [new file with mode: 0644]
www.texi

diff --git a/README b/README
index 1f4e6ba1ee1c74c41215e7ab3815fe6ffbed4385..98dc76bb2ff413dc9e3dc0058a054c6591986176 100644 (file)
--- 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
 
index 21553b4eca70dff952a065a5e00d042b26f27088..3c13122a28c71535190d843e602f8fe979762e5f 100644 (file)
@@ -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)
-       }
-}
index fc2d61a51952e8c3d1711a9bae9ce4ed8078050b..2e828473bd003cb056ade61510990ac9bc2be805 100644 (file)
@@ -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()
        }
index 2c7a398766a92d2b9909b3d5ee5f9a9d3f684b71..1d8e4adad15cd34a3930cdff2379fdb4d4dcfd5c 100644 (file)
@@ -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
index 052acc6e4d8b2bab2f404b91e1f9f4afad5ca2ca..fea76451e116895b879076dcdfc32c1311ffd7d8 100644 (file)
 
 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,
index 709e61bb93d0efa713b92586e87e5636d1f7049c..d4c5c1e9e7d8015bf67c38c39d50373063ab6596 100644 (file)
@@ -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
 }
index e8865021122365ea57f64c78dd231e5e0bc704bb..c3d1dce938621a6c4b8cd1800a996e7d988535c8 100644 (file)
@@ -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 (file)
index 0000000..4295164
--- /dev/null
@@ -0,0 +1,28 @@
+// GoGOST -- Pure Go GOST cryptographic functions library
+// Copyright (C) 2015-2016 Sergey Matveev <stargrave@stargrave.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+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 (file)
index 0000000..b8d53d2
--- /dev/null
@@ -0,0 +1,34 @@
+// GoGOST -- Pure Go GOST cryptographic functions library
+// Copyright (C) 2015-2016 Sergey Matveev <stargrave@stargrave.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+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 (file)
index 0000000..85197a1
--- /dev/null
@@ -0,0 +1,40 @@
+// GoGOST -- Pure Go GOST cryptographic functions library
+// Copyright (C) 2015-2016 Sergey Matveev <stargrave@stargrave.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+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 (file)
index 0000000..90a0855
--- /dev/null
@@ -0,0 +1,68 @@
+// GoGOST -- Pure Go GOST cryptographic functions library
+// Copyright (C) 2015-2016 Sergey Matveev <stargrave@stargrave.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+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 (file)
index 0000000..d12b51b
--- /dev/null
@@ -0,0 +1,55 @@
+// GoGOST -- Pure Go GOST cryptographic functions library
+// Copyright (C) 2015-2016 Sergey Matveev <stargrave@stargrave.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+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 (file)
index 0000000..4d5630e
--- /dev/null
@@ -0,0 +1,116 @@
+// GoGOST -- Pure Go GOST cryptographic functions library
+// Copyright (C) 2015-2016 Sergey Matveev <stargrave@stargrave.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+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)
+       }
+}
index 4f1f22c359a2546795cfae11589080ccace34fa4..f305cbf5fbfd28e82530f2a20181022502c514ab 100644 (file)
--- 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