]> Cypherpunks repositories - gostls13.git/commitdiff
encoding/asn1: promote untyped strings to UTF8 as needed.
authorAdam Langley <agl@golang.org>
Tue, 10 Jul 2012 22:23:30 +0000 (18:23 -0400)
committerAdam Langley <agl@golang.org>
Tue, 10 Jul 2012 22:23:30 +0000 (18:23 -0400)
Previously, strings that didn't have an explicit ASN.1 string type
were taken to be ASN.1 PrintableStrings. This resulted in an error if
a unrepresentable charactor was included.

For compatibility reasons, I'm too afraid to switch the default string
type to UTF8String, but this patch causes untyped strings to become
UTF8Strings if they contain a charactor that's not valid in a
PrintableString.

Fixes #3791.

R=golang-dev, bradfitz, r, r
CC=golang-dev
https://golang.org/cl/6348074

src/pkg/crypto/x509/x509_test.go
src/pkg/encoding/asn1/common.go
src/pkg/encoding/asn1/marshal.go
src/pkg/encoding/asn1/marshal_test.go

index 46d04dc999f565c45a038a27e7d269f621d91250..813a96409b32b8f8dda5fda47caeafc64422180c 100644 (file)
@@ -251,7 +251,7 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
                SerialNumber: big.NewInt(1),
                Subject: pkix.Name{
                        CommonName:   commonName,
-                       Organization: []string{"Acme Co"},
+                       Organization: []string{"Σ Acme Co"},
                },
                NotBefore: time.Unix(1000, 0),
                NotAfter:  time.Unix(100000, 0),
index 03856bc55c5ab41e3e95a0686c5ff314d0e160e6..33a117ece195411304421f4ccedac5c6698325d4 100644 (file)
@@ -98,6 +98,8 @@ func parseFieldParameters(str string) (ret fieldParameters) {
                        ret.stringType = tagIA5String
                case part == "printable":
                        ret.stringType = tagPrintableString
+               case part == "utf8":
+                       ret.stringType = tagUTF8String
                case strings.HasPrefix(part, "default:"):
                        i, err := strconv.ParseInt(part[8:], 10, 64)
                        if err == nil {
index 163bca575de1c0e6dca78f3883cc9ff6119e7f72..3fd6be8ad92627391dd110a5538f56a2c6798db0 100644 (file)
@@ -6,11 +6,13 @@ package asn1
 
 import (
        "bytes"
+       "errors"
        "fmt"
        "io"
        "math/big"
        "reflect"
        "time"
+       "unicode/utf8"
 )
 
 // A forkableWriter is an in-memory buffer that can be
@@ -280,6 +282,11 @@ func marshalIA5String(out *forkableWriter, s string) (err error) {
        return
 }
 
+func marshalUTF8String(out *forkableWriter, s string) (err error) {
+       _, err = out.Write([]byte(s))
+       return
+}
+
 func marshalTwoDigits(out *forkableWriter, v int) (err error) {
        err = out.WriteByte(byte('0' + (v/10)%10))
        if err != nil {
@@ -446,10 +453,13 @@ func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameter
                }
                return
        case reflect.String:
-               if params.stringType == tagIA5String {
+               switch params.stringType {
+               case tagIA5String:
                        return marshalIA5String(out, v.String())
-               } else {
+               case tagPrintableString:
                        return marshalPrintableString(out, v.String())
+               default:
+                       return marshalUTF8String(out, v.String())
                }
                return
        }
@@ -492,11 +502,27 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
        }
        class := classUniversal
 
-       if params.stringType != 0 {
-               if tag != tagPrintableString {
-                       return StructuralError{"Explicit string type given to non-string member"}
+       if params.stringType != 0 && tag != tagPrintableString {
+               return StructuralError{"Explicit string type given to non-string member"}
+       }
+
+       if tag == tagPrintableString {
+               if params.stringType == 0 {
+                       // This is a string without an explicit string type. We'll use
+                       // a PrintableString if the character set in the string is
+                       // sufficiently limited, otherwise we'll use a UTF8String.
+                       for _, r := range v.String() {
+                               if r >= utf8.RuneSelf || !isPrintable(byte(r)) {
+                                       if !utf8.ValidString(v.String()) {
+                                               return errors.New("asn1: string not valid UTF-8")
+                                       }
+                                       tag = tagUTF8String
+                                       break
+                               }
+                       }
+               } else {
+                       tag = params.stringType
                }
-               tag = params.stringType
        }
 
        if params.set {
index f43bcae681a503bdf674ce971a2f9c590baa25ff..c203533a91761c9a9c9c86ebc889b093d71b9ae4 100644 (file)
@@ -122,6 +122,7 @@ var marshalTests = []marshalTest{
        {testSET([]int{10}), "310302010a"},
        {omitEmptyTest{[]string{}}, "3000"},
        {omitEmptyTest{[]string{"1"}}, "30053003130131"},
+       {"Σ", "0c02cea3"},
 }
 
 func TestMarshal(t *testing.T) {
@@ -137,3 +138,10 @@ func TestMarshal(t *testing.T) {
                }
        }
 }
+
+func TestInvalidUTF8(t *testing.T) {
+       _, err := Marshal(string([]byte{0xff, 0xff}))
+       if err == nil {
+               t.Errorf("invalid UTF8 string was accepted")
+       }
+}