From c321c2f0319beedceadffd9a4ecd2727583d365ae4dc89be8f26f33b2f7db32a Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Sun, 15 Dec 2024 12:49:56 +0300 Subject: [PATCH] Less panics --- gyac/cmd/print/main.go | 6 +- gyac/cmd/test-vector-anys/main.go | 7 ++- gyac/enc.go | 2 + gyac/fromgo.go | 92 +++++++++++++++++++------------ gyac/fuzz_test.go | 14 +++-- gyac/mapstruct/dec.go | 7 ++- gyac/pki/av.go | 13 +++-- gyac/pki/cer.go | 7 ++- gyac/pki/cmd/yacertool/main.go | 17 ++++-- gyac/pki/cmd/yacsdtool/main.go | 7 ++- gyac/pki/gost/signer.go | 3 +- gyac/pki/signed-data.go | 14 ++++- gyac/togo.go | 56 +++++++++++-------- 13 files changed, 165 insertions(+), 80 deletions(-) diff --git a/gyac/cmd/print/main.go b/gyac/cmd/print/main.go index f49b610..c4bfee5 100644 --- a/gyac/cmd/print/main.go +++ b/gyac/cmd/print/main.go @@ -22,5 +22,9 @@ func main() { if len(tail) > 0 { log.Fatalln("trailing data:", tail) } - fmt.Printf("%v\n", item.ToGo()) + e, err := item.ToGo() + if err != nil { + log.Fatal(err) + } + fmt.Printf("%v\n", e) } diff --git a/gyac/cmd/test-vector-anys/main.go b/gyac/cmd/test-vector-anys/main.go index c1713d8..a5fd7f5 100644 --- a/gyac/cmd/test-vector-anys/main.go +++ b/gyac/cmd/test-vector-anys/main.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/hex" "fmt" + "log" "math/big" "time" @@ -107,5 +108,9 @@ func main() { }, "uuid": uuid.MustParse("0e875e3f-d385-49eb-87b4-be42d641c367"), } - fmt.Println(hex.EncodeToString(gyac.FromGo(data).Encode(nil))) + item, err := gyac.FromGo(data) + if err != nil { + log.Fatal(err) + } + fmt.Println(hex.EncodeToString(item.Encode(nil))) } diff --git a/gyac/enc.go b/gyac/enc.go index c689ec2..a1f06d4 100644 --- a/gyac/enc.go +++ b/gyac/enc.go @@ -29,6 +29,8 @@ import ( // Encode an item appending to the buf. func (item Item) Encode(buf []byte) []byte { switch item.T { + case types.Invalid: + panic("invalid item's type met") case types.NIL: return atom.NILEncode(buf) case types.Bool: diff --git a/gyac/fromgo.go b/gyac/fromgo.go index 3a815f1..d7beeed 100644 --- a/gyac/fromgo.go +++ b/gyac/fromgo.go @@ -62,19 +62,19 @@ func structTagRead(f reflect.StructField) (name string, omit bool) { // TAI64 (if nanoseconds=0), or TAI64N // - uint, uint8, uint16, uint32, uint64 // - uuid.UUID -func FromGo(v any) Item { +func FromGo(v any) (Item, error) { if v == nil { - return Item{T: types.NIL} + return Item{T: types.NIL}, nil } rv := reflect.ValueOf(v) if b, ok := v.([]byte); ok { - return Item{T: types.Bin, V: b} + return Item{T: types.Bin, V: b}, nil } switch v := v.(type) { case *Blob: - return Item{T: types.Blob, V: *v} + return Item{T: types.Blob, V: *v}, nil case Blob: - return Item{T: types.Blob, V: v} + return Item{T: types.Blob, V: v}, nil case time.Time: t := tai64n.Leapsecs.Add(v) var taiRaw []byte @@ -87,40 +87,57 @@ func FromGo(v any) Item { tai.FromTime(t) taiRaw = tai[:] } - return Item{T: types.TAI64, V: taiRaw} + return Item{T: types.TAI64, V: taiRaw}, nil case *atom.Raw: - return Item{T: types.Raw, V: *v} + return Item{T: types.Raw, V: *v}, nil case atom.Raw: - return Item{T: types.Raw, V: v} + return Item{T: types.Raw, V: v}, nil case *big.Int: - return Item{T: types.BigInt, V: v} + return Item{T: types.BigInt, V: v}, nil } switch reflect.TypeOf(v).Kind() { case reflect.Pointer: if rv.IsNil() { - return Item{T: types.NIL} + return Item{T: types.NIL}, nil } return FromGo(rv.Elem().Interface()) case reflect.Slice: var ret []Item + var err error if anys, ok := v.([]any); ok { for _, v := range anys { - ret = append(ret, FromGo(v)) + var item Item + item, err = FromGo(v) + if err != nil { + return item, err + } + ret = append(ret, item) } } else { rv = reflect.ValueOf(v) for i := 0; i < rv.Len(); i++ { - ret = append(ret, FromGo(rv.Index(i).Interface())) + var item Item + item, err = FromGo(rv.Index(i).Interface()) + if err != nil { + return item, err + } + ret = append(ret, item) } } - return Item{T: types.List, V: ret} + return Item{T: types.List, V: ret}, nil case reflect.Map: ret := make(map[string]Item, rv.Len()) iter := rv.MapRange() + var err error for iter.Next() { - ret[iter.Key().String()] = FromGo(iter.Value().Interface()) + var item Item + item, err = FromGo(iter.Value().Interface()) + if err != nil { + return item, err + } + ret[iter.Key().String()] = item } - return Item{T: types.Map, V: ret} + return Item{T: types.Map, V: ret}, nil } { t := rv.Type() @@ -130,7 +147,10 @@ func FromGo(v any) Item { fv := rv.FieldByIndex(f.Index) name, omit := structTagRead(f) var empty bool - item := FromGo(fv.Interface()) + item, err := FromGo(fv.Interface()) + if err != nil { + return item, err + } switch item.T { case types.NIL: empty = true @@ -147,52 +167,52 @@ func FromGo(v any) Item { ret[name] = item } } - return Item{T: types.Map, V: ret} + return Item{T: types.Map, V: ret}, nil } } switch v := v.(type) { case bool: - return Item{T: types.Bool, V: v} + return Item{T: types.Bool, V: v}, nil case uuid.UUID: - return Item{T: types.UUID, V: v} + return Item{T: types.UUID, V: v}, nil case uint: - return Item{T: types.UInt, V: uint64(v)} + return Item{T: types.UInt, V: uint64(v)}, nil case uint8: - return Item{T: types.UInt, V: uint64(v)} + return Item{T: types.UInt, V: uint64(v)}, nil case uint16: - return Item{T: types.UInt, V: uint64(v)} + return Item{T: types.UInt, V: uint64(v)}, nil case uint32: - return Item{T: types.UInt, V: uint64(v)} + return Item{T: types.UInt, V: uint64(v)}, nil case uint64: - return Item{T: types.UInt, V: v} + return Item{T: types.UInt, V: v}, nil case int: if v >= 0 { - return Item{T: types.UInt, V: uint64(v)} + return Item{T: types.UInt, V: uint64(v)}, nil } - return Item{T: types.Int, V: int64(v)} + return Item{T: types.Int, V: int64(v)}, nil case int8: if v >= 0 { - return Item{T: types.UInt, V: uint64(v)} + return Item{T: types.UInt, V: uint64(v)}, nil } - return Item{T: types.Int, V: int64(v)} + return Item{T: types.Int, V: int64(v)}, nil case int16: if v >= 0 { - return Item{T: types.UInt, V: uint64(v)} + return Item{T: types.UInt, V: uint64(v)}, nil } - return Item{T: types.Int, V: int64(v)} + return Item{T: types.Int, V: int64(v)}, nil case int32: if v >= 0 { - return Item{T: types.UInt, V: uint64(v)} + return Item{T: types.UInt, V: uint64(v)}, nil } - return Item{T: types.Int, V: int64(v)} + return Item{T: types.Int, V: int64(v)}, nil case int64: if v >= 0 { - return Item{T: types.UInt, V: uint64(v)} + return Item{T: types.UInt, V: uint64(v)}, nil } - return Item{T: types.Int, V: v} + return Item{T: types.Int, V: v}, nil case string: - return Item{T: types.Str, V: v} + return Item{T: types.Str, V: v}, nil default: - panic(fmt.Errorf("unhandled type: %+v", v)) + return Item{}, fmt.Errorf("unhandled type: %+v", v) } } diff --git a/gyac/fuzz_test.go b/gyac/fuzz_test.go index 103e4da..f009115 100644 --- a/gyac/fuzz_test.go +++ b/gyac/fuzz_test.go @@ -9,13 +9,19 @@ func FuzzItemDecode(f *testing.F) { var item Item var err error var tail []byte + var e any f.Fuzz(func(t *testing.T, b []byte) { item, tail, err = Decode(b) if err == nil { - if !bytes.Equal( - append(FromGo(item.ToGo()).Encode(nil), tail...), - b, - ) { + e, err = item.ToGo() + if err != nil { + t.Fail() + } + item, err = FromGo(e) + if err != nil { + t.Fail() + } + if !bytes.Equal(append(item.Encode(nil), tail...), b) { t.Fail() } } diff --git a/gyac/mapstruct/dec.go b/gyac/mapstruct/dec.go index 5d214f9..53eaca2 100644 --- a/gyac/mapstruct/dec.go +++ b/gyac/mapstruct/dec.go @@ -34,6 +34,11 @@ func Decode(dst any, raw []byte) (tail []byte, err error) { err = errors.New("non-map") return } - err = FromMap(dst, item.ToGo().(map[string]any)) + var e any + e, err = item.ToGo() + if err != nil { + return + } + err = FromMap(dst, e.(map[string]any)) return } diff --git a/gyac/pki/av.go b/gyac/pki/av.go index 4e601d8..8995419 100644 --- a/gyac/pki/av.go +++ b/gyac/pki/av.go @@ -28,12 +28,17 @@ func (av *AV) Id() (id uuid.UUID) { case GOST3410256A, GOST3410512C: hasher = pkihash.ByName(pkihash.Streebog256) default: - return uuid.Nil + id = uuid.Nil + return } - utils.MustWrite(hasher, gyac.FromGo(av).Encode(nil)) - id, err := uuid.NewRandomFromReader(bytes.NewReader(hasher.Sum(nil))) + item, err := gyac.FromGo(av) if err != nil { panic(err) } - return id + utils.MustWrite(hasher, item.Encode(nil)) + id, err = uuid.NewRandomFromReader(bytes.NewReader(hasher.Sum(nil))) + if err != nil { + panic(err) + } + return } diff --git a/gyac/pki/cer.go b/gyac/pki/cer.go index 4235fc4..2093f57 100644 --- a/gyac/pki/cer.go +++ b/gyac/pki/cer.go @@ -176,7 +176,12 @@ func (sd *SignedData) CerCheckSignatureFrom(parent *CerLoad) (err error) { return } tbs := SignedDataTBS{T: sd.Load.T, V: sd.Load.V, TBS: sig.TBS} - return parent.CheckSignature(gyac.FromGo(tbs).Encode(nil), sig.Sign.V) + var item gyac.Item + item, err = gyac.FromGo(tbs) + if err != nil { + return + } + return parent.CheckSignature(item.Encode(nil), sig.Sign.V) } // Get CerLoad from SignedData. diff --git a/gyac/pki/cmd/yacertool/main.go b/gyac/pki/cmd/yacertool/main.go index c71cd33..39c5f98 100644 --- a/gyac/pki/cmd/yacertool/main.go +++ b/gyac/pki/cmd/yacertool/main.go @@ -121,6 +121,7 @@ func main() { log.Fatal("no -prv is set") } + var item gyac.Item var prv crypto.Signer var prvRaw []byte var pub []byte @@ -141,11 +142,11 @@ func main() { if err != nil { log.Fatal(err) } - err = os.WriteFile( - *prvPath, - gyac.FromGo(pki.AV{A: *algo, V: prvRaw}).Encode(nil), - 0o600, - ) + item, err = gyac.FromGo(pki.AV{A: *algo, V: prvRaw}) + if err != nil { + log.Fatal(err) + } + err = os.WriteFile(*prvPath, item.Encode(nil), 0o600) if err != nil { log.Fatal(err) } @@ -173,7 +174,11 @@ func main() { log.Fatal(err) } - err = os.WriteFile(*cerPath, gyac.FromGo(sd).Encode(nil), 0o666) + item, err = gyac.FromGo(sd) + if err != nil { + log.Fatal(err) + } + err = os.WriteFile(*cerPath, item.Encode(nil), 0o666) if err != nil { log.Fatal(err) } diff --git a/gyac/pki/cmd/yacsdtool/main.go b/gyac/pki/cmd/yacsdtool/main.go index 7b170d9..929356b 100644 --- a/gyac/pki/cmd/yacsdtool/main.go +++ b/gyac/pki/cmd/yacsdtool/main.go @@ -98,7 +98,12 @@ func main() { if err != nil { log.Fatal(err) } - err = os.WriteFile(*sdPath, gyac.FromGo(sd).Encode(nil), 0o666) + var item gyac.Item + item, err = gyac.FromGo(sd) + if err != nil { + log.Fatal(err) + } + err = os.WriteFile(*sdPath, item.Encode(nil), 0o666) if err != nil { log.Fatal(err) } diff --git a/gyac/pki/gost/signer.go b/gyac/pki/gost/signer.go index f1f0772..2f7436b 100644 --- a/gyac/pki/gost/signer.go +++ b/gyac/pki/gost/signer.go @@ -2,6 +2,7 @@ package gost import ( "crypto" + "errors" "hash" "io" @@ -49,7 +50,7 @@ func NewSigner(a string, v []byte) (prv crypto.Signer, pub []byte, err error) { gost3410.CurveIdtc26gost341012512paramSetC(), v, ) default: - panic("unknown GOST algorithm") + err = errors.New("unsupported GOST R 34.10 algorithm") } if err != nil { return diff --git a/gyac/pki/signed-data.go b/gyac/pki/signed-data.go index 30505e5..e35cffc 100644 --- a/gyac/pki/signed-data.go +++ b/gyac/pki/signed-data.go @@ -52,7 +52,12 @@ func SignedDataParseItem(item gyac.Item) (sd *SignedData, err error) { return } var _sd SignedData - err = mapstruct.FromMap(&_sd, item.ToGo().(map[string]any)) + var e any + e, err = item.ToGo() + if err != nil { + return + } + err = mapstruct.FromMap(&_sd, e.(map[string]any)) if err != nil { return } @@ -143,9 +148,14 @@ func (sd *SignedData) SignWith( sdTBS := SignedDataTBS{T: sd.Load.T, V: sd.Load.V, TBS: sigTBS} sig := Sig{TBS: sigTBS} sig.Sign.A = parent.Pub[0].A + var item gyac.Item + item, err = gyac.FromGo(sdTBS) + if err != nil { + return + } sig.Sign.V, err = prv.Sign( rand.Reader, - gyac.FromGo(sdTBS).Encode(nil), + item.Encode(nil), crypto.Hash(0), ) if err != nil { diff --git a/gyac/togo.go b/gyac/togo.go index 27ee36c..c4d5b09 100644 --- a/gyac/togo.go +++ b/gyac/togo.go @@ -28,34 +28,46 @@ import ( // Convert an item to various native Go types, atom.Raw, Blob, uuid.UUID. // Pay attention that f TAI equals to leap second, then it is converted to Raw. -func (item Item) ToGo() any { +func (item Item) ToGo() (any, error) { switch item.T { case types.NIL: - return nil + return nil, nil case types.Bool: - return item.V.(bool) + return item.V.(bool), nil case types.UUID: - return item.V.(uuid.UUID) + return item.V.(uuid.UUID), nil case types.UInt: - return item.V.(uint64) + return item.V.(uint64), nil case types.Int: - return item.V.(int64) + return item.V.(int64), nil case types.List: var ret []any + var err error for _, v := range item.V.([]Item) { - ret = append(ret, v.ToGo()) + var e any + e, err = v.ToGo() + if err != nil { + return nil, err + } + ret = append(ret, e) } - return ret + return ret, nil case types.Map: ret := make(map[string]any) + var err error for k, v := range item.V.(map[string]Item) { - ret[k] = v.ToGo() + var e any + e, err = v.ToGo() + if err != nil { + return nil, err + } + ret[k] = e } - return ret + return ret, nil case types.Blob: - return item.V.(Blob) + return item.V.(Blob), nil case types.BigInt: - return item.V.(*big.Int) + return item.V.(*big.Int), nil case types.Float: panic("float is unsupported") case types.TAI64: @@ -65,28 +77,28 @@ func (item Item) ToGo() any { tai := tai64n.TAI64(raw) t, isLeap := tai64n.Leapsecs.Sub(tai.Time()) if isLeap { - return atom.Raw{T: atom.TAI64, V: raw} + return atom.Raw{T: atom.TAI64, V: raw}, nil } - return t + return t, nil case tai64n.TAI64NSize: tai := tai64n.TAI64N(raw) t, isLeap := tai64n.Leapsecs.Sub(tai.Time()) if isLeap { - return atom.Raw{T: atom.TAI64N, V: raw} + return atom.Raw{T: atom.TAI64N, V: raw}, nil } - return t + return t, nil case tai64n.TAI64NASize: - return atom.Raw{T: atom.TAI64NA, V: raw} + return atom.Raw{T: atom.TAI64NA, V: raw}, nil default: - panic("unexpected TAI size") + panic("wrong TAI64 value") } case types.Bin: - return item.V.([]byte) + return item.V.([]byte), nil case types.Str: - return item.V.(string) + return item.V.(string), nil case types.Raw: - return item.V.(atom.Raw) + return item.V.(atom.Raw), nil default: - panic(fmt.Errorf("unhandled type: %+v", item)) + return nil, fmt.Errorf("unhandled type: %+v", item) } } -- 2.50.0