From: Sergey Matveev Date: Mon, 7 Oct 2024 12:52:31 +0000 (+0300) Subject: gyac/yacpki X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=eed7982d70b87cfae928ba8a56f68079d4aeac14f228cc7fcf476c0cced7959b;p=keks.git gyac/yacpki --- diff --git a/gyac/cmd/certool/cer.go b/gyac/cmd/certool/cer.go deleted file mode 100644 index 4ebc352..0000000 --- a/gyac/cmd/certool/cer.go +++ /dev/null @@ -1,78 +0,0 @@ -package main - -import ( - "errors" - "os" - "time" - - "github.com/google/uuid" - "go.cypherpunks.su/yac/gyac" -) - -type AV struct { - A string `yac:"a"` - V []byte `yac:"v"` -} - -type CerTBS struct { - KU []string `yac:"ku,omitempty"` - Subj map[string]string `yac:"sub"` - Exp []time.Time `yac:"exp"` - Crit []map[string]any `yac:"crit,omitempty"` - Pub AV `yac:"pub"` - KID uuid.UUID `yac:"kid"` -} - -func (tbs *CerTBS) HasCA() (hasCA bool) { - for _, ku := range tbs.KU { - if ku == "ca" { - hasCA = true - } - } - return -} - -type SignedDataLoad struct { - V any `yac:"v"` - T string `yac:"t"` -} - -type SignedDataTBS struct { - V any `yac:"v"` - Load map[string]any `yac:"load"` - T string `yac:"t"` - KID uuid.UUID `yac:"kid"` -} - -type Sig struct { - Load map[string]any `yac:"load,omitempty"` - CerLoc []string `yac:"cer-loc,omitempty"` - Sign AV `yac:"sign"` - KID uuid.UUID `yac:"kid"` -} - -type SignedData struct { - Hashes []string `yac:"hash,omitempty"` - Load SignedDataLoad `yac:"load"` - Sigs []Sig `yac:"sigs"` - Cers []SignedData `yac:"certs,omitempty"` -} - -func loadCerFromFile(pth string) (*SignedData, error) { - data, err := os.ReadFile(pth) - if err != nil { - return nil, err - } - var sd SignedData - err = gyac.DecodeToStruct(&sd, data) - if err != nil { - return nil, err - } - if sd.Load.T != "cer" { - err = errors.New("non \"cer\" in CA SignedData") - } - if len(sd.Sigs) == 0 { - err = errors.New("no signatures present") - } - return &sd, err -} diff --git a/gyac/cmd/certool/main.go b/gyac/cmd/certool/main.go deleted file mode 100644 index 1a3cc65..0000000 --- a/gyac/cmd/certool/main.go +++ /dev/null @@ -1,265 +0,0 @@ -package main - -import ( - "bytes" - "crypto/rand" - "encoding/json" - "flag" - "hash" - "io" - "log" - "os" - "sort" - "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" -) - -const ( - AlgoStreebog256 = "streebog256" - AlgoGOST3410256A = "gost3410-256A" - AlgoGOST3410256B = "gost3410-256B" - AlgoGOST3410256C = "gost3410-256C" - AlgoGOST3410256D = "gost3410-256D" - AlgoGOST3410512A = "gost3410-512A" - AlgoGOST3410512B = "gost3410-512B" - AlgoGOST3410512C = "gost3410-512C" -) - -func GOST3410CurveByName(name string) (curve *gost3410.Curve) { - switch name { - case AlgoGOST3410256A: - curve = gost3410.CurveIdtc26gost341012256paramSetA() - case AlgoGOST3410256B: - curve = gost3410.CurveIdtc26gost341012256paramSetB() - case AlgoGOST3410256C: - curve = gost3410.CurveIdtc26gost341012256paramSetC() - case AlgoGOST3410256D: - curve = gost3410.CurveIdtc26gost341012256paramSetD() - case AlgoGOST3410512A: - curve = gost3410.CurveIdtc26gost341012512paramSetA() - case AlgoGOST3410512B: - curve = gost3410.CurveIdtc26gost341012512paramSetB() - case AlgoGOST3410512C: - curve = gost3410.CurveIdtc26gost341012512paramSetC() - } - return -} - -func HasherByKeyAlgo(a string) hash.Hash { - switch a { - case AlgoGOST3410256A, AlgoGOST3410256B, AlgoGOST3410256C, AlgoGOST3410256D: - return gost34112012256.New() - case AlgoGOST3410512A, AlgoGOST3410512B, AlgoGOST3410512C: - return gost34112012512.New() - default: - log.Fatal("unsupported CA algorithm") - } - return nil -} - -func main() { - kuMap := make(map[string]struct{}) - flag.Func( - "ku", - "Optional key usage, may be specified multiple times", - func(v string) error { - kuMap[v] = struct{}{} - return nil - }, - ) - sinceRaw := flag.String("since", "", - "Optional notBefore, \"2006-01-02 15:04:05\" format") - lifetime := flag.Uint("lifetime", 365, - "Lifetime of the certificate, days") - subjRaw := flag.String("subj", `{"CN": "test"}`, - "JSON map of the subject") - algo := flag.String("algo", "gost3410-256A", "Public key algorithm") - issuingCer := flag.String("ca-cer", "", - "Path to certificate file for issuing with") - issuingPrv := flag.String("ca-prv", "", - "Path to private key file for issuing with") - reuseKey := flag.Bool("reuse-key", false, - "Reuse the key, do not generate new one") - prvPath := flag.String("prv", "", "Path to private key file") - cerPath := flag.String("cer", "", "Path to certificate file") - verify := flag.Bool("verify", false, "Verify provided -cer with ca-cer") - - flag.Parse() - log.SetFlags(log.Lshortfile) - - if *cerPath == "" { - log.Fatal("no -cer is set") - } - - var ku []string - for k := range kuMap { - ku = append(ku, k) - } - kuMap = nil - sort.Sort(gyac.ByLenFirst(ku)) - - var subj map[string]string - err := json.Unmarshal([]byte(*subjRaw), &subj) - if err != nil { - log.Fatalln("while parsing -subj:", err) - } - - var since time.Time - if *sinceRaw == "" { - since = time.Now().UTC().Truncate(time.Second) - } else { - since, err = time.Parse(time.DateTime, *sinceRaw) - if err != nil { - log.Fatalln("while parsing -since:", err) - } - } - till := since.Add(time.Duration(*lifetime) * 24 * time.Hour) - - var caPrv *gost3410.PrivateKey - var caCerTBS CerTBS - if *issuingCer != "" { - var sd *SignedData - sd, err = loadCerFromFile(*issuingCer) - if err != nil { - log.Fatal(err) - } - err = gyac.MapToStruct(&caCerTBS, sd.Load.V.(map[string]any)) - if err != nil { - log.Fatal(err) - } - if !*verify { - if *issuingPrv == "" { - log.Fatal("no -issuing-key is set") - } - caPrv, err = loadPrvFromFile(*issuingPrv) - if err != nil { - log.Fatal(err) - } - } - } - - if *verify { - var sd *SignedData - sd, err = loadCerFromFile(*cerPath) - if err != nil { - log.Fatal(err) - } - var tbs CerTBS - err = gyac.MapToStruct(&tbs, sd.Load.V.(map[string]any)) - if err != nil { - log.Fatal(err) - } - sig := sd.Sigs[0] - if sig.KID != caCerTBS.KID { - log.Fatal("SKID != AKID") - } - if sig.KID != tbs.KID && !caCerTBS.HasCA() { - log.Fatal("no \"ca\" KU met in CA") - } - sdTBS := SignedDataTBS{T: "cer", V: tbs, KID: sig.KID} - hasher := HasherByKeyAlgo(sig.Sign.A) - hasher.Write(gyac.EncodeItem(nil, gyac.ItemFromGo(sdTBS))) - var pub *gost3410.PublicKey - pub, err = gost3410.NewPublicKeyBE( - GOST3410CurveByName(caCerTBS.Pub.A), - caCerTBS.Pub.V, - ) - if err != nil { - log.Fatal(err) - } - var valid bool - valid, err = pub.VerifyDigest(hasher.Sum(nil), sig.Sign.V) - if err != nil { - log.Fatal(err) - } - if !valid { - os.Exit(1) - } - return - } - - if *prvPath == "" { - log.Fatal("no -prv is set") - } - - curve := GOST3410CurveByName(*algo) - if curve == nil { - log.Fatal("unknown -algo specified") - } - var prv *gost3410.PrivateKey - if *reuseKey { - prv, err = loadPrvFromFile(*prvPath) - if err != nil { - log.Fatal(err) - } - if 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) - } - prv, err = gost3410.NewPrivateKeyBE(curve, prvRaw) - if err != nil { - log.Fatal(err) - } - err = os.WriteFile(*prvPath, gyac.EncodeItem(nil, - gyac.ItemFromGo(AV{A: *algo, V: prv.RawBE()})), 0o600) - if err != nil { - log.Fatal(err) - } - } - - var pub *gost3410.PublicKey - pub, err = prv.PublicKey() - if err != nil { - log.Fatal(err) - } - pubTBS := AV{A: *algo, V: pub.RawBE()} - var spki uuid.UUID - { - hasher := gost34112012256.New() - hasher.Write(gyac.EncodeItem(nil, gyac.ItemFromGo(pubTBS))) - spki, err = uuid.NewRandomFromReader(bytes.NewReader(hasher.Sum(nil))) - if err != nil { - log.Fatal(err) - } - } - cerTBS := CerTBS{ - KU: ku, - Exp: []time.Time{since, till}, - KID: spki, - Subj: subj, - Pub: pubTBS, - } - if caPrv == nil { - caPrv = prv - caCerTBS = cerTBS - } else { - if !caCerTBS.HasCA() { - log.Fatal("no \"ca\" KU met in CA") - } - } - sig := Sig{KID: caCerTBS.KID} - sdTBS := SignedDataTBS{T: "cer", V: cerTBS, KID: sig.KID} - { - hasher := HasherByKeyAlgo(caCerTBS.Pub.A) - hasher.Write(gyac.EncodeItem(nil, gyac.ItemFromGo(sdTBS))) - sig.Sign = AV{A: caCerTBS.Pub.A} - sig.Sign.V, err = caPrv.SignDigest(hasher.Sum(nil), rand.Reader) - if err != nil { - log.Fatal(err) - } - } - cer := SignedData{Load: SignedDataLoad{T: "cer", V: cerTBS}, Sigs: []Sig{sig}} - err = os.WriteFile(*cerPath, gyac.EncodeItem(nil, gyac.ItemFromGo(cer)), 0o666) - if err != nil { - log.Fatal(err) - } -} diff --git a/gyac/cmd/certool/basic.t b/gyac/cmd/yacertool/basic.t similarity index 74% rename from gyac/cmd/certool/basic.t rename to gyac/cmd/yacertool/basic.t index 9db0be1..a4ea1ca 100755 --- a/gyac/cmd/certool/basic.t +++ b/gyac/cmd/yacertool/basic.t @@ -7,30 +7,30 @@ test_description="Check that basic functionality works" TMPDIR=${TMPDIR:-/tmp} subj='{"CN": "CA", "C": "RU"}' -test_expect_success "CA generation" "certool \ +test_expect_success "CA generation" "yacertool \ -algo gost3410-512C \ -ku ca \ -prv $TMPDIR/ca.prv -cer $TMPDIR/ca.cer \ -subj '$subj'" -test_expect_success "CA regeneration" "certool \ +test_expect_success "CA regeneration" "yacertool \ -algo gost3410-512C \ -ku ca \ -prv $TMPDIR/ca.prv -cer $TMPDIR/ca.cer \ -reuse-key \ -subj '$subj'" -test_expect_success "CA self-signature" "certool \ +test_expect_success "CA self-signature" "yacertool \ -ca-cer $TMPDIR/ca.cer \ -cer $TMPDIR/ca.cer \ -verify" subj='{"CN": "EE", "C": "RU"}' -test_expect_success "EE generation" "certool \ +test_expect_success "EE generation" "yacertool \ -algo gost3410-256A \ -ca-prv $TMPDIR/ca.prv -ca-cer $TMPDIR/ca.cer \ -prv $TMPDIR/ee.prv -cer $TMPDIR/ee.cer \ -subj '$subj'" -test_expect_success "EE chain" "certool \ +test_expect_success "EE chain" "yacertool \ -ca-cer $TMPDIR/ca.cer \ -cer $TMPDIR/ee.cer \ -verify" diff --git a/gyac/cmd/certool/go.mod b/gyac/cmd/yacertool/go.mod similarity index 63% rename from gyac/cmd/certool/go.mod rename to gyac/cmd/yacertool/go.mod index c8dfebd..6c9c464 100644 --- a/gyac/cmd/certool/go.mod +++ b/gyac/cmd/yacertool/go.mod @@ -1,4 +1,4 @@ -module go.cypherpunks.su/yac/gyac/cmd/certool +module go.cypherpunks.su/yac/gyac/cmd/yacertool go 1.22 @@ -6,6 +6,7 @@ require ( github.com/google/uuid v1.6.0 go.cypherpunks.su/gogost/v6 v6.0.1 go.cypherpunks.su/yac/gyac v0.0.0-00010101000000-000000000000 + go.cypherpunks.su/yac/gyac/yacpki v0.0.0-00010101000000-000000000000 ) require ( @@ -14,3 +15,5 @@ require ( ) replace go.cypherpunks.su/yac/gyac => ../.. + +replace go.cypherpunks.su/yac/gyac/yacpki => ../../yacpki diff --git a/gyac/cmd/certool/go.sum b/gyac/cmd/yacertool/go.sum similarity index 100% rename from gyac/cmd/certool/go.sum rename to gyac/cmd/yacertool/go.sum diff --git a/gyac/cmd/yacertool/main.go b/gyac/cmd/yacertool/main.go new file mode 100644 index 0000000..ade183a --- /dev/null +++ b/gyac/cmd/yacertool/main.go @@ -0,0 +1,193 @@ +package main + +import ( + "crypto" + "crypto/rand" + "encoding/json" + "flag" + "fmt" + "io" + "log" + "os" + "sort" + "time" + + "go.cypherpunks.su/gogost/v6/gost3410" + "go.cypherpunks.su/yac/gyac" + "go.cypherpunks.su/yac/gyac/yacpki" +) + +func MustReadFile(p string) []byte { + data, err := os.ReadFile(p) + if err != nil { + panic(fmt.Errorf("read %s: %v", p, err)) + } + return data +} + +func main() { + kuMap := make(map[string]struct{}) + flag.Func( + "ku", + "Optional key usage, may be specified multiple times", + func(v string) error { + kuMap[v] = struct{}{} + return nil + }, + ) + sinceRaw := flag.String("since", "", + "Optional notBefore, \"2006-01-02 15:04:05\" format") + lifetime := flag.Uint("lifetime", 365, + "Lifetime of the certificate, days") + subjRaw := flag.String("subj", `{"CN": "test"}`, + "JSON map of the subject") + algo := flag.String("algo", "gost3410-256A", "Public key algorithm") + issuingCer := flag.String("ca-cer", "", + "Path to certificate file for issuing with") + issuingPrv := flag.String("ca-prv", "", + "Path to private key file for issuing with") + reuseKey := flag.Bool("reuse-key", false, + "Reuse the key, do not generate new one") + prvPath := flag.String("prv", "", "Path to private key file") + cerPath := flag.String("cer", "", "Path to certificate file") + verify := flag.Bool("verify", false, "Verify provided -cer with -ca-cer") + + flag.Parse() + log.SetFlags(log.Lshortfile) + + if *cerPath == "" { + log.Fatal("no -cer is set") + } + + var ku []string + for k := range kuMap { + ku = append(ku, k) + } + kuMap = nil + sort.Sort(gyac.ByLenFirst(ku)) + + var subj map[string]string + err := json.Unmarshal([]byte(*subjRaw), &subj) + if err != nil { + log.Fatalln("while parsing -subj:", err) + } + + var since time.Time + if *sinceRaw == "" { + since = time.Now().UTC().Truncate(time.Second) + } else { + since, err = time.Parse(time.DateTime, *sinceRaw) + if err != nil { + log.Fatalln("while parsing -since:", err) + } + } + till := since.Add(time.Duration(*lifetime) * 24 * time.Hour) + + var caPrv *gost3410.PrivateKey + var caCerLoad *yacpki.CerLoad + if *issuingCer != "" { + var sd *yacpki.SignedData + sd, _, err = yacpki.CerParse(MustReadFile(*issuingCer)) + if err != nil { + log.Fatal(err) + } + caCerLoad = sd.Load.V.(*yacpki.CerLoad) + if !*verify { + if *issuingPrv == "" { + log.Fatal("no -issuing-key is set") + } + var signer crypto.Signer + signer, err = yacpki.PrvParse(MustReadFile(*issuingPrv)) + if err != nil { + log.Fatal(err) + } + caPrv = signer.(*gost3410.PrivateKey) + } + } + + if *verify { + var sd *yacpki.SignedData + sd, _, err = yacpki.CerParse(MustReadFile(*cerPath)) + if err != nil { + log.Fatal(err) + } + cerLoad := sd.Load.V.(*yacpki.CerLoad) + sig := sd.Sigs[0] + if sig.KID != cerLoad.KID && !caCerLoad.HasCA() { + log.Fatal("no \"ca\" KU met in CA") + } + err = sd.CheckSignatureFrom(caCerLoad) + if err != nil { + if err == yacpki.SigInvalid { + os.Exit(1) + } + log.Fatal(err) + } + return + } + + if *prvPath == "" { + log.Fatal("no -prv is set") + } + + curve := yacpki.GOST3410CurveByName(*algo) + if curve == nil { + log.Fatal("unknown -algo specified") + } + var prv *gost3410.PrivateKey + if *reuseKey { + var signer crypto.Signer + signer, err = yacpki.PrvParse(MustReadFile(*prvPath)) + if err != nil { + log.Fatal(err) + } + prv = signer.(*gost3410.PrivateKey) + if 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) + } + prv, err = gost3410.NewPrivateKeyBE(curve, prvRaw) + if err != nil { + log.Fatal(err) + } + err = os.WriteFile(*prvPath, gyac.EncodeItem(nil, + gyac.ItemFromGo(yacpki.AV{A: *algo, V: prv.RawBE()})), 0o600) + if err != nil { + log.Fatal(err) + } + } + + var pub *gost3410.PublicKey + pub, err = prv.PublicKey() + if err != nil { + log.Fatal(err) + } + cerLoad := yacpki.CerLoad{ + KU: ku, + Exp: []time.Time{since, till}, + Subj: subj, + Pub: yacpki.AV{A: *algo, V: pub.RawBE()}, + } + cerLoad.KID = yacpki.KIDFromPub(&cerLoad.Pub) + if caPrv == nil { + caPrv = prv + caCerLoad = &cerLoad + } else { + if !caCerLoad.HasCA() { + log.Fatal("no \"ca\" KU met in CA") + } + } + sd := yacpki.SignedData{Load: yacpki.SignedDataLoad{T: "cer", V: cerLoad}} + err = sd.SignWith(caCerLoad, caPrv) + if err != nil { + log.Fatal(err) + } + err = os.WriteFile(*cerPath, gyac.EncodeItem(nil, gyac.ItemFromGo(sd)), 0o666) + if err != nil { + log.Fatal(err) + } +} diff --git a/gyac/reflect.go b/gyac/reflect.go index b1be6ef..d4f9514 100644 --- a/gyac/reflect.go +++ b/gyac/reflect.go @@ -225,16 +225,16 @@ func MapToStruct(dst any, src map[string]any) error { return decoder.Decode(src) } -func DecodeToStruct(dst any, raw []byte) error { - item, tail, err := DecodeItem(raw) +func DecodeToStruct(dst any, raw []byte) (tail []byte, err error) { + var item *Item + item, tail, err = DecodeItem(raw) if err != nil { - return err - } - if len(tail) != 0 { - return errors.New("trailing data") + return } if ItemType(item.T) != ItemMap { - return errors.New("non-map") + err = errors.New("non-map") + return } - return MapToStruct(dst, item.ToGo().(map[string]any)) + err = MapToStruct(dst, item.ToGo().(map[string]any)) + return } diff --git a/gyac/yacpki/cer.go b/gyac/yacpki/cer.go new file mode 100644 index 0000000..6a40913 --- /dev/null +++ b/gyac/yacpki/cer.go @@ -0,0 +1,147 @@ +package yacpki + +import ( + "bytes" + "crypto" + "crypto/rand" + "errors" + "time" + + "github.com/google/uuid" + "go.cypherpunks.su/gogost/v6/gost3410" + "go.cypherpunks.su/gogost/v6/gost34112012256" + "go.cypherpunks.su/yac/gyac" +) + +type AV struct { + A string `yac:"a"` + V []byte `yac:"v"` +} + +type SignedDataLoad struct { + V any `yac:"v"` + T string `yac:"t"` +} + +type SignedDataTBS struct { + V any `yac:"v"` + Load map[string]any `yac:"load"` + T string `yac:"t"` + KID uuid.UUID `yac:"kid"` +} + +type Sig struct { + Load map[string]any `yac:"load,omitempty"` + CerLoc []string `yac:"cer-loc,omitempty"` + Sign AV `yac:"sign"` + KID uuid.UUID `yac:"kid"` +} + +type SignedData struct { + Hashes []string `yac:"hash,omitempty"` + Load SignedDataLoad `yac:"load"` + Sigs []Sig `yac:"sigs"` + Cers []SignedData `yac:"certs,omitempty"` +} + +type CerLoad struct { + KU []string `yac:"ku,omitempty"` + Subj map[string]string `yac:"sub"` + Exp []time.Time `yac:"exp"` + Crit []map[string]any `yac:"crit,omitempty"` + Pub AV `yac:"pub"` + KID uuid.UUID `yac:"kid"` +} + +func (tbs *CerLoad) HasCA() (hasCA bool) { + for _, ku := range tbs.KU { + if ku == "ca" { + hasCA = true + } + } + return +} + +func KIDFromPub(pub *AV) (kid uuid.UUID) { + hasher := gost34112012256.New() + hasher.Write(gyac.EncodeItem(nil, gyac.ItemFromGo(pub))) + var err error + kid, err = uuid.NewRandomFromReader(bytes.NewReader(hasher.Sum(nil))) + if err != nil { + panic(err) + } + return +} + +var SigInvalid = errors.New("signature is invalid") + +func (cer *CerLoad) CheckSignature(signed, signature []byte) (err error) { + switch cer.Pub.A { + case AlgoGOST3410256A, AlgoGOST3410256B, AlgoGOST3410256C, AlgoGOST3410256D, AlgoGOST3410512A, AlgoGOST3410512B, AlgoGOST3410512C: + var pub *gost3410.PublicKey + pub, err = gost3410.NewPublicKeyBE(GOST3410CurveByName(cer.Pub.A), cer.Pub.V) + if err != nil { + return + } + hasher := HasherByKeyAlgo(cer.Pub.A) + hasher.Write(signed) + var valid bool + valid, err = pub.VerifyDigest(hasher.Sum(nil), signature) + if !valid { + err = SigInvalid + } + default: + err = errors.New("unsupported signature algorithm") + } + return +} + +func (sd *SignedData) CheckSignatureFrom(parent *CerLoad) (err error) { + sig := sd.Sigs[0] + if sig.KID != parent.KID { + err = errors.New("signer KID != parent KID") + return + } + tbs := SignedDataTBS{T: sd.Load.T, V: sd.Load.V, KID: parent.KID} + return parent.CheckSignature( + gyac.EncodeItem(nil, gyac.ItemFromGo(tbs)), + sig.Sign.V, + ) +} + +func (sd *SignedData) SignWith(parent *CerLoad, prv crypto.Signer) (err error) { + tbs := SignedDataTBS{T: sd.Load.T, V: sd.Load.V, KID: parent.KID} + hasher := HasherByKeyAlgo(parent.Pub.A) + hasher.Write(gyac.EncodeItem(nil, gyac.ItemFromGo(tbs))) + sig := Sig{KID: parent.KID, Sign: AV{A: parent.Pub.A}} + sig.Sign.V, err = prv.Sign(rand.Reader, hasher.Sum(nil), nil) + if err != nil { + return + } + sd.Sigs = append(sd.Sigs, sig) + return +} + +func CerParse(data []byte) (sd *SignedData, tail []byte, err error) { + var s SignedData + tail, err = gyac.DecodeToStruct(&s, data) + if err != nil { + return + } + sd = &s + if sd.Load.T != "cer" { + err = errors.New("SignedData: not \"cer\" type") + return + } + if len(sd.Sigs) == 0 { + err = errors.New("SignedData: no \"sigs\"") + return + } + var tbs CerLoad + err = gyac.MapToStruct(&tbs, sd.Load.V.(map[string]any)) + if err != nil { + return + } + sd.Load.V = &tbs + return +} diff --git a/gyac/yacpki/crypto.go b/gyac/yacpki/crypto.go new file mode 100644 index 0000000..0507478 --- /dev/null +++ b/gyac/yacpki/crypto.go @@ -0,0 +1,53 @@ +package yacpki + +import ( + "hash" + "log" + + "go.cypherpunks.su/gogost/v6/gost3410" + "go.cypherpunks.su/gogost/v6/gost34112012256" + "go.cypherpunks.su/gogost/v6/gost34112012512" +) + +const ( + AlgoStreebog256 = "streebog256" + AlgoGOST3410256A = "gost3410-256A" + AlgoGOST3410256B = "gost3410-256B" + AlgoGOST3410256C = "gost3410-256C" + AlgoGOST3410256D = "gost3410-256D" + AlgoGOST3410512A = "gost3410-512A" + AlgoGOST3410512B = "gost3410-512B" + AlgoGOST3410512C = "gost3410-512C" +) + +func GOST3410CurveByName(name string) (curve *gost3410.Curve) { + switch name { + case AlgoGOST3410256A: + curve = gost3410.CurveIdtc26gost341012256paramSetA() + case AlgoGOST3410256B: + curve = gost3410.CurveIdtc26gost341012256paramSetB() + case AlgoGOST3410256C: + curve = gost3410.CurveIdtc26gost341012256paramSetC() + case AlgoGOST3410256D: + curve = gost3410.CurveIdtc26gost341012256paramSetD() + case AlgoGOST3410512A: + curve = gost3410.CurveIdtc26gost341012512paramSetA() + case AlgoGOST3410512B: + curve = gost3410.CurveIdtc26gost341012512paramSetB() + case AlgoGOST3410512C: + curve = gost3410.CurveIdtc26gost341012512paramSetC() + } + return +} + +func HasherByKeyAlgo(a string) hash.Hash { + switch a { + case AlgoGOST3410256A, AlgoGOST3410256B, AlgoGOST3410256C, AlgoGOST3410256D: + return gost34112012256.New() + case AlgoGOST3410512A, AlgoGOST3410512B, AlgoGOST3410512C: + return gost34112012512.New() + default: + log.Fatal("unsupported CA algorithm") + } + return nil +} diff --git a/gyac/yacpki/go.mod b/gyac/yacpki/go.mod new file mode 100644 index 0000000..71040d4 --- /dev/null +++ b/gyac/yacpki/go.mod @@ -0,0 +1,15 @@ +module go.cypherpunks.su/yac/gyac/yacpki + +go 1.22 + +require go.cypherpunks.su/tai64n/v3 v3.1.0 // indirect + +require ( + github.com/google/uuid v1.6.0 + go.cypherpunks.su/gogost/v6 v6.0.1 + go.cypherpunks.su/yac/gyac v0.0.0-00010101000000-000000000000 +) + +require github.com/mitchellh/mapstructure v1.5.0 // indirect + +replace go.cypherpunks.su/yac/gyac => .. diff --git a/gyac/yacpki/go.sum b/gyac/yacpki/go.sum new file mode 100644 index 0000000..90b7e96 --- /dev/null +++ b/gyac/yacpki/go.sum @@ -0,0 +1,10 @@ +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +go.cypherpunks.su/gogost/v6 v6.0.1 h1:PFjBnUmfdbx7L5R6hRt/+ZgGwWx45wTIWezFSgmknrs= +go.cypherpunks.su/gogost/v6 v6.0.1/go.mod h1:qJm0B7KJY4/OD5nYqL10kXY09dUwu2AfwSPu72Otngs= +go.cypherpunks.su/tai64n/v3 v3.1.0 h1:cdGnanxA5/H3hc37BO9D3h/exChVNEvrPWjTT/kuwQ4= +go.cypherpunks.su/tai64n/v3 v3.1.0/go.mod h1:zGDFuyiFKJk+iem8lyBaFeCm+MNMOn7RRWy456n1J78= +golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= diff --git a/gyac/cmd/certool/prv.go b/gyac/yacpki/prv.go similarity index 83% rename from gyac/cmd/certool/prv.go rename to gyac/yacpki/prv.go index ac3cf3b..17d0179 100644 --- a/gyac/cmd/certool/prv.go +++ b/gyac/yacpki/prv.go @@ -1,22 +1,23 @@ -package main +package yacpki import ( + "crypto" + "errors" "fmt" - "os" "go.cypherpunks.su/gogost/v6/gost3410" "go.cypherpunks.su/yac/gyac" ) -func loadPrvFromFile(pth string) (prv *gost3410.PrivateKey, err error) { - var data []byte - data, err = os.ReadFile(pth) +func PrvParse(data []byte) (prv crypto.Signer, err error) { + var av AV + var tail []byte + tail, err = gyac.DecodeToStruct(&av, data) if err != nil { return } - var av AV - err = gyac.DecodeToStruct(&av, data) - if err != nil { + if len(tail) != 0 { + err = errors.New("trailing data") return } switch av.A { @@ -50,7 +51,6 @@ func loadPrvFromFile(pth string) (prv *gost3410.PrivateKey, err error) { ) default: err = fmt.Errorf("unknown private key algo: %s", av.A) - return } return }