Commit a3a1bdff authored by sergeilem's avatar sergeilem Committed by Andrew Bonventre

encoding/asn1: handle ASN1's string type BMPString

This code enables handling of ASN1's string type BMPString, used in some digital signatures.
Parsing code taken from golang.org/x/crypto/pkcs12.

Change-Id: Ibeae9cf4d8ae7c18f8b5420ad9244a16e117ff6b
GitHub-Last-Rev: 694525351411f2ec3982a6bf4ac33be892ce1b12
GitHub-Pull-Request: golang/go#26690
Reviewed-on: https://go-review.googlesource.com/c/go/+/126624
Run-TryBot: Andrew Bonventre <andybons@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarAndrew Bonventre <andybons@golang.org>
parent 5e907e38
...@@ -27,6 +27,7 @@ import ( ...@@ -27,6 +27,7 @@ import (
"reflect" "reflect"
"strconv" "strconv"
"time" "time"
"unicode/utf16"
"unicode/utf8" "unicode/utf8"
) )
...@@ -475,6 +476,29 @@ func parseUTF8String(bytes []byte) (ret string, err error) { ...@@ -475,6 +476,29 @@ func parseUTF8String(bytes []byte) (ret string, err error) {
return string(bytes), nil return string(bytes), nil
} }
// BMPString
// parseBMPString parses an ASN.1 BMPString (Basic Multilingual Plane of
// ISO/IEC/ITU 10646-1) from the given byte slice and returns it.
func parseBMPString(bmpString []byte) (string, error) {
if len(bmpString)%2 != 0 {
return "", errors.New("pkcs12: odd-length BMP string")
}
// Strip terminator if present.
if l := len(bmpString); l >= 2 && bmpString[l-1] == 0 && bmpString[l-2] == 0 {
bmpString = bmpString[:l-2]
}
s := make([]uint16, 0, len(bmpString)/2)
for len(bmpString) > 0 {
s = append(s, uint16(bmpString[0])<<8+uint16(bmpString[1]))
bmpString = bmpString[2:]
}
return string(utf16.Decode(s)), nil
}
// A RawValue represents an undecoded ASN.1 object. // A RawValue represents an undecoded ASN.1 object.
type RawValue struct { type RawValue struct {
Class, Tag int Class, Tag int
...@@ -589,7 +613,7 @@ func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type ...@@ -589,7 +613,7 @@ func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type
return return
} }
switch t.tag { switch t.tag {
case TagIA5String, TagGeneralString, TagT61String, TagUTF8String, TagNumericString: case TagIA5String, TagGeneralString, TagT61String, TagUTF8String, TagNumericString, TagBMPString:
// We pretend that various other string types are // We pretend that various other string types are
// PRINTABLE STRINGs so that a sequence of them can be // PRINTABLE STRINGs so that a sequence of them can be
// parsed into a []string. // parsed into a []string.
...@@ -691,6 +715,8 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam ...@@ -691,6 +715,8 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
result, err = parseGeneralizedTime(innerBytes) result, err = parseGeneralizedTime(innerBytes)
case TagOctetString: case TagOctetString:
result = innerBytes result = innerBytes
case TagBMPString:
result, err = parseBMPString(innerBytes)
default: default:
// If we don't know how to handle the type, we just leave Value as nil. // If we don't know how to handle the type, we just leave Value as nil.
} }
...@@ -759,7 +785,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam ...@@ -759,7 +785,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
if universalTag == TagPrintableString { if universalTag == TagPrintableString {
if t.class == ClassUniversal { if t.class == ClassUniversal {
switch t.tag { switch t.tag {
case TagIA5String, TagGeneralString, TagT61String, TagUTF8String, TagNumericString: case TagIA5String, TagGeneralString, TagT61String, TagUTF8String, TagNumericString, TagBMPString:
universalTag = t.tag universalTag = t.tag
} }
} else if params.stringType != 0 { } else if params.stringType != 0 {
...@@ -957,6 +983,9 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam ...@@ -957,6 +983,9 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
// that allow the encoding to change midstring and // that allow the encoding to change midstring and
// such. We give up and pass it as an 8-bit string. // such. We give up and pass it as an 8-bit string.
v, err = parseT61String(innerBytes) v, err = parseT61String(innerBytes)
case TagBMPString:
v, err = parseBMPString(innerBytes)
default: default:
err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)} err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)}
} }
......
...@@ -6,6 +6,7 @@ package asn1 ...@@ -6,6 +6,7 @@ package asn1
import ( import (
"bytes" "bytes"
"encoding/hex"
"fmt" "fmt"
"math" "math"
"math/big" "math/big"
...@@ -1096,3 +1097,35 @@ func TestTaggedRawValue(t *testing.T) { ...@@ -1096,3 +1097,35 @@ func TestTaggedRawValue(t *testing.T) {
} }
} }
} }
var bmpStringTests = []struct {
decoded string
encodedHex string
}{
{"", "0000"},
// Example from https://tools.ietf.org/html/rfc7292#appendix-B.
{"Beavis", "0042006500610076006900730000"},
// Some characters from the "Letterlike Symbols Unicode block".
{"\u2115 - Double-struck N", "21150020002d00200044006f00750062006c0065002d00730074007200750063006b0020004e0000"},
}
func TestBMPString(t *testing.T) {
for i, test := range bmpStringTests {
encoded, err := hex.DecodeString(test.encodedHex)
if err != nil {
t.Fatalf("#%d: failed to decode from hex string", i)
}
decoded, err := parseBMPString(encoded)
if err != nil {
t.Errorf("#%d: decoding output gave an error: %s", i, err)
continue
}
if decoded != test.decoded {
t.Errorf("#%d: decoding output resulted in %q, but it should have been %q", i, decoded, test.decoded)
continue
}
}
}
...@@ -37,6 +37,7 @@ const ( ...@@ -37,6 +37,7 @@ const (
TagUTCTime = 23 TagUTCTime = 23
TagGeneralizedTime = 24 TagGeneralizedTime = 24
TagGeneralString = 27 TagGeneralString = 27
TagBMPString = 30
) )
// ASN.1 class types represent the namespace of the tag. // ASN.1 class types represent the namespace of the tag.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment