Skip to content

Commit

Permalink
fix: fix bug
Browse files Browse the repository at this point in the history
  • Loading branch information
donutnomad committed Nov 1, 2024
1 parent 555ec4a commit bcd548f
Show file tree
Hide file tree
Showing 5 changed files with 455 additions and 16 deletions.
172 changes: 159 additions & 13 deletions xasn1/mod.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,20 @@ import (
"crypto/x509/pkix"
"encoding/asn1"
"errors"
"github.com/samber/lo"
"golang.org/x/crypto/cryptobyte"
asn11 "golang.org/x/crypto/cryptobyte/asn1"
"math"
"math/big"
)

// A StructuralError suggests that the ASN.1 data is valid, but the Go type
// which is receiving it doesn't match.
type StructuralError = asn1.StructuralError

// A SyntaxError suggests that the ASN.1 data is invalid.
type SyntaxError = asn1.SyntaxError

// ParseBase128Int parses a base-128 encoded int from the given offset in the
// given byte slice. It returns the value and the new offset.
func ParseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error) {
Expand All @@ -19,15 +27,15 @@ func ParseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error)
// 5 * 7 bits per byte == 35 bits of data
// Thus the representation is either non-minimal or too large for an int32
if shifted == 5 {
panic("base 128 integer too large")
err = StructuralError{Msg: "base 128 integer too large"}
return
}
ret64 <<= 7
b := bytes[offset]
// integers should be minimally encoded, so the leading octet should
// never be 0x80
if shifted == 0 && b == 0x80 {
panic("\"integer is not minimally encoded\"")
err = SyntaxError{Msg: "integer is not minimally encoded"}
return
}
ret64 |= int64(b & 0x7f)
Expand All @@ -36,12 +44,96 @@ func ParseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error)
ret = int(ret64)
// Ensure that the returned value fits in an int on all platforms
if ret64 > math.MaxInt32 {
panic("base 128 integer too large")
err = StructuralError{Msg: "base 128 integer too large"}
}
return
}
}
err = SyntaxError{Msg: "truncated base 128 integer"}
return
}

type TagAndLength struct {
Class, Tag, Length int
IsCompound bool
}

// ParseTagAndLength parses an ASN.1 tag and length pair from the given offset
// into a byte slice. It returns the parsed data and the new offset. SET and
// SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we
// don't distinguish between ordered and unordered objects in this code.
func ParseTagAndLength(bytes []byte, initOffset int) (ret TagAndLength, offset int, err error) {
offset = initOffset
// parseTagAndLength should not be called without at least a single
// byte to read. Thus this check is for robustness:
if offset >= len(bytes) {
err = errors.New("asn1: internal error in parseTagAndLength")
return
}
b := bytes[offset]
offset++
ret.Class = int(b >> 6)
ret.IsCompound = b&0x20 == 0x20
ret.Tag = int(b & 0x1f)

// If the bottom five bits are set, then the tag number is actually base 128
// encoded afterwards
if ret.Tag == 0x1f {
ret.Tag, offset, err = ParseBase128Int(bytes, offset)
if err != nil {
return
}
// Tags should be encoded in minimal form.
if ret.Tag < 0x1f {
err = SyntaxError{Msg: "non-minimal tag"}
return
}
}
if offset >= len(bytes) {
err = SyntaxError{Msg: "truncated tag or length"}
return
}
b = bytes[offset]
offset++
if b&0x80 == 0 {
// The length is encoded in the bottom 7 bits.
ret.Length = int(b & 0x7f)
} else {
// Bottom 7 bits give the number of length bytes to follow.
numBytes := int(b & 0x7f)
if numBytes == 0 {
err = SyntaxError{Msg: "indefinite length found (not DER)"}
return
}
ret.Length = 0
for i := 0; i < numBytes; i++ {
if offset >= len(bytes) {
err = SyntaxError{Msg: "truncated tag or length"}
return
}
b = bytes[offset]
offset++
if ret.Length >= 1<<23 {
// We can't shift ret.length up without
// overflowing.
err = StructuralError{Msg: "length too large"}
return
}
ret.Length <<= 8
ret.Length |= int(b)
if ret.Length == 0 {
// DER requires that lengths be minimal.
err = StructuralError{Msg: "superfluous leading zeros in length"}
return
}
}
// Short lengths must be encoded in short form.
if ret.Length < 0x80 {
err = StructuralError{Msg: "non-minimal length"}
return
}
}
panic("base 128 integer too large")

return
}

Expand Down Expand Up @@ -83,24 +175,55 @@ func ParseObjectIdentifier(bytes []byte) (s []int, err error) {
return
}

type publicKeyInfo struct {
func FindTypeList(bs []byte, offset int, target int) [][]byte {
var out [][]byte
for i := offset; i < len(bs); {
ret, ni, err := ParseTagAndLength(bs, i)
if err != nil {
return nil
}
i = ni
if ret.Tag == target {
out = append(out, bs[i:i+ret.Length])
} else if ret.Tag == asn1.TagSequence {
if ret := FindTypeList(bs, i, target); len(ret) > 0 {
out = append(out, ret...)
}
}
i += ret.Length
}
return out
}

func FindOids(bs []byte) []asn1.ObjectIdentifier {
var oids = FindTypeList(bs, 0, asn1.TagOID)
return lo.FilterMap(oids, func(item []byte, index int) (asn1.ObjectIdentifier, bool) {
identifier, err := ParseObjectIdentifier(item)
if err != nil {
return nil, false
}
return identifier, true
})
}

type PublicKeyInfo struct {
Raw asn1.RawContent
Algorithm pkix.AlgorithmIdentifier
PublicKey asn1.BitString
}

// ParsePKIXPublicKey x509.ParsePKIXPublicKey
func ParsePKIXPublicKey(bs []byte) ([]byte, error) {
var pki publicKeyInfo
func ParsePKIXPublicKey(bs []byte) (*PublicKeyInfo, error) {
var pki PublicKeyInfo
if rest, err := asn1.Unmarshal(bs, &pki); err != nil {
return nil, errors.New("x509: failed to parse public key (use ParsePKCS1PublicKey instead for this key format)")
} else if len(rest) != 0 {
return nil, errors.New("x509: trailing data after ASN.1 of public-key")
}
return pki.PublicKey.Bytes, nil
return &pki, nil
}

func ParseSignatureRS(bs []byte) (r *big.Int, s *big.Int, _ error) {
func ParseSignatureRS(bs []byte) (r []byte, s []byte, _ error) {
var inner cryptobyte.String
input := cryptobyte.String(bs)
if !input.ReadASN1(&inner, asn11.SEQUENCE) ||
Expand All @@ -110,15 +233,38 @@ func ParseSignatureRS(bs []byte) (r *big.Int, s *big.Int, _ error) {
!inner.Empty() {
return nil, nil, errors.New("invalid ASN.1")
}
return r, s, nil
return padSliceLeft(r, 32), padSliceLeft(s, 32), nil
}

func ParseSignatureRSSlice(bs []byte) (out [64]byte, _ error) {
func ParseSignatureRSSlice(bs []byte) ([64]byte, error) {
r, s, err := ParseSignatureRS(bs)
if err != nil {
return [64]byte{}, err
}
r.FillBytes(out[0:32])
s.FillBytes(out[32:64])
var out [64]byte
copy(out[0:32], r)
copy(out[32:64], s)
return out, nil
}

func MarshalAsn1SignatureRS(r, s []byte) []byte {
var b cryptobyte.Builder
b.AddASN1(asn11.SEQUENCE, func(child *cryptobyte.Builder) {
child.AddASN1BigInt(new(big.Int).SetBytes(r))
child.AddASN1BigInt(new(big.Int).SetBytes(s))
})
return b.BytesOrPanic()
}

func MarshalAsn1SignatureSlice(bs [64]byte) []byte {
return MarshalAsn1SignatureRS(bs[0:32], bs[32:64])
}

func padSliceLeft(bs []byte, size int) []byte {
if len(bs) >= size {
return bs[:size]
}
var out = make([]byte, size)
copy(out[size-len(bs):], bs[:])
return out
}
27 changes: 27 additions & 0 deletions xasn1/mod_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package xasn1

import (
"bytes"
"crypto/rand"
"github.com/samber/lo"
"testing"
)

func TestMarshalSigRS(t *testing.T) {
var r = mustRand(31)
var s = mustRand(31)
var asn1Sig = MarshalAsn1SignatureRS(r, s)
var rr, ss, err = ParseSignatureRS(asn1Sig)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(padSliceLeft(r, 32), rr) || !bytes.Equal(padSliceLeft(s, 32), ss) {
t.Fatalf("marshal failed")
}
}

func mustRand(size int) []byte {
var out = make([]byte, size)
lo.Must1(rand.Reader.Read(out))
return out
}
3 changes: 2 additions & 1 deletion xed25519/pubkey.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ func ParsePubKey(serialized [32]byte) (key PublicKey, err error) {
}

func ParsePubKeyASN1(bs []byte) (key PublicKey, err error) {
serialized, err := xasn1.ParsePKIXPublicKey(bs)
k, err := xasn1.ParsePKIXPublicKey(bs)
if err != nil {
return PublicKey{}, err
}
serialized := k.PublicKey.Bytes
if len(serialized) != 32 {
return PublicKey{}, BadFormatPublicKeyErr
}
Expand Down
5 changes: 3 additions & 2 deletions xsecp256k1/kit.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ func (k *secp256k1Kit) SignANS1(asn1Key []byte, hash []byte) (SignatureCompat, e
if err != nil {
return SignatureCompat{}, err
}
if len(key) != 32 {
bs := key.PublicKey.Bytes
if len(bs) != 32 {
return SignatureCompat{}, BadFormatPublicKeyErr
}
return k.Sign([32]byte(key), hash), nil
return k.Sign([32]byte(bs), hash), nil
}

func (k *secp256k1Kit) VerifySignatureRS(pubKey []byte, rBig, sBig *big.Int, hash []byte) bool {
Expand Down
Loading

0 comments on commit bcd548f

Please sign in to comment.