README | 3 ++-
src/cypherpunks.ru/gogost/gost3410/2001_test.go | 53 +++++------------------------------------------------
src/cypherpunks.ru/gogost/gost3410/2012_test.go | 10 +++++-----
src/cypherpunks.ru/gogost/gost3410/doc.go | 5 ++++-
src/cypherpunks.ru/gogost/gost3410/params.go | 6 +++---
src/cypherpunks.ru/gogost/gost3410/private.go | 89 ++++++++++++++++++-----------------------------------
src/cypherpunks.ru/gogost/gost3410/public.go | 68 +++++++++++++++++++++++++++---------------------------
src/cypherpunks.ru/gogost/gost3410/ukm.go | 28 ++++++++++++++++++++++++++++
src/cypherpunks.ru/gogost/gost3410/vko.go | 34 ++++++++++++++++++++++++++++++++++
src/cypherpunks.ru/gogost/gost3410/vko2001.go | 40 ++++++++++++++++++++++++++++++++++++++++
src/cypherpunks.ru/gogost/gost3410/vko2001_test.go | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++
src/cypherpunks.ru/gogost/gost3410/vko2012.go | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++
src/cypherpunks.ru/gogost/gost3410/vko2012_test.go | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++
www.texi | 4 +++-
diff --git a/README b/README
index 5967e6a4bebe41fec943418d53193a1405fd528b5c4d983579f19624a0530a5b..4fa4ae3a8a16b960f83d84d9cd31ad483705f4630c552695ddcd9620bb5eeae4 100644
--- a/README
+++ b/README
@@ -10,7 +10,8 @@ * GOST R 34.11-2012 Стрибог (Streebog) hash function (RFC 6986)
* 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 8e9993c059f87725693a3317bfc92625191aa436e0d8e74015f48cfa5fcc6b54..161c653a25dbe4bc75c083dc5a1b9bd73f9c62b9d16fac28aa7e6f368e2cb8e7 100644
--- a/src/cypherpunks.ru/gogost/gost3410/2001_test.go
+++ b/src/cypherpunks.ru/gogost/gost3410/2001_test.go
@@ -19,7 +19,6 @@
import (
"bytes"
"crypto/rand"
- "encoding/hex"
"testing"
"testing/quick"
)
@@ -64,7 +63,7 @@ c, err := NewCurveFromParams(CurveParamsGostR34102001Test)
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 @@ c, _ := NewCurveFromParams(CurveParamsGostR34102001Test)
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 @@ if err != nil {
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 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 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 @@ for i := 0; i < b.N; i++ {
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 a340493967a4ea6080df098f6d6aea02f4e6cece504912cbfff990b3a95ffe15..a7377087b69bbc74e3e1d2fcf941b61d68f4a80db4591b145364acc3945ee24f 100644
--- a/src/cypherpunks.ru/gogost/gost3410/2012_test.go
+++ b/src/cypherpunks.ru/gogost/gost3410/2012_test.go
@@ -146,7 +146,7 @@ c, err := NewCurve(p, q, a, b, x, y)
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 @@ c, _ := NewCurveFromParams(CurveParamsGostR34102012TC26ParamSetA)
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 @@ if err != nil {
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 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 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 a57387212c6db45ff19bef9dab91fa6f4a75a0414dabb2ec14d1cb6e6b6280f7..9ef8000aca3171d52d6c9f88ec3036dfd676ca2144c8d32bc4329b577397b522 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 4e637055728478dc80cb8ecc0147765e6eefae2a9fce3e629267b91a09919754..dab4dfd60d4196245deb9ad44c389a5fa44ffad4406926c0fffa5355016b34f6 100644
--- a/src/cypherpunks.ru/gogost/gost3410/params.go
+++ b/src/cypherpunks.ru/gogost/gost3410/params.go
@@ -16,14 +16,14 @@ // along with this program. If not, see .
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 ee3f841af33e486c5dd2011875099459cb3addaef595e31a1d28f347f9160a3c..b0c9e0ebb460babc52da0ec87a463608500c21ab6254c76433abf727e86009e7 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 @@ if _, err = io.ReadFull(rand, kRaw); err != nil {
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 b2a1013479a4c48d5210380892789b4d0220afd78ee648fc4e19f17f310938e7..0743313a7d905a5a69c1ec8432d774858e3f0888d002f5031d527d643ac9f2c7 100644
--- a/src/cypherpunks.ru/gogost/gost3410/public.go
+++ b/src/cypherpunks.ru/gogost/gost3410/public.go
@@ -22,84 +22,84 @@ "math/big"
)
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 0000000000000000000000000000000000000000..be6641a7cf7603a8559d20be7b462baaf50945590d75fd14325f450d0abc769f
--- /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 0000000000000000000000000000000000000000..cc69106508a8fb0238ff3812a3985b15664a946a7a7009389250e2a766017ac1
--- /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 0000000000000000000000000000000000000000..c0943b6faf9d620c87ce7ee83212d92ece45658e2ca2c76f80da51616f295097
--- /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 0000000000000000000000000000000000000000..cdfddbb51e7190e53f744280e54016b62474a459c9cfe55e1a5939aff46af89b
--- /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 0000000000000000000000000000000000000000..5e31f4d0d0d122cb74ef43209a927d0026f810c871ed595b8a6d5546f3278fb1
--- /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 0000000000000000000000000000000000000000..1885a4cdebab90a4ddf6bf6b470e3f4d698cc1707fd0a415e517b1521df03401
--- /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 49d646ce5910fc37b2630112096798a438647088ab618b824ea9ea1f5bef0d1d..92da7fad0481cf3b4a25e309b13a08e4d710203fc4a8732c2b7e0900889987a0 100644
--- a/www.texi
+++ b/www.texi
@@ -36,8 +36,10 @@ @item GOST R 34.10-2012
(@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