mirror of
https://github.com/golang/go
synced 2024-11-21 22:14:41 -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.
|
||||
|
||||
import (
|
||||
"big"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
@ -88,6 +89,27 @@ func parseInt(bytes []byte) (int, os.Error) {
|
||||
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
|
||||
|
||||
// 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{})
|
||||
rawValueType = reflect.TypeOf(RawValue{})
|
||||
rawContentsType = reflect.TypeOf(RawContent(nil))
|
||||
bigIntType = reflect.TypeOf(new(big.Int))
|
||||
)
|
||||
|
||||
// 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:
|
||||
v.SetBool(true)
|
||||
return
|
||||
case bigIntType:
|
||||
parsedInt := parseBigInt(innerBytes)
|
||||
v.Set(reflect.ValueOf(parsedInt))
|
||||
return
|
||||
}
|
||||
switch val := v; val.Kind() {
|
||||
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 {
|
||||
in []byte
|
||||
ok bool
|
||||
|
@ -132,6 +132,8 @@ func getUniversalType(t reflect.Type) (tagNumber int, isCompound, ok bool) {
|
||||
return tagUTCTime, false, true
|
||||
case enumeratedType:
|
||||
return tagEnum, false, true
|
||||
case bigIntType:
|
||||
return tagInteger, false, true
|
||||
}
|
||||
switch t.Kind() {
|
||||
case reflect.Bool:
|
||||
|
@ -5,6 +5,7 @@
|
||||
package asn1
|
||||
|
||||
import (
|
||||
"big"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -125,6 +126,43 @@ func int64Length(i int64) (numBytes int) {
|
||||
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) {
|
||||
n := lengthLength(i)
|
||||
|
||||
@ -334,6 +372,8 @@ func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameter
|
||||
return marshalBitString(out, value.Interface().(BitString))
|
||||
case objectIdentifierType:
|
||||
return marshalObjectIdentifier(out, value.Interface().(ObjectIdentifier))
|
||||
case bigIntType:
|
||||
return marshalBigInt(out, value.Interface().(*big.Int))
|
||||
}
|
||||
|
||||
switch v := value; v.Kind() {
|
||||
@ -420,6 +460,9 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
|
||||
|
||||
if v.Type() == rawValueType {
|
||||
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})
|
||||
if err != nil {
|
||||
return
|
||||
|
Loading…
Reference in New Issue
Block a user