mirror of
https://github.com/golang/go
synced 2024-11-24 21:00:09 -07:00
asn1: add big support.
Initially I wanted to minimise dependencies but it's become clear that big int support in ASN.1 is a common need and that it should be part of the core. R=bradfitz CC=golang-dev https://golang.org/cl/4550063
This commit is contained in:
parent
4fdcb7b684
commit
7f099cdc76
@ -20,6 +20,7 @@ package asn1
|
|||||||
// everything by any means.
|
// everything by any means.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"big"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -88,6 +89,27 @@ func parseInt(bytes []byte) (int, os.Error) {
|
|||||||
return int(ret64), nil
|
return int(ret64), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var bigOne = big.NewInt(1)
|
||||||
|
|
||||||
|
// parseBigInt treats the given bytes as a big-endian, signed integer and returns
|
||||||
|
// the result.
|
||||||
|
func parseBigInt(bytes []byte) *big.Int {
|
||||||
|
ret := new(big.Int)
|
||||||
|
if len(bytes) > 0 && bytes[0]&0x80 == 0x80 {
|
||||||
|
// This is a negative number.
|
||||||
|
notBytes := make([]byte, len(bytes))
|
||||||
|
for i := range notBytes {
|
||||||
|
notBytes[i] = ^bytes[i]
|
||||||
|
}
|
||||||
|
ret.SetBytes(notBytes)
|
||||||
|
ret.Add(ret, bigOne)
|
||||||
|
ret.Neg(ret)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
ret.SetBytes(bytes)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
// BIT STRING
|
// BIT STRING
|
||||||
|
|
||||||
// BitString is the structure to use when you want an ASN.1 BIT STRING type. A
|
// BitString is the structure to use when you want an ASN.1 BIT STRING type. A
|
||||||
@ -425,6 +447,7 @@ var (
|
|||||||
timeType = reflect.TypeOf(&time.Time{})
|
timeType = reflect.TypeOf(&time.Time{})
|
||||||
rawValueType = reflect.TypeOf(RawValue{})
|
rawValueType = reflect.TypeOf(RawValue{})
|
||||||
rawContentsType = reflect.TypeOf(RawContent(nil))
|
rawContentsType = reflect.TypeOf(RawContent(nil))
|
||||||
|
bigIntType = reflect.TypeOf(new(big.Int))
|
||||||
)
|
)
|
||||||
|
|
||||||
// invalidLength returns true iff offset + length > sliceLength, or if the
|
// invalidLength returns true iff offset + length > sliceLength, or if the
|
||||||
@ -639,6 +662,10 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
|
|||||||
case flagType:
|
case flagType:
|
||||||
v.SetBool(true)
|
v.SetBool(true)
|
||||||
return
|
return
|
||||||
|
case bigIntType:
|
||||||
|
parsedInt := parseBigInt(innerBytes)
|
||||||
|
v.Set(reflect.ValueOf(parsedInt))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
switch val := v; val.Kind() {
|
switch val := v; val.Kind() {
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
|
@ -42,6 +42,33 @@ func TestParseInt64(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var bigIntTests = []struct {
|
||||||
|
in []byte
|
||||||
|
base10 string
|
||||||
|
}{
|
||||||
|
{[]byte{0xff}, "-1"},
|
||||||
|
{[]byte{0x00}, "0"},
|
||||||
|
{[]byte{0x01}, "1"},
|
||||||
|
{[]byte{0x00, 0xff}, "255"},
|
||||||
|
{[]byte{0xff, 0x00}, "-256"},
|
||||||
|
{[]byte{0x01, 0x00}, "256"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseBigInt(t *testing.T) {
|
||||||
|
for i, test := range bigIntTests {
|
||||||
|
ret := parseBigInt(test.in)
|
||||||
|
if ret.String() != test.base10 {
|
||||||
|
t.Errorf("#%d: bad result from %x, got %s want %s", i, test.in, ret.String(), test.base10)
|
||||||
|
}
|
||||||
|
fw := newForkableWriter()
|
||||||
|
marshalBigInt(fw, ret)
|
||||||
|
result := fw.Bytes()
|
||||||
|
if !bytes.Equal(result, test.in) {
|
||||||
|
t.Errorf("#%d: got %x from marshaling %s, want %x", i, result, ret, test.in)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type bitStringTest struct {
|
type bitStringTest struct {
|
||||||
in []byte
|
in []byte
|
||||||
ok bool
|
ok bool
|
||||||
|
@ -132,6 +132,8 @@ func getUniversalType(t reflect.Type) (tagNumber int, isCompound, ok bool) {
|
|||||||
return tagUTCTime, false, true
|
return tagUTCTime, false, true
|
||||||
case enumeratedType:
|
case enumeratedType:
|
||||||
return tagEnum, false, true
|
return tagEnum, false, true
|
||||||
|
case bigIntType:
|
||||||
|
return tagInteger, false, true
|
||||||
}
|
}
|
||||||
switch t.Kind() {
|
switch t.Kind() {
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
package asn1
|
package asn1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"big"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -125,6 +126,43 @@ func int64Length(i int64) (numBytes int) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func marshalBigInt(out *forkableWriter, n *big.Int) (err os.Error) {
|
||||||
|
if n.Sign() < 0 {
|
||||||
|
// A negative number has to be converted to two's-complement
|
||||||
|
// form. So we'll subtract 1 and invert. If the
|
||||||
|
// most-significant-bit isn't set then we'll need to pad the
|
||||||
|
// beginning with 0xff in order to keep the number negative.
|
||||||
|
nMinus1 := new(big.Int).Neg(n)
|
||||||
|
nMinus1.Sub(nMinus1, bigOne)
|
||||||
|
bytes := nMinus1.Bytes()
|
||||||
|
for i := range bytes {
|
||||||
|
bytes[i] ^= 0xff
|
||||||
|
}
|
||||||
|
if len(bytes) == 0 || bytes[0]&0x80 == 0 {
|
||||||
|
err = out.WriteByte(0xff)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err = out.Write(bytes)
|
||||||
|
} else if n.Sign() == 0 {
|
||||||
|
// Zero is written as a single 0 zero rather than no bytes.
|
||||||
|
err = out.WriteByte(0x00)
|
||||||
|
} else {
|
||||||
|
bytes := n.Bytes()
|
||||||
|
if len(bytes) > 0 && bytes[0]&0x80 != 0 {
|
||||||
|
// We'll have to pad this with 0x00 in order to stop it
|
||||||
|
// looking like a negative number.
|
||||||
|
err = out.WriteByte(0)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err = out.Write(bytes)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func marshalLength(out *forkableWriter, i int) (err os.Error) {
|
func marshalLength(out *forkableWriter, i int) (err os.Error) {
|
||||||
n := lengthLength(i)
|
n := lengthLength(i)
|
||||||
|
|
||||||
@ -334,6 +372,8 @@ func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameter
|
|||||||
return marshalBitString(out, value.Interface().(BitString))
|
return marshalBitString(out, value.Interface().(BitString))
|
||||||
case objectIdentifierType:
|
case objectIdentifierType:
|
||||||
return marshalObjectIdentifier(out, value.Interface().(ObjectIdentifier))
|
return marshalObjectIdentifier(out, value.Interface().(ObjectIdentifier))
|
||||||
|
case bigIntType:
|
||||||
|
return marshalBigInt(out, value.Interface().(*big.Int))
|
||||||
}
|
}
|
||||||
|
|
||||||
switch v := value; v.Kind() {
|
switch v := value; v.Kind() {
|
||||||
@ -420,6 +460,9 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
|
|||||||
|
|
||||||
if v.Type() == rawValueType {
|
if v.Type() == rawValueType {
|
||||||
rv := v.Interface().(RawValue)
|
rv := v.Interface().(RawValue)
|
||||||
|
if rv.Class == 0 && rv.Tag == 0 && len(rv.Bytes) == 0 && params.optional {
|
||||||
|
return
|
||||||
|
}
|
||||||
err = marshalTagAndLength(out, tagAndLength{rv.Class, rv.Tag, len(rv.Bytes), rv.IsCompound})
|
err = marshalTagAndLength(out, tagAndLength{rv.Class, rv.Tag, len(rv.Bytes), rv.IsCompound})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
Loading…
Reference in New Issue
Block a user