]> Cypherpunks.ru repositories - gogost.git/commitdiff
28147-89 and CryptoPro key wrapping support v5.14.0
authorSergey Matveev <stargrave@stargrave.org>
Fri, 8 Dec 2023 09:21:21 +0000 (12:21 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Fri, 8 Dec 2023 09:24:11 +0000 (12:24 +0300)
README
gost28147/wrap.go [new file with mode: 0644]
gost28147/wrap_test.go [new file with mode: 0644]
news.texi

diff --git a/README b/README
index 05dd22283f50d7a740a0fbb0b5f20dadd7c9c663..2085a18e6953ba730ccd9a6a18f7a7eca883a2a7 100644 (file)
--- a/README
+++ b/README
@@ -14,6 +14,7 @@ GOST is GOvernment STandard of Russian Federation (and Soviet Union).
   vice versa
 * VKO GOST R 34.10-2001 key agreement function (RFC 4357)
 * VKO GOST R 34.10-2012 key agreement function (RFC 7836)
+* 28147-89 and CryptoPro key wrapping (RFC 4357)
 * KDF_GOSTR3411_2012_256 KDF function (RFC 7836)
 * GOST R 34.12-2015 128-bit block cipher Кузнечик (Kuznechik) (RFC 7801)
 * GOST R 34.12-2015 64-bit block cipher Магма (Magma)
diff --git a/gost28147/wrap.go b/gost28147/wrap.go
new file mode 100644 (file)
index 0000000..24190ea
--- /dev/null
@@ -0,0 +1,83 @@
+// GoGOST -- Pure Go GOST cryptographic functions library
+// Copyright (C) 2015-2023 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, version 3 of the License.
+//
+// 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 gost28147
+
+import (
+       "bytes"
+       "crypto/subtle"
+       "encoding/binary"
+)
+
+func WrapGost(ukm, kek, cek []byte) []byte {
+       c := NewCipher(kek, &SboxIdGost2814789CryptoProAParamSet)
+       mac, err := c.NewMAC(4, ukm)
+       if err != nil {
+               panic(err)
+       }
+       _, err = mac.Write(cek)
+       if err != nil {
+               panic(err)
+       }
+       cekMac := mac.Sum(nil)
+       cekEnc := make([]byte, 32)
+       c.NewECBEncrypter().CryptBlocks(cekEnc, cek)
+       return bytes.Join([][]byte{ukm, cekEnc, cekMac}, nil)
+}
+
+func UnwrapGost(kek, data []byte) []byte {
+       ukm, data := data[:8], data[8:]
+       cekEnc, cekMac := data[:KeySize], data[KeySize:]
+       c := NewCipher(kek, &SboxIdGost2814789CryptoProAParamSet)
+       cek := make([]byte, 32)
+       c.NewECBDecrypter().CryptBlocks(cek, cekEnc)
+       mac, err := c.NewMAC(4, ukm)
+       if err != nil {
+               panic(err)
+       }
+       _, err = mac.Write(cek)
+       if err != nil {
+               panic(err)
+       }
+       if subtle.ConstantTimeCompare(mac.Sum(nil), cekMac) != 1 {
+               return nil
+       }
+       return cek
+}
+
+func DiversifyCryptoPro(kek, ukm []byte) []byte {
+       out := kek
+       for i := 0; i < 8; i++ {
+               var s1, s2 uint64
+               for j := 0; j < 8; j++ {
+                       k := binary.LittleEndian.Uint32(out[j*4 : j*4+4])
+                       if (ukm[i]>>j)&1 > 0 {
+                               s1 += uint64(k)
+                       } else {
+                               s2 += uint64(k)
+                       }
+               }
+               iv := make([]byte, 8)
+               binary.LittleEndian.PutUint32(iv[:4], uint32(s1%(1<<32)))
+               binary.LittleEndian.PutUint32(iv[4:], uint32(s2%(1<<32)))
+               c := NewCipher(out, &SboxIdGost2814789CryptoProAParamSet)
+               c.NewCFBEncrypter(iv).XORKeyStream(out, out)
+       }
+       return out
+}
+
+func UnwrapCryptoPro(kek, data []byte) []byte {
+       return UnwrapGost(DiversifyCryptoPro(kek, data[:8]), data)
+}
diff --git a/gost28147/wrap_test.go b/gost28147/wrap_test.go
new file mode 100644 (file)
index 0000000..9cf066b
--- /dev/null
@@ -0,0 +1,30 @@
+package gost28147
+
+import (
+       "bytes"
+       "crypto/rand"
+       "io"
+       "testing"
+)
+
+func TestWrapSymmetric(t *testing.T) {
+       kek := make([]byte, KeySize)
+       cek := make([]byte, KeySize)
+       ukm := make([]byte, 8)
+       for i := 0; i < 1000; i++ {
+               if _, err := io.ReadFull(rand.Reader, kek); err != nil {
+                       t.Fatal(err)
+               }
+               if _, err := io.ReadFull(rand.Reader, cek); err != nil {
+                       t.Fatal(err)
+               }
+               if _, err := io.ReadFull(rand.Reader, ukm); err != nil {
+                       t.Fatal(err)
+               }
+               data := WrapGost(ukm, kek, cek)
+               got := UnwrapGost(kek, data)
+               if !bytes.Equal(got, cek) {
+                       t.FailNow()
+               }
+       }
+}
index a514e528f9faa324b82207ba456e3c0468629635..fdca6926bd4b8d2060c689daed395a16579756a2 100644 (file)
--- a/news.texi
+++ b/news.texi
@@ -3,6 +3,11 @@
 
 @table @strong
 
+@anchor{Release 5.14.0}
+@item 5.14.0
+28147-89 and CryptoPro key wrapping support
+(@url{https://tools.ietf.org/html/rfc4357.html, RFC 4357}).
+
 @anchor{Release 5.13.0}
 @item 5.13.0
     @itemize