]> Cypherpunks repositories - keks.git/commitdiff
Refactor and reorganise yacpki
authorSergey Matveev <stargrave@stargrave.org>
Thu, 12 Dec 2024 13:00:58 +0000 (16:00 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Thu, 12 Dec 2024 13:01:03 +0000 (16:01 +0300)
Mainly move cryptography-related code outside.

18 files changed:
gyac/yacpki/algo.go
gyac/yacpki/av.go [new file with mode: 0644]
gyac/yacpki/cer.go
gyac/yacpki/cmd/yacertool/main.go
gyac/yacpki/cmd/yacsdtool/main.go
gyac/yacpki/doc.go [new file with mode: 0644]
gyac/yacpki/ed25519-blake2b/algo.go [new file with mode: 0644]
gyac/yacpki/ed25519-blake2b/kp.go [new file with mode: 0644]
gyac/yacpki/ed25519-blake2b/prv.go [new file with mode: 0644]
gyac/yacpki/ed25519-blake2b/verify.go [new file with mode: 0644]
gyac/yacpki/gost.go [deleted file]
gyac/yacpki/gost/gost.go [new file with mode: 0644]
gyac/yacpki/gost/kp.go [new file with mode: 0644]
gyac/yacpki/gost/signer.go [new file with mode: 0644]
gyac/yacpki/gost/verify.go [new file with mode: 0644]
gyac/yacpki/hash/algo.go [new file with mode: 0644]
gyac/yacpki/prv.go
gyac/yacpki/signed-data.go

index 7307ed8ac5329be0dfe7ee39bd6c02bfb7ac1ad07c49cb6068077a33607c1371..fcee50c7147579bd741ba77b3e497dfd5b437da8e52b66538bbd0bfb3bd22ce0 100644 (file)
@@ -1,54 +1,12 @@
 package yacpki
 
 import (
-       "bytes"
-       "hash"
-
-       "github.com/google/uuid"
-       "go.cypherpunks.su/gogost/v6/gost34112012256"
-       "go.cypherpunks.su/gogost/v6/gost34112012512"
-       "golang.org/x/crypto/blake2b"
-
-       "go.cypherpunks.su/yac/gyac"
-       "go.cypherpunks.su/yac/gyac/yacpki/utils"
+       "go.cypherpunks.su/yac/gyac/yacpki/ed25519-blake2b"
+       "go.cypherpunks.su/yac/gyac/yacpki/gost"
 )
 
 const (
-       AlgoEd25519BLAKE2b = "ed25519-blake2b"
-       AlgoGOST3410256A   = "gost3410-256A"
-       AlgoGOST3410512C   = "gost3410-512C"
-       AlgoStreebog256    = "streebog256"
-       AlgoStreebog512    = "streebog512"
+       Ed25519BLAKE2b = ed25519blake2b.Ed25519BLAKE2b
+       GOST3410256A   = gost.GOST3410256A
+       GOST3410512C   = gost.GOST3410512C
 )
-
-var HashToNew = map[string]func() hash.Hash{
-       AlgoStreebog256: gost34112012256.New,
-       AlgoStreebog512: gost34112012512.New,
-}
-
-type AV struct {
-       A string `yac:"a"`
-       V []byte `yac:"v"`
-}
-
-func (av *AV) Id() (id uuid.UUID) {
-       var hasher hash.Hash
-       switch av.A {
-       case AlgoEd25519BLAKE2b:
-               var err error
-               hasher, err = blake2b.New256(nil)
-               if err != nil {
-                       panic(err)
-               }
-       case AlgoGOST3410256A, AlgoGOST3410512C:
-               hasher = gost34112012256.New()
-       default:
-               panic("unsupported algorithm")
-       }
-       utils.MustWrite(hasher, gyac.FromGo(av).Encode(nil))
-       id, err := uuid.NewRandomFromReader(bytes.NewReader(hasher.Sum(nil)))
-       if err != nil {
-               panic(err)
-       }
-       return id
-}
diff --git a/gyac/yacpki/av.go b/gyac/yacpki/av.go
new file mode 100644 (file)
index 0000000..acfdc90
--- /dev/null
@@ -0,0 +1,39 @@
+package yacpki
+
+import (
+       "bytes"
+       "hash"
+
+       "github.com/google/uuid"
+
+       "go.cypherpunks.su/yac/gyac"
+       pkihash "go.cypherpunks.su/yac/gyac/yacpki/hash"
+       "go.cypherpunks.su/yac/gyac/yacpki/utils"
+)
+
+// Algorithm-value often used structure.
+type AV struct {
+       A string `yac:"a"`
+       V []byte `yac:"v"`
+}
+
+// Calculate UUID of the AV. UUIDv4 is generated from the hash output of
+// the av structure. Hash algorithm selection depends on av.A. uuid.Nil
+// is returned if algorithm is not supported.
+func (av *AV) Id() (id uuid.UUID) {
+       var hasher hash.Hash
+       switch av.A {
+       case Ed25519BLAKE2b:
+               hasher = pkihash.ByName(pkihash.BLAKE2b256)
+       case GOST3410256A, GOST3410512C:
+               hasher = pkihash.ByName(pkihash.Streebog256)
+       default:
+               return uuid.Nil
+       }
+       utils.MustWrite(hasher, gyac.FromGo(av).Encode(nil))
+       id, err := uuid.NewRandomFromReader(bytes.NewReader(hasher.Sum(nil)))
+       if err != nil {
+               panic(err)
+       }
+       return id
+}
index 255fb4642a52ac5f996f51ab2d00843eced9efbbb359b919b9a63f2b72a6535b..62b7a3ae32e541db010982e14955908161e267c90495baa473021f59a12f6b7b 100644 (file)
@@ -4,31 +4,29 @@ import (
        "crypto"
        "errors"
        "fmt"
-       "hash"
        "time"
 
        "github.com/google/uuid"
-       "go.cypherpunks.su/gogost/v6/gost3410"
-       "go.cypherpunks.su/gogost/v6/gost34112012256"
-       "go.cypherpunks.su/gogost/v6/gost34112012512"
 
        "go.cypherpunks.su/yac/gyac"
        "go.cypherpunks.su/yac/gyac/mapstruct"
-       "go.cypherpunks.su/yac/gyac/yacpki/ed25519-blake2b/ed25519"
-       "go.cypherpunks.su/yac/gyac/yacpki/utils"
+       ed25519blake2b "go.cypherpunks.su/yac/gyac/yacpki/ed25519-blake2b"
+       "go.cypherpunks.su/yac/gyac/yacpki/gost"
 )
 
 const (
-       KUCA  = "ca"
-       KUSig = "sig"
+       KUCA  = "ca"  // CA-capable key usage
+       KUSig = "sig" // Signing-capable key usage
 )
 
+// Public key.
 type Pub struct {
        A  string    `yac:"a"`
        V  []byte    `yac:"v"`
        Id uuid.UUID `yac:"id"`
 }
 
+// Certificate load (contents).
 type CerLoad struct {
        KU   *map[string]*struct{} `yac:"ku,omitempty"`
        Subj map[string]string     `yac:"sub"`
@@ -36,15 +34,9 @@ type CerLoad struct {
        Pub  []Pub                 `yac:"pub"`
 }
 
-func CerParse(data []byte) (sd *SignedData, tail []byte, err error) {
-       sd, tail, err = SignedDataParse(data)
-       if err != nil {
-               return
-       }
-       err = sd.CerParse()
-       return
-}
-
+// Parse SignedData contents as CerLoad (certificate) and check its
+// signatures necessary structure. sd.Load.V will hold the CerLoad in
+// case of success.
 func (sd *SignedData) CerParse() error {
        if sd.Load.T != "cer" {
                return errors.New("CerParse: wrong load type")
@@ -104,16 +96,28 @@ func (sd *SignedData) CerParse() error {
        return nil
 }
 
-func (cer *CerLoad) Can(ku string) bool {
-       if cer.KU == nil {
-               return false
+// Parse YAC-encoded data as SignedData with the CerLoad (certificate) contents.
+func CerParse(data []byte) (sd *SignedData, tail []byte, err error) {
+       sd, tail, err = SignedDataParse(data)
+       if err != nil {
+               return
        }
-       if _, ok := (*cer.KU)[ku]; !ok {
+       err = sd.CerParse()
+       return
+}
+
+// Check if cer certificate has desired ku capability.
+func (cer *CerLoad) Can(ku string) (yes bool) {
+       if cer.KU == nil {
                return false
        }
-       return true
+       _, yes = (*cer.KU)[ku]
+       return
 }
 
+// Sign the current SignedData, having CerLoad payload with the provided
+// parent's CerLoad and prv key. Certificate's CID will be automatically
+// generated UUIDv7. since and till times must not have nanoseconds part.
 func (sd *SignedData) CerIssueWith(
        parent *CerLoad,
        prv crypto.Signer,
@@ -129,40 +133,23 @@ func (sd *SignedData) CerIssueWith(
 
 var ErrSigInvalid = errors.New("signature is invalid")
 
+// Verify signature of signed data. ErrSigInvalid will be returned in
+// case of invalid signature.
 func (cer *CerLoad) CheckSignature(signed, signature []byte) (err error) {
        if !cer.Can(KUSig) || len(cer.Pub) != 1 {
                err = errors.New("cer can not sign")
                return
        }
        pub := cer.Pub[0]
+       var valid bool
        switch pub.A {
-       case AlgoEd25519BLAKE2b:
-               if len(pub.V) != ed25519.PublicKeySize {
-                       err = errors.New("invalid ed25519 public key size")
-                       return
-               }
-               if !ed25519.Verify(ed25519.PublicKey(pub.V), signed, signature) {
-                       err = errors.New("invalid ed25519 signature")
-                       return
-               }
-       case AlgoGOST3410256A, AlgoGOST3410512C:
-               var pk *gost3410.PublicKey
-               pk, err = gost3410.NewPublicKeyBE(GOST3410CurveByName(pub.A), pub.V)
-               if err != nil {
-                       return
-               }
-               var hasher hash.Hash
-               switch pub.A {
-               case AlgoGOST3410256A:
-                       hasher = gost34112012256.New()
-               case AlgoGOST3410512C:
-                       hasher = gost34112012512.New()
+       case Ed25519BLAKE2b:
+               valid, err = ed25519blake2b.Verify(pub.V, signed, signature)
+               if !valid {
+                       err = ErrSigInvalid
                }
-               utils.MustWrite(hasher, signed)
-               hsh := hasher.Sum(nil)
-               var valid bool
-               valid, err = pk.VerifyDigest(hsh,
-                       append(signature[len(signature)/2:], signature[:len(signature)/2]...))
+       case GOST3410256A, GOST3410512C:
+               valid, err = gost.Verify(pub.A, pub.V, signed, signature)
                if !valid {
                        err = ErrSigInvalid
                }
@@ -172,6 +159,8 @@ func (cer *CerLoad) CheckSignature(signed, signature []byte) (err error) {
        return
 }
 
+// Verify SignedData CerLoad certificate's signature with provided parent.
+// Currently only single signature can be verified.
 func (sd *SignedData) CerCheckSignatureFrom(parent *CerLoad) (err error) {
        if len(sd.Sigs) != 1 {
                err = errors.New("can verify only single signature")
@@ -190,6 +179,8 @@ func (sd *SignedData) CerCheckSignatureFrom(parent *CerLoad) (err error) {
        return parent.CheckSignature(gyac.FromGo(tbs).Encode(nil), sig.Sign.V)
 }
 
+// Get CerLoad from SignedData.
+// Returns nil if SignedData does not hold it (or it is not yet parsed).
 func (sd *SignedData) CerLoad() *CerLoad {
        if sd.Load.T != "cer" {
                return nil
@@ -201,6 +192,8 @@ func (sd *SignedData) CerLoad() *CerLoad {
        return nil
 }
 
+// Verify sd SignedData CerLoad certificate against cers chain of
+// certificate authority ones at specified point of time t.
 func (sd *SignedData) CerVerify(cers []*SignedData, t time.Time) (err error) {
        {
                exp := *(sd.Sigs[0].TBS.Exp)
index 1fe1aa0d47876a14ab78a0666b32a1746388b3d9d39a83b1f9dca7c67e93973c..446ccd3103d05066f8a312bca3ada9ceeaf1fc11cc0f6c7cdc6956943657148d 100644 (file)
@@ -2,20 +2,17 @@ package main
 
 import (
        "crypto"
-       "crypto/rand"
        "errors"
        "flag"
-       "io"
        "log"
        "os"
        "strings"
        "time"
 
-       "go.cypherpunks.su/gogost/v6/gost3410"
-
        "go.cypherpunks.su/yac/gyac"
        "go.cypherpunks.su/yac/gyac/yacpki"
-       "go.cypherpunks.su/yac/gyac/yacpki/ed25519-blake2b/ed25519"
+       ed25519blake2b "go.cypherpunks.su/yac/gyac/yacpki/ed25519-blake2b"
+       "go.cypherpunks.su/yac/gyac/yacpki/gost"
        "go.cypherpunks.su/yac/gyac/yacpki/utils"
 )
 
@@ -55,7 +52,7 @@ func main() {
                "Optional notBefore, \"2006-01-02 15:04:05\" format")
        lifetime := flag.Uint("lifetime", 365,
                "Lifetime of the certificate, days")
-       algo := flag.String("algo", "gost3410-256A", "Public key algorithm")
+       algo := flag.String("algo", "", "Public key algorithm")
        issuingPrv := flag.String("ca-prv", "",
                "Path to private key file for issuing with")
        reuseKey := flag.Bool("reuse-key", false,
@@ -101,7 +98,7 @@ func main() {
                if *issuingPrv == "" {
                        log.Fatal("no -ca-key is set")
                }
-               caPrv, err = yacpki.PrvParse(utils.MustReadFile(*issuingPrv))
+               caPrv, _, err = yacpki.PrvParse(utils.MustReadFile(*issuingPrv))
                if err != nil {
                        log.Fatal(err)
                }
@@ -125,79 +122,38 @@ func main() {
        }
 
        var prv crypto.Signer
-       var pubRaw []byte
-       switch *algo {
-       case yacpki.AlgoEd25519BLAKE2b:
-               if *reuseKey {
-                       prv, err = yacpki.PrvParse(utils.MustReadFile(*prvPath))
-                       if err != nil {
-                               log.Fatal(err)
-                       }
-                       prvEd25519 := prv.(ed25519.PrivateKey)
-                       pubRaw = prvEd25519[ed25519.SeedSize:]
-               } else {
-                       var prvEd25519 ed25519.PrivateKey
-                       var pubEd25519 ed25519.PublicKey
-                       pubEd25519, prvEd25519, err = ed25519.GenerateKey(rand.Reader)
-                       if err != nil {
-                               log.Fatal(err)
-                       }
-                       prv = prvEd25519
-                       pubRaw = pubEd25519[:]
-                       err = os.WriteFile(*prvPath, gyac.FromGo(
-                               yacpki.AV{A: *algo, V: prvEd25519.Seed()},
-                       ).Encode(nil), 0o600)
-                       if err != nil {
-                               log.Fatal(err)
-                       }
+       var prvRaw []byte
+       var pub []byte
+       if *reuseKey {
+               prv, pub, err = yacpki.PrvParse(utils.MustReadFile(*prvPath))
+               if err != nil {
+                       log.Fatal(err)
                }
-       default: // GOST
-               curve := yacpki.GOST3410CurveByName(*algo)
-               if curve == nil {
-                       log.Fatal("unknown -algo specified")
+       } else {
+               switch *algo {
+               case yacpki.Ed25519BLAKE2b:
+                       prv, prvRaw, pub, err = ed25519blake2b.NewKeypair()
+               case yacpki.GOST3410256A, yacpki.GOST3410512C:
+                       prv, prvRaw, pub, err = gost.NewKeypair(*algo)
+               default:
+                       err = errors.New("unknown -algo specified")
                }
-               var signer *yacpki.GOSTSigner
-               if *reuseKey {
-                       prv, err = yacpki.PrvParse(utils.MustReadFile(*prvPath))
-                       if err != nil {
-                               log.Fatal(err)
-                       }
-                       signer = prv.(*yacpki.GOSTSigner)
-                       if signer.Prv.C.Name != curve.Name {
-                               log.Fatal("-algo is not same with private key")
-                       }
-               } else {
-                       prvRaw := make([]byte, curve.PointSize())
-                       if _, err = io.ReadFull(rand.Reader, prvRaw); err != nil {
-                               log.Fatal(err)
-                       }
-                       var prvKey *gost3410.PrivateKey
-                       prvKey, err = gost3410.NewPrivateKeyBE(curve, prvRaw)
-                       if err != nil {
-                               log.Fatal(err)
-                       }
-                       raw := gyac.FromGo(yacpki.AV{A: *algo, V: prvKey.RawBE()}).Encode(nil)
-                       prv, err = yacpki.PrvParse(raw)
-                       if err != nil {
-                               log.Fatal(err)
-                       }
-                       err = os.WriteFile(*prvPath, raw, 0o600)
-                       if err != nil {
-                               log.Fatal(err)
-                       }
-                       signer = prv.(*yacpki.GOSTSigner)
+               if err != nil {
+                       log.Fatal(err)
                }
-               var pub *gost3410.PublicKey
-               pub, err = signer.Prv.PublicKey()
+               err = os.WriteFile(
+                       *prvPath,
+                       gyac.FromGo(yacpki.AV{A: *algo, V: prvRaw}).Encode(nil),
+                       0o600,
+               )
                if err != nil {
                        log.Fatal(err)
                }
-               pubRaw = pub.RawBE()
        }
 
-       pubMap := yacpki.Pub{A: *algo, V: pubRaw}
+       pubMap := yacpki.Pub{A: *algo, V: pub}
        {
-               av := yacpki.AV{A: *algo, V: pubRaw}
+               av := yacpki.AV{A: *algo, V: pub}
                pubMap.Id = av.Id()
        }
        cerLoad := yacpki.CerLoad{Subj: subj, Pub: []yacpki.Pub{pubMap}}
index 16d08f2450ae123299bdfa3c8e9d6d1b06c80b506c9ef923994d44784506630a..565fcedb3563130782ef7d9a0ebccd590dc10594ba75b44202b286a6ba1e65bc 100644 (file)
@@ -12,6 +12,7 @@ import (
 
        "go.cypherpunks.su/yac/gyac"
        "go.cypherpunks.su/yac/gyac/yacpki"
+       pkihash "go.cypherpunks.su/yac/gyac/yacpki/hash"
        "go.cypherpunks.su/yac/gyac/yacpki/utils"
 )
 
@@ -21,7 +22,7 @@ func main() {
        sdPath := flag.String("sd", "", "Path to signed-data file")
        typ := flag.String("type", "data", "Type of the content, /load/t value")
        hashAlgo := flag.String("hash", "", "Algorithm identifier of the hash to use")
-       verify := flag.Bool("verify", false, "Verify provided -cer with -ca-cer")
+       verify := flag.Bool("verify", false, "Verify with provided -cer")
 
        flag.Parse()
        log.SetFlags(log.Lshortfile)
@@ -39,17 +40,16 @@ func main() {
                if *prvPath == "" {
                        log.Fatal("no -prv is set")
                }
-               signer, err = yacpki.PrvParse(utils.MustReadFile(*prvPath))
+               signer, _, err = yacpki.PrvParse(utils.MustReadFile(*prvPath))
                if err != nil {
                        log.Fatal(err)
                }
        }
 
-       hashNew := yacpki.HashToNew[*hashAlgo]
-       if hashNew == nil {
+       hasher := pkihash.ByName(*hashAlgo)
+       if hasher == nil {
                log.Fatal("unknown -hash specified")
        }
-       hasher := hashNew()
        _, err = io.Copy(hasher, bufio.NewReader(os.Stdin))
        if err != nil {
                log.Fatal(err)
diff --git a/gyac/yacpki/doc.go b/gyac/yacpki/doc.go
new file mode 100644 (file)
index 0000000..64fda62
--- /dev/null
@@ -0,0 +1,2 @@
+// yacpki provides PKI-related capabilities based on YAC encoded formats.
+package yacpki
diff --git a/gyac/yacpki/ed25519-blake2b/algo.go b/gyac/yacpki/ed25519-blake2b/algo.go
new file mode 100644 (file)
index 0000000..2bacaf6
--- /dev/null
@@ -0,0 +1,3 @@
+package ed25519blake2b
+
+const Ed25519BLAKE2b = "ed25519-blake2b"
diff --git a/gyac/yacpki/ed25519-blake2b/kp.go b/gyac/yacpki/ed25519-blake2b/kp.go
new file mode 100644 (file)
index 0000000..e5975ff
--- /dev/null
@@ -0,0 +1,21 @@
+package ed25519blake2b
+
+import (
+       "crypto"
+       "crypto/rand"
+
+       "go.cypherpunks.su/yac/gyac/yacpki/ed25519-blake2b/ed25519"
+)
+
+func NewKeypair() (signer crypto.Signer, prv, pub []byte, err error) {
+       var prvEd ed25519.PrivateKey
+       var pubEd ed25519.PublicKey
+       pubEd, prvEd, err = ed25519.GenerateKey(rand.Reader)
+       if err != nil {
+               return
+       }
+       signer = prvEd
+       prv = prvEd.Seed()
+       pub = pubEd[:]
+       return
+}
diff --git a/gyac/yacpki/ed25519-blake2b/prv.go b/gyac/yacpki/ed25519-blake2b/prv.go
new file mode 100644 (file)
index 0000000..4764214
--- /dev/null
@@ -0,0 +1,19 @@
+package ed25519blake2b
+
+import (
+       "crypto"
+       "errors"
+
+       "go.cypherpunks.su/yac/gyac/yacpki/ed25519-blake2b/ed25519"
+)
+
+func NewSigner(v []byte) (prv crypto.Signer, pub []byte, err error) {
+       if len(v) != ed25519.SeedSize {
+               err = errors.New("wrong ed25519 private key size")
+               return
+       }
+       p := ed25519.NewKeyFromSeed(v)
+       pub = p[ed25519.SeedSize:]
+       prv = p
+       return
+}
diff --git a/gyac/yacpki/ed25519-blake2b/verify.go b/gyac/yacpki/ed25519-blake2b/verify.go
new file mode 100644 (file)
index 0000000..c8f5421
--- /dev/null
@@ -0,0 +1,16 @@
+package ed25519blake2b
+
+import (
+       "errors"
+
+       "go.cypherpunks.su/yac/gyac/yacpki/ed25519-blake2b/ed25519"
+)
+
+func Verify(pub, signed, signature []byte) (valid bool, err error) {
+       if len(pub) != ed25519.PublicKeySize {
+               err = errors.New("invalid ed25519 public key size")
+               return
+       }
+       valid = ed25519.Verify(ed25519.PublicKey(pub), signed, signature)
+       return
+}
diff --git a/gyac/yacpki/gost.go b/gyac/yacpki/gost.go
deleted file mode 100644 (file)
index f6e00e6..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-package yacpki
-
-import (
-       "crypto"
-       "hash"
-       "io"
-       "log"
-
-       "go.cypherpunks.su/gogost/v6/gost3410"
-)
-
-type GOSTSigner struct {
-       Prv    *gost3410.PrivateKey
-       Hasher func() hash.Hash
-}
-
-func (s *GOSTSigner) Public() crypto.PublicKey {
-       return s.Prv.Public()
-}
-
-func (s *GOSTSigner) Sign(
-       rand io.Reader,
-       msg []byte,
-       opts crypto.SignerOpts,
-) (signature []byte, err error) {
-       h := s.Hasher()
-       h.Write(msg)
-       dgst := h.Sum(nil)
-       signature, err = s.Prv.Sign(rand, dgst, opts)
-       if err != nil {
-               return
-       }
-       signature = append(signature[len(signature)/2:], signature[:len(signature)/2]...)
-       return
-}
-
-func GOST3410CurveByName(name string) (curve *gost3410.Curve) {
-       switch name {
-       case AlgoGOST3410256A:
-               curve = gost3410.CurveIdtc26gost341012256paramSetA()
-       case AlgoGOST3410512C:
-               curve = gost3410.CurveIdtc26gost341012512paramSetC()
-       default:
-               log.Fatal("unknown curve")
-       }
-       return
-}
diff --git a/gyac/yacpki/gost/gost.go b/gyac/yacpki/gost/gost.go
new file mode 100644 (file)
index 0000000..bffbd62
--- /dev/null
@@ -0,0 +1,20 @@
+package gost
+
+import (
+       "go.cypherpunks.su/gogost/v6/gost3410"
+)
+
+const (
+       GOST3410256A = "gost3410-256A"
+       GOST3410512C = "gost3410-512C"
+)
+
+func CurveByName(name string) (curve *gost3410.Curve) {
+       switch name {
+       case GOST3410256A:
+               curve = gost3410.CurveIdtc26gost341012256paramSetA()
+       case GOST3410512C:
+               curve = gost3410.CurveIdtc26gost341012512paramSetC()
+       }
+       return
+}
diff --git a/gyac/yacpki/gost/kp.go b/gyac/yacpki/gost/kp.go
new file mode 100644 (file)
index 0000000..a9836dc
--- /dev/null
@@ -0,0 +1,30 @@
+package gost
+
+import (
+       "crypto"
+       "crypto/rand"
+       "errors"
+       "io"
+
+       "go.cypherpunks.su/gogost/v6/gost3410"
+)
+
+func NewKeypair(algo string) (signer crypto.Signer, prv, pub []byte, err error) {
+       curve := CurveByName(algo)
+       if curve == nil {
+               err = errors.New("unknown curve")
+               return
+       }
+       prv = make([]byte, curve.PointSize())
+       _, err = io.ReadFull(rand.Reader, prv)
+       if err != nil {
+               return
+       }
+       var pk *gost3410.PrivateKey
+       pk, err = gost3410.NewPrivateKeyBE(curve, prv)
+       if err != nil {
+               return
+       }
+       signer, pub, err = NewSigner(algo, pk.RawBE())
+       return
+}
diff --git a/gyac/yacpki/gost/signer.go b/gyac/yacpki/gost/signer.go
new file mode 100644 (file)
index 0000000..f1f0772
--- /dev/null
@@ -0,0 +1,64 @@
+package gost
+
+import (
+       "crypto"
+       "hash"
+       "io"
+
+       "go.cypherpunks.su/gogost/v6/gost3410"
+       "go.cypherpunks.su/gogost/v6/gost34112012256"
+       "go.cypherpunks.su/gogost/v6/gost34112012512"
+)
+
+type Signer struct {
+       Prv    *gost3410.PrivateKey
+       Hasher func() hash.Hash
+}
+
+func (s *Signer) Public() crypto.PublicKey {
+       return s.Prv.Public()
+}
+
+func (s *Signer) Sign(
+       rand io.Reader,
+       msg []byte,
+       opts crypto.SignerOpts,
+) (signature []byte, err error) {
+       h := s.Hasher()
+       h.Write(msg)
+       dgst := h.Sum(nil)
+       signature, err = s.Prv.Sign(rand, dgst, opts)
+       if err != nil {
+               return
+       }
+       signature = append(signature[len(signature)/2:], signature[:len(signature)/2]...)
+       return
+}
+
+func NewSigner(a string, v []byte) (prv crypto.Signer, pub []byte, err error) {
+       signer := Signer{}
+       switch a {
+       case GOST3410256A:
+               signer.Hasher = gost34112012256.New
+               signer.Prv, err = gost3410.NewPrivateKeyBE(
+                       gost3410.CurveIdtc26gost341012256paramSetA(), v,
+               )
+       case GOST3410512C:
+               signer.Hasher = gost34112012512.New
+               signer.Prv, err = gost3410.NewPrivateKeyBE(
+                       gost3410.CurveIdtc26gost341012512paramSetC(), v,
+               )
+       default:
+               panic("unknown GOST algorithm")
+       }
+       if err != nil {
+               return
+       }
+       prv = &signer
+       var pk *gost3410.PublicKey
+       pk, err = signer.Prv.PublicKey()
+       if err == nil {
+               pub = pk.RawBE()
+       }
+       return
+}
diff --git a/gyac/yacpki/gost/verify.go b/gyac/yacpki/gost/verify.go
new file mode 100644 (file)
index 0000000..6412ed3
--- /dev/null
@@ -0,0 +1,30 @@
+package gost
+
+import (
+       "hash"
+
+       "go.cypherpunks.su/gogost/v6/gost3410"
+       "go.cypherpunks.su/gogost/v6/gost34112012256"
+       "go.cypherpunks.su/gogost/v6/gost34112012512"
+       "go.cypherpunks.su/yac/gyac/yacpki/utils"
+)
+
+func Verify(algo string, pub, signed, signature []byte) (valid bool, err error) {
+       var pk *gost3410.PublicKey
+       pk, err = gost3410.NewPublicKeyBE(CurveByName(algo), pub)
+       if err != nil {
+               return
+       }
+       var hasher hash.Hash
+       switch algo {
+       case GOST3410256A:
+               hasher = gost34112012256.New()
+       case GOST3410512C:
+               hasher = gost34112012512.New()
+       }
+       utils.MustWrite(hasher, signed)
+       hsh := hasher.Sum(nil)
+       valid, err = pk.VerifyDigest(hsh,
+               append(signature[len(signature)/2:], signature[:len(signature)/2]...))
+       return
+}
diff --git a/gyac/yacpki/hash/algo.go b/gyac/yacpki/hash/algo.go
new file mode 100644 (file)
index 0000000..d5a0040
--- /dev/null
@@ -0,0 +1,38 @@
+package hash
+
+import (
+       "hash"
+
+       "go.cypherpunks.su/gogost/v6/gost34112012256"
+       "go.cypherpunks.su/gogost/v6/gost34112012512"
+       "golang.org/x/crypto/blake2b"
+)
+
+const (
+       Streebog256 = "streebog256"
+       Streebog512 = "streebog512"
+       BLAKE2b     = "blake2b"
+       BLAKE2b256  = "blake2b256"
+)
+
+func ByName(name string) hash.Hash {
+       switch name {
+       case Streebog256:
+               return gost34112012256.New()
+       case Streebog512:
+               return gost34112012512.New()
+       case BLAKE2b:
+               h, err := blake2b.New512(nil)
+               if err != nil {
+                       panic(err)
+               }
+               return h
+       case BLAKE2b256:
+               h, err := blake2b.New256(nil)
+               if err != nil {
+                       panic(err)
+               }
+               return h
+       }
+       return nil
+}
index 484de4132f172bdc2ba01163dc9ed31bfce6856b205791f86daa173fcaaf3fe6..feb0a83cc4d2d1919b52a74470abe64ce021d5cf8fb7ffd42940c64a6a37d26b 100644 (file)
@@ -5,15 +5,13 @@ import (
        "errors"
        "fmt"
 
-       "go.cypherpunks.su/gogost/v6/gost3410"
-       "go.cypherpunks.su/gogost/v6/gost34112012256"
-       "go.cypherpunks.su/gogost/v6/gost34112012512"
-
        "go.cypherpunks.su/yac/gyac/mapstruct"
-       "go.cypherpunks.su/yac/gyac/yacpki/ed25519-blake2b/ed25519"
+       ed25519blake2b "go.cypherpunks.su/yac/gyac/yacpki/ed25519-blake2b"
+       "go.cypherpunks.su/yac/gyac/yacpki/gost"
 )
 
-func PrvParse(data []byte) (prv crypto.Signer, err error) {
+// Parse private key contained in AV YAC-encoded structure.
+func PrvParse(data []byte) (prv crypto.Signer, pub []byte, err error) {
        var av AV
        var tail []byte
        tail, err = mapstruct.Decode(&av, data)
@@ -25,24 +23,10 @@ func PrvParse(data []byte) (prv crypto.Signer, err error) {
                return
        }
        switch av.A {
-       case AlgoEd25519BLAKE2b:
-               prv = ed25519.NewKeyFromSeed(av.V)
-               if len(av.V) != ed25519.SeedSize {
-                       err = errors.New("wrong ed25519 private key size")
-                       return
-               }
-       case AlgoGOST3410256A:
-               signer := &GOSTSigner{Hasher: gost34112012256.New}
-               signer.Prv, err = gost3410.NewPrivateKeyBE(
-                       gost3410.CurveIdtc26gost341012256paramSetA(), av.V,
-               )
-               prv = signer
-       case AlgoGOST3410512C:
-               signer := &GOSTSigner{Hasher: gost34112012512.New}
-               signer.Prv, err = gost3410.NewPrivateKeyBE(
-                       gost3410.CurveIdtc26gost341012512paramSetC(), av.V,
-               )
-               prv = signer
+       case Ed25519BLAKE2b:
+               prv, pub, err = ed25519blake2b.NewSigner(av.V)
+       case GOST3410256A, GOST3410512C:
+               prv, pub, err = gost.NewSigner(av.A, av.V)
        default:
                err = fmt.Errorf("unknown private key algo: %s", av.A)
        }
index c753372b4e5ed3e3d59697410e64872da321c35dd2621d4fe40f680bb002fa18..86f35b0636bd1f8025dd69c4e76a78083ae595e967d81f3cc9b55c57423cc7dd 100644 (file)
@@ -45,16 +45,7 @@ type SignedData struct {
        Sigs   []*Sig                `yac:"sigs"`
 }
 
-func SignedDataParse(data []byte) (sd *SignedData, tail []byte, err error) {
-       var item gyac.Item
-       item, tail, err = gyac.Decode(data)
-       if err != nil {
-               return
-       }
-       sd, err = SignedDataParseItem(item)
-       return
-}
-
+// Parse signed-data from decoded item.
 func SignedDataParseItem(item gyac.Item) (sd *SignedData, err error) {
        if item.T != types.Map {
                err = errors.New("SignedDataParse: non-map")
@@ -125,6 +116,21 @@ func SignedDataParseItem(item gyac.Item) (sd *SignedData, err error) {
        return
 }
 
+// Parse signed-data from YAC-encoded data. This is just a wrapper over
+// SignedDataParseItem.
+func SignedDataParse(data []byte) (sd *SignedData, tail []byte, err error) {
+       var item gyac.Item
+       item, tail, err = gyac.Decode(data)
+       if err != nil {
+               return
+       }
+       sd, err = SignedDataParseItem(item)
+       return
+}
+
+// Sign SignedData's contents and sigTBS corresponding data with the
+// provided prv signer, having parent certificate. Signature is appended
+// to the sd.Sigs. parent certificate must have "sig" key-usage.
 func (sd *SignedData) SignWith(
        parent *CerLoad,
        prv crypto.Signer,