Commit eeffa738 authored by Adam Langley's avatar Adam Langley

encoding/asn1: promote untyped strings to UTF8 as needed.

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
parent 685a61df
...@@ -251,7 +251,7 @@ func TestCreateSelfSignedCertificate(t *testing.T) { ...@@ -251,7 +251,7 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
SerialNumber: big.NewInt(1), SerialNumber: big.NewInt(1),
Subject: pkix.Name{ Subject: pkix.Name{
CommonName: commonName, CommonName: commonName,
Organization: []string{"Acme Co"}, Organization: []string{"Σ Acme Co"},
}, },
NotBefore: time.Unix(1000, 0), NotBefore: time.Unix(1000, 0),
NotAfter: time.Unix(100000, 0), NotAfter: time.Unix(100000, 0),
......
...@@ -98,6 +98,8 @@ func parseFieldParameters(str string) (ret fieldParameters) { ...@@ -98,6 +98,8 @@ func parseFieldParameters(str string) (ret fieldParameters) {
ret.stringType = tagIA5String ret.stringType = tagIA5String
case part == "printable": case part == "printable":
ret.stringType = tagPrintableString ret.stringType = tagPrintableString
case part == "utf8":
ret.stringType = tagUTF8String
case strings.HasPrefix(part, "default:"): case strings.HasPrefix(part, "default:"):
i, err := strconv.ParseInt(part[8:], 10, 64) i, err := strconv.ParseInt(part[8:], 10, 64)
if err == nil { if err == nil {
......
...@@ -6,11 +6,13 @@ package asn1 ...@@ -6,11 +6,13 @@ package asn1
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"io" "io"
"math/big" "math/big"
"reflect" "reflect"
"time" "time"
"unicode/utf8"
) )
// A forkableWriter is an in-memory buffer that can be // A forkableWriter is an in-memory buffer that can be
...@@ -280,6 +282,11 @@ func marshalIA5String(out *forkableWriter, s string) (err error) { ...@@ -280,6 +282,11 @@ func marshalIA5String(out *forkableWriter, s string) (err error) {
return return
} }
func marshalUTF8String(out *forkableWriter, s string) (err error) {
_, err = out.Write([]byte(s))
return
}
func marshalTwoDigits(out *forkableWriter, v int) (err error) { func marshalTwoDigits(out *forkableWriter, v int) (err error) {
err = out.WriteByte(byte('0' + (v/10)%10)) err = out.WriteByte(byte('0' + (v/10)%10))
if err != nil { if err != nil {
...@@ -446,10 +453,13 @@ func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameter ...@@ -446,10 +453,13 @@ func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameter
} }
return return
case reflect.String: case reflect.String:
if params.stringType == tagIA5String { switch params.stringType {
case tagIA5String:
return marshalIA5String(out, v.String()) return marshalIA5String(out, v.String())
} else { case tagPrintableString:
return marshalPrintableString(out, v.String()) return marshalPrintableString(out, v.String())
default:
return marshalUTF8String(out, v.String())
} }
return return
} }
...@@ -492,11 +502,27 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters) ...@@ -492,11 +502,27 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
} }
class := classUniversal class := classUniversal
if params.stringType != 0 { if params.stringType != 0 && tag != tagPrintableString {
if tag != tagPrintableString { return StructuralError{"Explicit string type given to non-string member"}
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 { if params.set {
......
...@@ -122,6 +122,7 @@ var marshalTests = []marshalTest{ ...@@ -122,6 +122,7 @@ var marshalTests = []marshalTest{
{testSET([]int{10}), "310302010a"}, {testSET([]int{10}), "310302010a"},
{omitEmptyTest{[]string{}}, "3000"}, {omitEmptyTest{[]string{}}, "3000"},
{omitEmptyTest{[]string{"1"}}, "30053003130131"}, {omitEmptyTest{[]string{"1"}}, "30053003130131"},
{"Σ", "0c02cea3"},
} }
func TestMarshal(t *testing.T) { func TestMarshal(t *testing.T) {
...@@ -137,3 +138,10 @@ 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")
}
}
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