From 1aa571d38b88142ec604ba20c2f9de611ff6413d Mon Sep 17 00:00:00 2001
From: Adam Langley <agl@golang.org>
Date: Wed, 6 Jul 2011 16:25:44 -0400
Subject: [PATCH] asn1: support T61 and UTF8 string.

There is no end to the kitchen sink of string types in ASN.1.

Fixes #1953.

R=golang-dev, gri
CC=golang-dev
https://golang.org/cl/4672045
---
 src/pkg/asn1/asn1.go   | 45 +++++++++++++++++++++++++-----------------
 src/pkg/asn1/common.go |  1 +
 2 files changed, 28 insertions(+), 18 deletions(-)

diff --git a/src/pkg/asn1/asn1.go b/src/pkg/asn1/asn1.go
index 95f299e63d..6557729317 100644
--- a/src/pkg/asn1/asn1.go
+++ b/src/pkg/asn1/asn1.go
@@ -149,7 +149,7 @@ func (b BitString) RightAlign() []byte {
 	return a
 }
 
-// parseBitString parses an ASN.1 bit string from the given byte array and returns it.
+// parseBitString parses an ASN.1 bit string from the given byte slice and returns it.
 func parseBitString(bytes []byte) (ret BitString, err os.Error) {
 	if len(bytes) == 0 {
 		err = SyntaxError{"zero length BIT STRING"}
@@ -227,7 +227,7 @@ type Enumerated int
 type Flag bool
 
 // parseBase128Int parses a base-128 encoded int from the given offset in the
-// given byte array. It returns the value and the new offset.
+// given byte slice. It returns the value and the new offset.
 func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err os.Error) {
 	offset = initOffset
 	for shifted := 0; offset < len(bytes); shifted++ {
@@ -259,7 +259,7 @@ func parseUTCTime(bytes []byte) (ret *time.Time, err os.Error) {
 	return
 }
 
-// parseGeneralizedTime parses the GeneralizedTime from the given byte array
+// parseGeneralizedTime parses the GeneralizedTime from the given byte slice
 // and returns the resulting time.
 func parseGeneralizedTime(bytes []byte) (ret *time.Time, err os.Error) {
 	return time.Parse("20060102150405Z0700", string(bytes))
@@ -300,7 +300,7 @@ func isPrintable(b byte) bool {
 // IA5String
 
 // parseIA5String parses a ASN.1 IA5String (ASCII string) from the given
-// byte array and returns it.
+// byte slice and returns it.
 func parseIA5String(bytes []byte) (ret string, err os.Error) {
 	for _, b := range bytes {
 		if b >= 0x80 {
@@ -315,11 +315,19 @@ func parseIA5String(bytes []byte) (ret string, err os.Error) {
 // T61String
 
 // parseT61String parses a ASN.1 T61String (8-bit clean string) from the given
-// byte array and returns it.
+// byte slice and returns it.
 func parseT61String(bytes []byte) (ret string, err os.Error) {
 	return string(bytes), nil
 }
 
+// UTF8String
+
+// parseUTF8String parses a ASN.1 UTF8String (raw UTF-8) from the given byte
+// array and returns it.
+func parseUTF8String(bytes []byte) (ret string, err os.Error) {
+	return string(bytes), nil
+}
+
 // A RawValue represents an undecoded ASN.1 object.
 type RawValue struct {
 	Class, Tag int
@@ -336,7 +344,7 @@ type RawContent []byte
 // Tagging
 
 // parseTagAndLength parses an ASN.1 tag and length pair from the given offset
-// into a byte array. It returns the parsed data and the new offset. SET and
+// 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 os.Error) {
@@ -393,7 +401,7 @@ func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset i
 }
 
 // parseSequenceOf is used for SEQUENCE OF and SET OF values. It tries to parse
-// a number of ASN.1 values from the given byte array and returns them as a
+// a number of ASN.1 values from the given byte slice and returns them as a
 // slice of Go values of the given type.
 func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type) (ret reflect.Value, err os.Error) {
 	expectedTag, compoundType, ok := getUniversalType(elemType)
@@ -456,7 +464,7 @@ func invalidLength(offset, length, sliceLength int) bool {
 	return offset+length < offset || offset+length > sliceLength
 }
 
-// parseField is the main parsing function. Given a byte array and an offset
+// parseField is the main parsing function. Given a byte slice and an offset
 // into the array, it will try to parse a suitable ASN.1 value out and store it
 // in the given Value.
 func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParameters) (offset int, err os.Error) {
@@ -573,16 +581,15 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
 		}
 	}
 
-	// Special case for strings: PrintableString and IA5String both map to
-	// the Go type string. getUniversalType returns the tag for
-	// PrintableString when it sees a string so, if we see an IA5String on
-	// the wire, we change the universal type to match.
-	if universalTag == tagPrintableString && t.tag == tagIA5String {
-		universalTag = tagIA5String
-	}
-	// Likewise for GeneralString
-	if universalTag == tagPrintableString && t.tag == tagGeneralString {
-		universalTag = tagGeneralString
+	// Special case for strings: all the ASN.1 string types map to the Go
+	// type string. getUniversalType returns the tag for PrintableString
+	// when it sees a string, so if we see a different string type on the
+	// wire, we change the universal type to match.
+	if universalTag == tagPrintableString {
+		switch t.tag {
+		case tagIA5String, tagGeneralString, tagT61String, tagUTF8String:
+			universalTag = t.tag
+		}
 	}
 
 	// Special case for time: UTCTime and GeneralizedTime both map to the
@@ -738,6 +745,8 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
 			v, err = parseIA5String(innerBytes)
 		case tagT61String:
 			v, err = parseT61String(innerBytes)
+		case tagUTF8String:
+			v, err = parseUTF8String(innerBytes)
 		case tagGeneralString:
 			// GeneralString is specified in ISO-2022/ECMA-35,
 			// A brief review suggests that it includes structures
diff --git a/src/pkg/asn1/common.go b/src/pkg/asn1/common.go
index 854f4da480..01f4f7b6ec 100644
--- a/src/pkg/asn1/common.go
+++ b/src/pkg/asn1/common.go
@@ -25,6 +25,7 @@ const (
 	tagOctetString     = 4
 	tagOID             = 6
 	tagEnum            = 10
+	tagUTF8String      = 12
 	tagSequence        = 16
 	tagSet             = 17
 	tagPrintableString = 19
-- 
2.30.9