Skip to content

Commit a3a1bdf

Browse files
sergeilemandybons
authored andcommitted
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: 6945253 GitHub-Pull-Request: #26690 Reviewed-on: https://go-review.googlesource.com/c/go/+/126624 Run-TryBot: Andrew Bonventre <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Andrew Bonventre <[email protected]>
1 parent 5e907e3 commit a3a1bdf

File tree

3 files changed

+65
-2
lines changed

3 files changed

+65
-2
lines changed

src/encoding/asn1/asn1.go

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"reflect"
2828
"strconv"
2929
"time"
30+
"unicode/utf16"
3031
"unicode/utf8"
3132
)
3233

@@ -475,6 +476,29 @@ func parseUTF8String(bytes []byte) (ret string, err error) {
475476
return string(bytes), nil
476477
}
477478

479+
// BMPString
480+
481+
// parseBMPString parses an ASN.1 BMPString (Basic Multilingual Plane of
482+
// ISO/IEC/ITU 10646-1) from the given byte slice and returns it.
483+
func parseBMPString(bmpString []byte) (string, error) {
484+
if len(bmpString)%2 != 0 {
485+
return "", errors.New("pkcs12: odd-length BMP string")
486+
}
487+
488+
// Strip terminator if present.
489+
if l := len(bmpString); l >= 2 && bmpString[l-1] == 0 && bmpString[l-2] == 0 {
490+
bmpString = bmpString[:l-2]
491+
}
492+
493+
s := make([]uint16, 0, len(bmpString)/2)
494+
for len(bmpString) > 0 {
495+
s = append(s, uint16(bmpString[0])<<8+uint16(bmpString[1]))
496+
bmpString = bmpString[2:]
497+
}
498+
499+
return string(utf16.Decode(s)), nil
500+
}
501+
478502
// A RawValue represents an undecoded ASN.1 object.
479503
type RawValue struct {
480504
Class, Tag int
@@ -589,7 +613,7 @@ func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type
589613
return
590614
}
591615
switch t.tag {
592-
case TagIA5String, TagGeneralString, TagT61String, TagUTF8String, TagNumericString:
616+
case TagIA5String, TagGeneralString, TagT61String, TagUTF8String, TagNumericString, TagBMPString:
593617
// We pretend that various other string types are
594618
// PRINTABLE STRINGs so that a sequence of them can be
595619
// parsed into a []string.
@@ -691,6 +715,8 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
691715
result, err = parseGeneralizedTime(innerBytes)
692716
case TagOctetString:
693717
result = innerBytes
718+
case TagBMPString:
719+
result, err = parseBMPString(innerBytes)
694720
default:
695721
// If we don't know how to handle the type, we just leave Value as nil.
696722
}
@@ -759,7 +785,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
759785
if universalTag == TagPrintableString {
760786
if t.class == ClassUniversal {
761787
switch t.tag {
762-
case TagIA5String, TagGeneralString, TagT61String, TagUTF8String, TagNumericString:
788+
case TagIA5String, TagGeneralString, TagT61String, TagUTF8String, TagNumericString, TagBMPString:
763789
universalTag = t.tag
764790
}
765791
} else if params.stringType != 0 {
@@ -957,6 +983,9 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
957983
// that allow the encoding to change midstring and
958984
// such. We give up and pass it as an 8-bit string.
959985
v, err = parseT61String(innerBytes)
986+
case TagBMPString:
987+
v, err = parseBMPString(innerBytes)
988+
960989
default:
961990
err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)}
962991
}

src/encoding/asn1/asn1_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package asn1
66

77
import (
88
"bytes"
9+
"encoding/hex"
910
"fmt"
1011
"math"
1112
"math/big"
@@ -1096,3 +1097,35 @@ func TestTaggedRawValue(t *testing.T) {
10961097
}
10971098
}
10981099
}
1100+
1101+
var bmpStringTests = []struct {
1102+
decoded string
1103+
encodedHex string
1104+
}{
1105+
{"", "0000"},
1106+
// Example from https://tools.ietf.org/html/rfc7292#appendix-B.
1107+
{"Beavis", "0042006500610076006900730000"},
1108+
// Some characters from the "Letterlike Symbols Unicode block".
1109+
{"\u2115 - Double-struck N", "21150020002d00200044006f00750062006c0065002d00730074007200750063006b0020004e0000"},
1110+
}
1111+
1112+
func TestBMPString(t *testing.T) {
1113+
for i, test := range bmpStringTests {
1114+
encoded, err := hex.DecodeString(test.encodedHex)
1115+
if err != nil {
1116+
t.Fatalf("#%d: failed to decode from hex string", i)
1117+
}
1118+
1119+
decoded, err := parseBMPString(encoded)
1120+
1121+
if err != nil {
1122+
t.Errorf("#%d: decoding output gave an error: %s", i, err)
1123+
continue
1124+
}
1125+
1126+
if decoded != test.decoded {
1127+
t.Errorf("#%d: decoding output resulted in %q, but it should have been %q", i, decoded, test.decoded)
1128+
continue
1129+
}
1130+
}
1131+
}

src/encoding/asn1/common.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const (
3737
TagUTCTime = 23
3838
TagGeneralizedTime = 24
3939
TagGeneralString = 27
40+
TagBMPString = 30
4041
)
4142

4243
// ASN.1 class types represent the namespace of the tag.

0 commit comments

Comments
 (0)