fleet/server/mdm/microsoft/wstep_csr.go
Marcos Oviedo 96449dd47b
Adding support for RequestSecurityToken messages - Windows MDM enroll endpoint (#12555)
This relates to #12263 

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [X] Changes file added for user-visible changes in `changes/` or
`orbit/changes/`.
See [Changes
files](https://fleetdm.com/docs/contributing/committing-changes#changes-files)
for more information.
- [X] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)
- [X] Added/updated tests

---------

Co-authored-by: Roberto Dip <me@roperzh.com>
2023-07-05 10:06:37 -03:00

1590 lines
48 KiB
Go

package microsoft_mdm
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"errors"
"fmt"
"math"
"math/big"
"net"
"net/url"
"reflect"
"strconv"
"strings"
"time"
"unicode"
"unicode/utf16"
"unicode/utf8"
// Explicitly import these for their crypto.RegisterHash init side-effects.
// Keep these as blank imports, even if they're imported above.
"crypto/dsa" //lint:ignore required for crypto.RegisterHash
_ "crypto/sha1" //nolint:gosec
_ "crypto/sha256"
_ "crypto/sha512"
"golang.org/x/crypto/cryptobyte"
cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
)
// ParseCertificateRequestFromWindowsDevice parses a single certificate request from a given ASN.1 blob.
// The certificate request is expected to be PEM encoded in ASN.1 format.
// This function is a modified version of the ParseCertificateRequest function from the standard x509 package.
// The only modification made was to support invalid characters in the certificate CommonName field.
// The certificates generated by a Windows device during MDM enrollment were not being parsed correctly
// by the standard x509 package. To work around this issue, we used a modified version of the isPrintable
// function from the ASN.1 package that allows invalid characters.
// You can find the implementation of isPrintable here:
// https://github.com/golang/go/blob/e126572f8a91d42b86242012012d0cad4507dca8/src/encoding/asn1/asn1.go#L433
// The modification to the standard library requires applying the following patch to the x509 package:
// https://github.com/fleetdm/fleet/blob/main/tools/mdm/windows/poc-mdm-server/patch/patch.go
// Since this solution is not ideal and does not scale, we have wrapped a custom version of
// ParseCertificateRequest with the necessary changes in ParseCertificateRequestFromWindowsDevice().
// The invalid characters are: '!' and 0x00.
// Here's an example of a certificate with invalid characters in the CommonName field:
// Common Name: F717C0F0-5F68-4AC3-A341-01B254!4219DFB0A902F747A9C4FD43C8CE36CE
// The new characters have been added to the isPrintable() function below.
func ParseCertificateRequestFromWindowsDevice(asn1Data []byte) (*x509.CertificateRequest, error) {
var csr certificateRequest
rest, err := asn1.Unmarshal(asn1Data, &csr)
if err != nil {
return nil, err
}
if len(rest) != 0 {
return nil, asn1.SyntaxError{Msg: "trailing data"}
}
return parseCertificateRequest(&csr)
}
// isPrintable reports whether the given b is in the ASN.1 PrintableString set.
// If asterisk is allowAsterisk then '*' is also allowed, reflecting existing
// practice. If ampersand is allowAmpersand then '&' is allowed as well.
func isPrintable(b byte, asterisk asteriskFlag, ampersand ampersandFlag) bool {
return 'a' <= b && b <= 'z' ||
'A' <= b && b <= 'Z' ||
'0' <= b && b <= '9' ||
'\'' <= b && b <= ')' ||
'+' <= b && b <= '/' ||
b == ' ' ||
b == ':' ||
b == '=' ||
b == '?' ||
b == '!' || // Windows MDM Certificate Parsing Patch
b == 0 || // Windows MDM Certificate Parsing Patch
// This is technically not allowed in a PrintableString.
// However, x509 certificates with wildcard strings don't
// always use the correct string type so we permit it.
(bool(asterisk) && b == '*') ||
// This is not technically allowed either. However, not
// only is it relatively common, but there are also a
// handful of CA certificates that contain it. At least
// one of which will not expire until 2027.
(bool(ampersand) && b == '&')
}
// oidExtensionRequest is a PKCS #9 OBJECT IDENTIFIER that indicates requested
// extensions in a CSR.
var oidExtensionRequest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 14}
type (
asteriskFlag bool
ampersandFlag bool
)
const (
allowAsterisk asteriskFlag = true
rejectAsterisk asteriskFlag = false
allowAmpersand ampersandFlag = true
rejectAmpersand ampersandFlag = false
)
const (
nameTypeEmail = 1
nameTypeDNS = 2
nameTypeURI = 6
nameTypeIP = 7
)
var (
oidNamedCurveP224 = asn1.ObjectIdentifier{1, 3, 132, 0, 33}
oidNamedCurveP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7}
oidNamedCurveP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34}
oidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35}
)
var oidExtensionSubjectAltName = []int{2, 5, 29, 17}
var (
oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
oidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
oidPublicKeyEd25519 = oidSignatureEd25519
)
var (
oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
oidSignatureRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10}
oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2}
oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
oidSignatureEd25519 = asn1.ObjectIdentifier{1, 3, 101, 112}
oidSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1}
oidSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2}
oidSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3}
oidMGF1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8}
// oidISOSignatureSHA1WithRSA means the same as oidSignatureSHA1WithRSA
// but it's specified by ISO. Microsoft's makecert.exe has been known
// to produce certificates with this OID.
oidISOSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 29}
)
var signatureAlgorithmDetails = []struct {
algo x509.SignatureAlgorithm
name string
oid asn1.ObjectIdentifier
pubKeyAlgo x509.PublicKeyAlgorithm
hash crypto.Hash
}{
{x509.MD2WithRSA, "MD2-RSA", oidSignatureMD2WithRSA, x509.RSA, crypto.Hash(0) /* no value for MD2 */},
{x509.MD5WithRSA, "MD5-RSA", oidSignatureMD5WithRSA, x509.RSA, crypto.MD5},
{x509.SHA1WithRSA, "SHA1-RSA", oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
{x509.SHA1WithRSA, "SHA1-RSA", oidISOSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
{x509.SHA256WithRSA, "SHA256-RSA", oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256},
{x509.SHA384WithRSA, "SHA384-RSA", oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384},
{x509.SHA512WithRSA, "SHA512-RSA", oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512},
{x509.SHA256WithRSAPSS, "SHA256-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA256},
{x509.SHA384WithRSAPSS, "SHA384-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA384},
{x509.SHA512WithRSAPSS, "SHA512-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA512},
{x509.DSAWithSHA1, "DSA-SHA1", oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1},
{x509.DSAWithSHA256, "DSA-SHA256", oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256},
{x509.ECDSAWithSHA1, "ECDSA-SHA1", oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1},
{x509.ECDSAWithSHA256, "ECDSA-SHA256", oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256},
{x509.ECDSAWithSHA384, "ECDSA-SHA384", oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384},
{x509.ECDSAWithSHA512, "ECDSA-SHA512", oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512},
{x509.PureEd25519, "Ed25519", oidSignatureEd25519, x509.Ed25519, crypto.Hash(0) /* no pre-hashing */},
}
var (
bitStringType = reflect.TypeOf(asn1.BitString{})
objectIdentifierType = reflect.TypeOf(asn1.ObjectIdentifier{})
enumeratedType = reflect.TypeOf(asn1.Enumerated(0))
flagType = reflect.TypeOf(asn1.Flag(false))
timeType = reflect.TypeOf(time.Time{})
rawValueType = reflect.TypeOf(asn1.RawValue{})
rawContentsType = reflect.TypeOf(asn1.RawContent(nil))
bigIntType = reflect.TypeOf(new(big.Int))
)
// ASN.1 class types represent the namespace of the tag.
const (
classUniversal = 0
classApplication = 1
classContextSpecific = 2
classPrivate = 3
)
type tagAndLength struct {
class, tag, length int
isCompound bool
}
// pkcs1PublicKey reflects the ASN.1 structure of a PKCS #1 public key.
type pkcs1PublicKey struct {
n *big.Int
e int
}
type publicKeyInfo struct {
Raw asn1.RawContent
Algorithm pkix.AlgorithmIdentifier
PublicKey asn1.BitString
}
type tbsCertificateRequest struct {
Raw asn1.RawContent
Version int
Subject asn1.RawValue
PublicKey publicKeyInfo
RawAttributes []asn1.RawValue `asn1:"tag:0"`
}
type certificateRequest struct {
Raw asn1.RawContent
TBSCSR tbsCertificateRequest
SignatureAlgorithm pkix.AlgorithmIdentifier
SignatureValue asn1.BitString
}
// pssParameters reflects the parameters in an AlgorithmIdentifier that
// specifies RSA PSS. See RFC 3447, Appendix A.2.3.
type pssParameters struct {
// The following three fields are not marked as
// optional because the default values specify SHA-1,
// which is no longer suitable for use in signatures.
Hash pkix.AlgorithmIdentifier `asn1:"explicit,tag:0"`
MGF pkix.AlgorithmIdentifier `asn1:"explicit,tag:1"`
SaltLength int `asn1:"explicit,tag:2"`
TrailerField int `asn1:"optional,explicit,tag:3,default:1"`
}
// fieldParameters is the parsed representation of tag string from a structure field.
type fieldParameters struct {
optional bool // true iff the field is OPTIONAL
explicit bool // true iff an EXPLICIT tag is in use.
application bool // true iff an APPLICATION tag is in use.
private bool // true iff a PRIVATE tag is in use.
defaultValue *int64 // a default value for INTEGER typed fields (maybe nil).
tag *int // the EXPLICIT or IMPLICIT tag (maybe nil).
stringType int // the string tag to use when marshaling.
timeType int // the time tag to use when marshaling.
set bool // true iff this should be encoded as a SET
omitEmpty bool // true iff this should be omitted if empty when marshaling.
// Invariants:
// if explicit is set, tag is non-nil.
}
// An invalidUnmarshalError describes an invalid argument passed to Unmarshal.
// (The argument to Unmarshal must be a non-nil pointer.)
type invalidUnmarshalError struct {
Type reflect.Type
}
func (e *invalidUnmarshalError) Error() string {
if e.Type == nil {
return "asn1: Unmarshal recipient value is nil"
}
if e.Type.Kind() != reflect.Pointer {
return "asn1: Unmarshal recipient value is non-pointer " + e.Type.String()
}
return "asn1: Unmarshal recipient value is nil " + e.Type.String()
}
func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) x509.PublicKeyAlgorithm {
switch {
case oid.Equal(oidPublicKeyRSA):
return x509.RSA
case oid.Equal(oidPublicKeyDSA):
return x509.DSA
case oid.Equal(oidPublicKeyECDSA):
return x509.ECDSA
case oid.Equal(oidPublicKeyEd25519):
return x509.Ed25519
}
return x509.UnknownPublicKeyAlgorithm
}
func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) x509.SignatureAlgorithm {
if ai.Algorithm.Equal(oidSignatureEd25519) {
// RFC 8410, Section 3
// > For all of the OIDs, the parameters MUST be absent.
if len(ai.Parameters.FullBytes) != 0 {
return x509.UnknownSignatureAlgorithm
}
}
if !ai.Algorithm.Equal(oidSignatureRSAPSS) {
for _, details := range signatureAlgorithmDetails {
if ai.Algorithm.Equal(details.oid) {
return details.algo
}
}
return x509.UnknownSignatureAlgorithm
}
// RSA PSS is special because it encodes important parameters
// in the Parameters.
var params pssParameters
if _, err := nnmarshalASN1(ai.Parameters.FullBytes, &params); err != nil {
return x509.UnknownSignatureAlgorithm
}
var mgf1HashFunc pkix.AlgorithmIdentifier
if _, err := nnmarshalASN1(params.MGF.Parameters.FullBytes, &mgf1HashFunc); err != nil {
return x509.UnknownSignatureAlgorithm
}
// PSS is greatly overburdened with options. This code forces them into
// three buckets by requiring that the MGF1 hash function always match the
// message hash function (as recommended in RFC 3447, Section 8.1), that the
// salt length matches the hash length, and that the trailer field has the
// default value.
if (len(params.Hash.Parameters.FullBytes) != 0 && !bytes.Equal(params.Hash.Parameters.FullBytes, asn1.NullBytes)) ||
!params.MGF.Algorithm.Equal(oidMGF1) ||
!mgf1HashFunc.Algorithm.Equal(params.Hash.Algorithm) ||
(len(mgf1HashFunc.Parameters.FullBytes) != 0 && !bytes.Equal(mgf1HashFunc.Parameters.FullBytes, asn1.NullBytes)) ||
params.TrailerField != 1 {
return x509.UnknownSignatureAlgorithm
}
switch {
case params.Hash.Algorithm.Equal(oidSHA256) && params.SaltLength == 32:
return x509.SHA256WithRSAPSS
case params.Hash.Algorithm.Equal(oidSHA384) && params.SaltLength == 48:
return x509.SHA384WithRSAPSS
case params.Hash.Algorithm.Equal(oidSHA512) && params.SaltLength == 64:
return x509.SHA512WithRSAPSS
}
return x509.UnknownSignatureAlgorithm
}
func parseRawAttributes(rawAttributes []asn1.RawValue) []pkix.AttributeTypeAndValueSET {
var attributes []pkix.AttributeTypeAndValueSET
for _, rawAttr := range rawAttributes {
var attr pkix.AttributeTypeAndValueSET
rest, err := nnmarshalASN1(rawAttr.FullBytes, &attr)
// Ignore attributes that don't parse into pkix.AttributeTypeAndValueSET
// (i.e.: challengePassword or unstructuredName).
if err == nil && len(rest) == 0 {
attributes = append(attributes, attr)
}
}
return attributes
}
func parseCertificateRequest(in *certificateRequest) (*x509.CertificateRequest, error) {
out := &x509.CertificateRequest{
Raw: in.Raw,
RawTBSCertificateRequest: in.TBSCSR.Raw,
RawSubjectPublicKeyInfo: in.TBSCSR.PublicKey.Raw,
RawSubject: in.TBSCSR.Subject.FullBytes,
Signature: in.SignatureValue.RightAlign(),
SignatureAlgorithm: getSignatureAlgorithmFromAI(in.SignatureAlgorithm),
PublicKeyAlgorithm: getPublicKeyAlgorithmFromOID(in.TBSCSR.PublicKey.Algorithm.Algorithm),
Version: in.TBSCSR.Version,
Attributes: parseRawAttributes(in.TBSCSR.RawAttributes),
}
var err error
out.PublicKey, err = parsePublicKey(out.PublicKeyAlgorithm, &in.TBSCSR.PublicKey)
if err != nil {
return nil, err
}
var subject pkix.RDNSequence
if rest, err := nnmarshalASN1(in.TBSCSR.Subject.FullBytes, &subject); err != nil {
return nil, err
} else if len(rest) != 0 {
return nil, errors.New("x509: trailing data after X.509 Subject")
}
out.Subject.FillFromRDNSequence(&subject)
if out.Extensions, err = parseCSRExtensions(in.TBSCSR.RawAttributes); err != nil {
return nil, err
}
for _, extension := range out.Extensions {
switch {
case extension.Id.Equal(oidExtensionSubjectAltName):
out.DNSNames, out.EmailAddresses, out.IPAddresses, out.URIs, err = parseSANExtension(extension.Value)
if err != nil {
return nil, err
}
}
}
return out, nil
}
func parsePublicKey(algo x509.PublicKeyAlgorithm, keyData *publicKeyInfo) (any, error) {
der := cryptobyte.String(keyData.PublicKey.RightAlign())
switch algo {
case x509.RSA:
// RSA public keys must have a NULL in the parameters.
// See RFC 3279, Section 2.3.1.
if !bytes.Equal(keyData.Algorithm.Parameters.FullBytes, asn1.NullBytes) {
return nil, errors.New("x509: RSA key missing NULL parameters")
}
p := &pkcs1PublicKey{n: new(big.Int)}
if !der.ReadASN1(&der, cryptobyte_asn1.SEQUENCE) {
return nil, errors.New("x509: invalid RSA public key")
}
if !der.ReadASN1Integer(p.n) {
return nil, errors.New("x509: invalid RSA modulus")
}
if !der.ReadASN1Integer(&p.e) {
return nil, errors.New("x509: invalid RSA public exponent")
}
if p.n.Sign() <= 0 {
return nil, errors.New("x509: RSA modulus is not a positive number")
}
if p.e <= 0 {
return nil, errors.New("x509: RSA public exponent is not a positive number")
}
pub := &rsa.PublicKey{
E: p.e,
N: p.n,
}
return pub, nil
case x509.ECDSA:
paramsDer := cryptobyte.String(keyData.Algorithm.Parameters.FullBytes)
namedCurveOID := new(asn1.ObjectIdentifier)
if !paramsDer.ReadASN1ObjectIdentifier(namedCurveOID) {
return nil, errors.New("x509: invalid ECDSA parameters")
}
namedCurve := namedCurveFromOID(*namedCurveOID)
if namedCurve == nil {
return nil, errors.New("x509: unsupported elliptic curve")
}
x, y := elliptic.Unmarshal(namedCurve, der)
if x == nil {
return nil, errors.New("x509: failed to unmarshal elliptic curve point")
}
pub := &ecdsa.PublicKey{
Curve: namedCurve,
X: x,
Y: y,
}
return pub, nil
case x509.Ed25519:
// RFC 8410, Section 3
// > For all of the OIDs, the parameters MUST be absent.
if len(keyData.Algorithm.Parameters.FullBytes) != 0 {
return nil, errors.New("x509: Ed25519 key encoded with illegal parameters")
}
if len(der) != ed25519.PublicKeySize {
return nil, errors.New("x509: wrong Ed25519 public key size")
}
return ed25519.PublicKey(der), nil
case x509.DSA:
y := new(big.Int)
if !der.ReadASN1Integer(y) {
return nil, errors.New("x509: invalid DSA public key")
}
pub := &dsa.PublicKey{
Y: y,
Parameters: dsa.Parameters{
P: new(big.Int),
Q: new(big.Int),
G: new(big.Int),
},
}
paramsDer := cryptobyte.String(keyData.Algorithm.Parameters.FullBytes)
if !paramsDer.ReadASN1(&paramsDer, cryptobyte_asn1.SEQUENCE) ||
!paramsDer.ReadASN1Integer(pub.Parameters.P) ||
!paramsDer.ReadASN1Integer(pub.Parameters.Q) ||
!paramsDer.ReadASN1Integer(pub.Parameters.G) {
return nil, errors.New("x509: invalid DSA parameters")
}
if pub.Y.Sign() <= 0 || pub.Parameters.P.Sign() <= 0 ||
pub.Parameters.Q.Sign() <= 0 || pub.Parameters.G.Sign() <= 0 {
return nil, errors.New("x509: zero or negative DSA parameter")
}
return pub, nil
default:
return nil, nil
}
}
func namedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve {
switch {
case oid.Equal(oidNamedCurveP224):
return elliptic.P224()
case oid.Equal(oidNamedCurveP256):
return elliptic.P256()
case oid.Equal(oidNamedCurveP384):
return elliptic.P384()
case oid.Equal(oidNamedCurveP521):
return elliptic.P521()
}
return nil
}
// parseCSRExtensions parses the attributes from a CSR and extracts any
// requested extensions.
func parseCSRExtensions(rawAttributes []asn1.RawValue) ([]pkix.Extension, error) {
// pkcs10Attribute reflects the Attribute structure from RFC 2986, Section 4.1.
type pkcs10Attribute struct {
Id asn1.ObjectIdentifier
Values []asn1.RawValue `asn1:"set"`
}
var ret []pkix.Extension
seenExts := make(map[string]bool)
for _, rawAttr := range rawAttributes {
var attr pkcs10Attribute
if rest, err := nnmarshalASN1(rawAttr.FullBytes, &attr); err != nil || len(rest) != 0 || len(attr.Values) == 0 {
// Ignore attributes that don't parse.
continue
}
oidStr := attr.Id.String()
if seenExts[oidStr] {
return nil, errors.New("x509: certificate request contains duplicate extensions")
}
seenExts[oidStr] = true
if !attr.Id.Equal(oidExtensionRequest) {
continue
}
var extensions []pkix.Extension
if _, err := nnmarshalASN1(attr.Values[0].FullBytes, &extensions); err != nil {
return nil, err
}
requestedExts := make(map[string]bool)
for _, ext := range extensions {
oidStr := ext.Id.String()
if requestedExts[oidStr] {
return nil, errors.New("x509: certificate request contains duplicate requested extensions")
}
requestedExts[oidStr] = true
}
ret = append(ret, extensions...)
}
return ret, nil
}
func forEachSAN(der cryptobyte.String, callback func(tag int, data []byte) error) error {
if !der.ReadASN1(&der, cryptobyte_asn1.SEQUENCE) {
return errors.New("x509: invalid subject alternative names")
}
for !der.Empty() {
var san cryptobyte.String
var tag cryptobyte_asn1.Tag
if !der.ReadAnyASN1(&san, &tag) {
return errors.New("x509: invalid subject alternative name")
}
if err := callback(int(tag^0x80), san); err != nil {
return err
}
}
return nil
}
func parseSANExtension(der cryptobyte.String) (dnsNames, emailAddresses []string, ipAddresses []net.IP, uris []*url.URL, err error) {
err = forEachSAN(der, func(tag int, data []byte) error {
switch tag {
case nameTypeEmail:
email := string(data)
if err := isIA5String(email); err != nil {
return errors.New("x509: SAN rfc822Name is malformed")
}
emailAddresses = append(emailAddresses, email)
case nameTypeDNS:
name := string(data)
if err := isIA5String(name); err != nil {
return errors.New("x509: SAN dNSName is malformed")
}
dnsNames = append(dnsNames, name)
case nameTypeURI:
uriStr := string(data)
if err := isIA5String(uriStr); err != nil {
return errors.New("x509: SAN uniformResourceIdentifier is malformed")
}
uri, err := url.Parse(uriStr)
if err != nil {
return fmt.Errorf("x509: cannot parse URI %q: %s", uriStr, err)
}
if len(uri.Host) > 0 {
if _, ok := domainToReverseLabels(uri.Host); !ok {
return fmt.Errorf("x509: cannot parse URI %q: invalid domain", uriStr)
}
}
uris = append(uris, uri)
case nameTypeIP:
switch len(data) {
case net.IPv4len, net.IPv6len:
ipAddresses = append(ipAddresses, data)
default:
return errors.New("x509: cannot parse IP address of length " + strconv.Itoa(len(data)))
}
}
return nil
})
return
}
func isIA5String(s string) error {
for _, r := range s {
// Per RFC5280 "IA5String is limited to the set of ASCII characters"
if r > unicode.MaxASCII {
return fmt.Errorf("x509: %q cannot be encoded as an IA5String", s)
}
}
return nil
}
// domainToReverseLabels converts a textual domain name like foo.example.com to
// the list of labels in reverse order, e.g. ["com", "example", "foo"].
func domainToReverseLabels(domain string) (reverseLabels []string, ok bool) {
for len(domain) > 0 {
if i := strings.LastIndexByte(domain, '.'); i == -1 {
reverseLabels = append(reverseLabels, domain)
domain = ""
} else {
reverseLabels = append(reverseLabels, domain[i+1:])
domain = domain[:i]
}
}
if len(reverseLabels) > 0 && len(reverseLabels[0]) == 0 {
// An empty label at the end indicates an absolute value.
return nil, false
}
for _, label := range reverseLabels {
if len(label) == 0 {
// Empty labels are otherwise invalid.
return nil, false
}
for _, c := range label {
if c < 33 || c > 126 {
// Invalid character.
return nil, false
}
}
}
return reverseLabels, true
}
func nnmarshalASN1(b []byte, val any) (rest []byte, err error) {
return unmarshalWithParams(b, val, "")
}
func unmarshalWithParams(b []byte, val any, params string) (rest []byte, err error) {
v := reflect.ValueOf(val)
if v.Kind() != reflect.Pointer || v.IsNil() {
return nil, &invalidUnmarshalError{reflect.TypeOf(val)}
}
offset, err := customParseField(v.Elem(), b, 0, customParseFieldParameters(params))
if err != nil {
return nil, err
}
return b[offset:], nil
}
// Given a tag string with the format specified in the package comment,
// parseFieldParameters will parse it into a fieldParameters structure,
// ignoring unknown parts of the string.
func customParseFieldParameters(str string) (ret fieldParameters) {
var part string
for len(str) > 0 {
part, str, _ = strings.Cut(str, ",")
switch {
case part == "optional":
ret.optional = true
case part == "explicit":
ret.explicit = true
if ret.tag == nil {
ret.tag = new(int)
}
case part == "generalized":
ret.timeType = asn1.TagGeneralizedTime
case part == "utc":
ret.timeType = asn1.TagUTCTime
case part == "ia5":
ret.stringType = asn1.TagIA5String
case part == "printable":
ret.stringType = asn1.TagPrintableString
case part == "numeric":
ret.stringType = asn1.TagNumericString
case part == "utf8":
ret.stringType = asn1.TagUTF8String
case strings.HasPrefix(part, "default:"):
i, err := strconv.ParseInt(part[8:], 10, 64)
if err == nil {
ret.defaultValue = new(int64)
*ret.defaultValue = i
}
case strings.HasPrefix(part, "tag:"):
i, err := strconv.Atoi(part[4:])
if err == nil {
ret.tag = new(int)
*ret.tag = i
}
case part == "set":
ret.set = true
case part == "application":
ret.application = true
if ret.tag == nil {
ret.tag = new(int)
}
case part == "private":
ret.private = true
if ret.tag == nil {
ret.tag = new(int)
}
case part == "omitempty":
ret.omitEmpty = true
}
}
return
}
// canHaveDefaultValue reports whether k is a Kind that we will set a default
// value for. (A signed integer, essentially.)
func canHaveDefaultValue(k reflect.Kind) bool {
switch k {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return true
}
return false
}
// setDefaultValue is used to install a default value, from a tag string, into
// a Value. It is successful if the field was optional, even if a default value
// wasn't provided or it failed to install it into the Value.
func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
if !params.optional {
return
}
ok = true
if params.defaultValue == nil {
return
}
if canHaveDefaultValue(v.Kind()) {
v.SetInt(*params.defaultValue)
}
return
}
// 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 customParseField(v reflect.Value, bytes []byte, initOffset int, params fieldParameters) (offset int, err error) {
offset = initOffset
fieldType := v.Type()
// If we have run out of data, it may be that there are optional elements at the end.
if offset == len(bytes) {
if !setDefaultValue(v, params) {
err = asn1.SyntaxError{Msg: "sequence truncated"}
}
return
}
// Deal with the ANY type.
if ifaceType := fieldType; ifaceType.Kind() == reflect.Interface && ifaceType.NumMethod() == 0 {
var t tagAndLength
t, offset, err = parseTagAndLength(bytes, offset)
if err != nil {
return
}
if invalidLength(offset, t.length, len(bytes)) {
err = asn1.SyntaxError{Msg: "data truncated"}
return
}
var result any
if !t.isCompound && t.class == asn1.ClassUniversal {
innerBytes := bytes[offset : offset+t.length]
switch t.tag {
case asn1.TagPrintableString:
result, err = parsePrintableString(innerBytes)
case asn1.TagNumericString:
result, err = parseNumericString(innerBytes)
case asn1.TagIA5String:
result, err = parseIA5String(innerBytes)
case asn1.TagT61String:
result, err = parseT61String(innerBytes)
case asn1.TagUTF8String:
result, err = parseUTF8String(innerBytes)
case asn1.TagInteger:
result, err = parseInt64(innerBytes)
case asn1.TagBitString:
result, err = parseBitString(innerBytes)
case asn1.TagOID:
result, err = parseObjectIdentifier(innerBytes)
case asn1.TagUTCTime:
result, err = parseUTCTime(innerBytes)
case asn1.TagGeneralizedTime:
result, err = parseGeneralizedTime(innerBytes)
case asn1.TagOctetString:
result = innerBytes
case asn1.TagBMPString:
result, err = parseBMPString(innerBytes)
default:
// If we don't know how to handle the type, we just leave Value as nil.
}
}
offset += t.length
if err != nil {
return
}
if result != nil {
v.Set(reflect.ValueOf(result))
}
return
}
t, offset, err := parseTagAndLength(bytes, offset)
if err != nil {
return
}
if params.explicit {
expectedClass := asn1.ClassContextSpecific
if params.application {
expectedClass = asn1.ClassApplication
}
if offset == len(bytes) {
err = asn1.StructuralError{Msg: "explicit tag has no child"}
return
}
if t.class == expectedClass && t.tag == *params.tag && (t.length == 0 || t.isCompound) {
if t.length > 0 {
t, offset, err = parseTagAndLength(bytes, offset)
if err != nil {
return
}
} else {
if fieldType != flagType {
err = asn1.StructuralError{Msg: "zero length explicit tag was not an asn1.Flag"}
return
}
v.SetBool(true)
return
}
} else {
// The tags didn't match, it might be an optional element.
ok := setDefaultValue(v, params)
if ok {
offset = initOffset
} else {
err = asn1.StructuralError{Msg: "explicitly tagged member didn't match"}
}
return
}
}
matchAny, universalTag, compoundType, ok1 := getUniversalType(fieldType)
if !ok1 {
err = asn1.StructuralError{Msg: fmt.Sprintf("unknown Go type: %v", fieldType)}
return
}
// 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 == asn1.TagPrintableString {
if t.class == asn1.ClassUniversal {
switch t.tag {
case asn1.TagIA5String, asn1.TagGeneralString, asn1.TagT61String, asn1.TagUTF8String, asn1.TagNumericString, asn1.TagBMPString:
universalTag = t.tag
}
} else if params.stringType != 0 {
universalTag = params.stringType
}
}
// Special case for time: UTCTime and GeneralizedTime both map to the
// Go type time.Time.
if universalTag == asn1.TagUTCTime && t.tag == asn1.TagGeneralizedTime && t.class == asn1.ClassUniversal {
universalTag = asn1.TagGeneralizedTime
}
if params.set {
universalTag = asn1.TagSet
}
matchAnyClassAndTag := matchAny
expectedClass := asn1.ClassUniversal
expectedTag := universalTag
if !params.explicit && params.tag != nil {
expectedClass = asn1.ClassContextSpecific
expectedTag = *params.tag
matchAnyClassAndTag = false
}
if !params.explicit && params.application && params.tag != nil {
expectedClass = asn1.ClassApplication
expectedTag = *params.tag
matchAnyClassAndTag = false
}
if !params.explicit && params.private && params.tag != nil {
expectedClass = asn1.ClassPrivate
expectedTag = *params.tag
matchAnyClassAndTag = false
}
// We have unwrapped any explicit tagging at this point.
if !matchAnyClassAndTag && (t.class != expectedClass || t.tag != expectedTag) ||
(!matchAny && t.isCompound != compoundType) {
// Tags don't match. Again, it could be an optional element.
ok := setDefaultValue(v, params)
if ok {
offset = initOffset
} else {
err = asn1.StructuralError{Msg: fmt.Sprintf("tags don't match (%d vs %+v) %+v %s @%d", expectedTag, t, params, fieldType.Name(), offset)}
}
return
}
if invalidLength(offset, t.length, len(bytes)) {
err = asn1.SyntaxError{Msg: "data truncated"}
return
}
innerBytes := bytes[offset : offset+t.length]
offset += t.length
// We deal with the structures defined in this package first.
switch v := v.Addr().Interface().(type) {
case *asn1.RawValue:
*v = asn1.RawValue{Class: t.class, Tag: t.tag, IsCompound: t.isCompound, Bytes: innerBytes, FullBytes: bytes[initOffset:offset]}
return
case *asn1.ObjectIdentifier:
*v, err = parseObjectIdentifier(innerBytes)
return
case *asn1.BitString:
*v, err = parseBitString(innerBytes)
return
case *time.Time:
if universalTag == asn1.TagUTCTime {
*v, err = parseUTCTime(innerBytes)
return
}
*v, err = parseGeneralizedTime(innerBytes)
return
case *asn1.Enumerated:
parsedInt, err1 := parseInt32(innerBytes)
if err1 == nil {
*v = asn1.Enumerated(parsedInt)
}
err = err1
return
case *asn1.Flag:
*v = true
return
case **big.Int:
parsedInt, err1 := parseBigInt(innerBytes)
if err1 == nil {
*v = parsedInt
}
err = err1
return
}
switch val := v; val.Kind() {
case reflect.Bool:
parsedBool, err1 := parseBool(innerBytes)
if err1 == nil {
val.SetBool(parsedBool)
}
err = err1
return
case reflect.Int, reflect.Int32, reflect.Int64:
if val.Type().Size() == 4 {
parsedInt, err1 := parseInt32(innerBytes)
if err1 == nil {
val.SetInt(int64(parsedInt))
}
err = err1
} else {
parsedInt, err1 := parseInt64(innerBytes)
if err1 == nil {
val.SetInt(parsedInt)
}
err = err1
}
return
// TODO(dfc) Add support for the remaining integer types
case reflect.Struct:
structType := fieldType
for i := 0; i < structType.NumField(); i++ {
if !structType.Field(i).IsExported() {
err = asn1.StructuralError{Msg: "struct contains unexported fields"}
return
}
}
if structType.NumField() > 0 &&
structType.Field(0).Type == rawContentsType {
bytes := bytes[initOffset:offset]
val.Field(0).Set(reflect.ValueOf(asn1.RawContent(bytes)))
}
innerOffset := 0
for i := 0; i < structType.NumField(); i++ {
field := structType.Field(i)
if i == 0 && field.Type == rawContentsType {
continue
}
innerOffset, err = customParseField(val.Field(i), innerBytes, innerOffset, customParseFieldParameters(field.Tag.Get("asn1")))
if err != nil {
return
}
}
// We allow extra bytes at the end of the SEQUENCE because
// adding elements to the end has been used in X.509 as the
// version numbers have increased.
return
case reflect.Slice:
sliceType := fieldType
if sliceType.Elem().Kind() == reflect.Uint8 {
val.Set(reflect.MakeSlice(sliceType, len(innerBytes), len(innerBytes)))
reflect.Copy(val, reflect.ValueOf(innerBytes))
return
}
newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem())
if err1 == nil {
val.Set(newSlice)
}
err = err1
return
case reflect.String:
var v string
switch universalTag {
case asn1.TagPrintableString:
v, err = parsePrintableString(innerBytes)
case asn1.TagNumericString:
v, err = parseNumericString(innerBytes)
case asn1.TagIA5String:
v, err = parseIA5String(innerBytes)
case asn1.TagT61String:
v, err = parseT61String(innerBytes)
case asn1.TagUTF8String:
v, err = parseUTF8String(innerBytes)
case asn1.TagGeneralString:
// GeneralString is specified in ISO-2022/ECMA-35,
// A brief review suggests that it includes structures
// that allow the encoding to change midstring and
// such. We give up and pass it as an 8-bit string.
v, err = parseT61String(innerBytes)
case asn1.TagBMPString:
v, err = parseBMPString(innerBytes)
default:
err = asn1.SyntaxError{Msg: fmt.Sprintf("internal error: unknown string type %d", universalTag)}
}
if err == nil {
val.SetString(v)
}
return
}
err = asn1.StructuralError{Msg: "unsupported: " + v.Type().String()}
return
}
// parseTagAndLength parses an ASN.1 tag and length pair from the given offset
// 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 error) {
offset = initOffset
// parseTagAndLength should not be called without at least a single
// byte to read. Thus this check is for robustness:
if offset >= len(bytes) {
err = errors.New("asn1: internal error in parseTagAndLength")
return
}
b := bytes[offset]
offset++
ret.class = int(b >> 6)
ret.isCompound = b&0x20 == 0x20
ret.tag = int(b & 0x1f)
// If the bottom five bits are set, then the tag number is actually base 128
// encoded afterwards
if ret.tag == 0x1f {
ret.tag, offset, err = parseBase128Int(bytes, offset)
if err != nil {
return
}
// Tags should be encoded in minimal form.
if ret.tag < 0x1f {
err = asn1.SyntaxError{Msg: "non-minimal tag"}
return
}
}
if offset >= len(bytes) {
err = asn1.SyntaxError{Msg: "truncated tag or length"}
return
}
b = bytes[offset]
offset++
if b&0x80 == 0 {
// The length is encoded in the bottom 7 bits.
ret.length = int(b & 0x7f)
} else {
// Bottom 7 bits give the number of length bytes to follow.
numBytes := int(b & 0x7f)
if numBytes == 0 {
err = asn1.SyntaxError{Msg: "indefinite length found (not DER)"}
return
}
ret.length = 0
for i := 0; i < numBytes; i++ {
if offset >= len(bytes) {
err = asn1.SyntaxError{Msg: "truncated tag or length"}
return
}
b = bytes[offset]
offset++
if ret.length >= 1<<23 {
// We can't shift ret.length up without
// overflowing.
err = asn1.StructuralError{Msg: "length too large"}
return
}
ret.length <<= 8
ret.length |= int(b)
if ret.length == 0 {
// DER requires that lengths be minimal.
err = asn1.StructuralError{Msg: "superfluous leading zeros in length"}
return
}
}
// Short lengths must be encoded in short form.
if ret.length < 0x80 {
err = asn1.StructuralError{Msg: "non-minimal length"}
return
}
}
return
}
// parseBase128Int parses a base-128 encoded int from the given offset in the
// given byte slice. It returns the value and the new offset.
func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error) {
offset = initOffset
var ret64 int64
for shifted := 0; offset < len(bytes); shifted++ {
// 5 * 7 bits per byte == 35 bits of data
// Thus the representation is either non-minimal or too large for an int32
if shifted == 5 {
err = asn1.StructuralError{Msg: "base 128 integer too large"}
return
}
ret64 <<= 7
b := bytes[offset]
// integers should be minimally encoded, so the leading octet should
// never be 0x80
if shifted == 0 && b == 0x80 {
err = asn1.SyntaxError{Msg: "integer is not minimally encoded"}
return
}
ret64 |= int64(b & 0x7f)
offset++
if b&0x80 == 0 {
ret = int(ret64)
// Ensure that the returned value fits in an int on all platforms
if ret64 > math.MaxInt32 {
err = asn1.StructuralError{Msg: "base 128 integer too large"}
}
return
}
}
err = asn1.SyntaxError{Msg: "truncated base 128 integer"}
return
}
// parsePrintableString parses an ASN.1 PrintableString from the given byte
// array and returns it.
func parsePrintableString(bytes []byte) (ret string, err error) {
for _, b := range bytes {
if !isPrintable(b, allowAsterisk, allowAmpersand) {
err = asn1.SyntaxError{Msg: "PrintableString contains invalid character"}
return
}
}
ret = string(bytes)
return
}
// invalidLength reports whether offset + length > sliceLength, or if the
// addition would overflow.
func invalidLength(offset, length, sliceLength int) bool {
return offset+length < offset || offset+length > sliceLength
}
// parseNumericString parses an ASN.1 NumericString from the given byte array
// and returns it.
func parseNumericString(bytes []byte) (ret string, err error) {
for _, b := range bytes {
if !isNumeric(b) {
return "", asn1.SyntaxError{Msg: "NumericString contains invalid character"}
}
}
return string(bytes), nil
}
// isNumeric reports whether the given b is in the ASN.1 NumericString set.
func isNumeric(b byte) bool {
return '0' <= b && b <= '9' ||
b == ' '
}
// parseIA5String parses an ASN.1 IA5String (ASCII string) from the given
// byte slice and returns it.
func parseIA5String(bytes []byte) (ret string, err error) {
for _, b := range bytes {
if b >= utf8.RuneSelf {
err = asn1.SyntaxError{Msg: "IA5String contains invalid character"}
return
}
}
ret = string(bytes)
return
}
// parseT61String parses an ASN.1 T61String (8-bit clean string) from the given
// byte slice and returns it.
func parseT61String(bytes []byte) (ret string, err error) {
return string(bytes), nil
}
// parseUTF8String parses an ASN.1 UTF8String (raw UTF-8) from the given byte
// array and returns it.
func parseUTF8String(bytes []byte) (ret string, err error) {
if !utf8.Valid(bytes) {
return "", errors.New("asn1: invalid UTF-8 string")
}
return string(bytes), nil
}
// parseInt64 treats the given bytes as a big-endian, signed integer and
// returns the result.
func parseInt64(bytes []byte) (ret int64, err error) {
err = checkInteger(bytes)
if err != nil {
return
}
if len(bytes) > 8 {
// We'll overflow an int64 in this case.
err = asn1.StructuralError{Msg: "integer too large"}
return
}
for bytesRead := 0; bytesRead < len(bytes); bytesRead++ {
ret <<= 8
ret |= int64(bytes[bytesRead])
}
// Shift up and down in order to sign extend the result.
ret <<= 64 - uint8(len(bytes))*8
ret >>= 64 - uint8(len(bytes))*8
return
}
// checkInteger returns nil if the given bytes are a valid DER-encoded
// INTEGER and an error otherwise.
func checkInteger(bytes []byte) error {
if len(bytes) == 0 {
return asn1.StructuralError{Msg: "empty integer"}
}
if len(bytes) == 1 {
return nil
}
if (bytes[0] == 0 && bytes[1]&0x80 == 0) || (bytes[0] == 0xff && bytes[1]&0x80 == 0x80) {
return asn1.StructuralError{Msg: "integer not minimally-encoded"}
}
return nil
}
// parseBitString parses an ASN.1 bit string from the given byte slice and returns it.
func parseBitString(bytes []byte) (ret asn1.BitString, err error) {
if len(bytes) == 0 {
err = asn1.SyntaxError{Msg: "zero length BIT STRING"}
return
}
paddingBits := int(bytes[0])
if paddingBits > 7 ||
len(bytes) == 1 && paddingBits > 0 ||
bytes[len(bytes)-1]&((1<<bytes[0])-1) != 0 {
err = asn1.SyntaxError{Msg: "invalid padding bits in BIT STRING"}
return
}
ret.BitLength = (len(bytes)-1)*8 - paddingBits
ret.Bytes = bytes[1:]
return
}
// parseObjectIdentifier parses an OBJECT IDENTIFIER from the given bytes and
// returns it. An object identifier is a sequence of variable length integers
// that are assigned in a hierarchy.
func parseObjectIdentifier(bytes []byte) (s asn1.ObjectIdentifier, err error) {
if len(bytes) == 0 {
err = asn1.SyntaxError{Msg: "zero length OBJECT IDENTIFIER"}
return
}
// In the worst case, we get two elements from the first byte (which is
// encoded differently) and then every varint is a single byte long.
s = make([]int, len(bytes)+1)
// The first varint is 40*value1 + value2:
// According to this packing, value1 can take the values 0, 1 and 2 only.
// When value1 = 0 or value1 = 1, then value2 is <= 39. When value1 = 2,
// then there are no restrictions on value2.
v, offset, err := parseBase128Int(bytes, 0)
if err != nil {
return
}
if v < 80 {
s[0] = v / 40
s[1] = v % 40
} else {
s[0] = 2
s[1] = v - 80
}
i := 2
for ; offset < len(bytes); i++ {
v, offset, err = parseBase128Int(bytes, offset)
if err != nil {
return
}
s[i] = v
}
s = s[0:i]
return
}
func parseUTCTime(bytes []byte) (ret time.Time, err error) {
s := string(bytes)
formatStr := "0601021504Z0700"
ret, err = time.Parse(formatStr, s)
if err != nil {
formatStr = "060102150405Z0700"
ret, err = time.Parse(formatStr, s)
}
if err != nil {
return
}
if serialized := ret.Format(formatStr); serialized != s {
err = fmt.Errorf("asn1: time did not serialize back to the original value and may be invalid: given %q, but serialized as %q", s, serialized)
return
}
if ret.Year() >= 2050 {
// UTCTime only encodes times prior to 2050. See https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1
ret = ret.AddDate(-100, 0, 0)
}
return
}
// parseGeneralizedTime parses the GeneralizedTime from the given byte slice
// and returns the resulting time.
func parseGeneralizedTime(bytes []byte) (ret time.Time, err error) {
const formatStr = "20060102150405Z0700"
s := string(bytes)
if ret, err = time.Parse(formatStr, s); err != nil {
return
}
if serialized := ret.Format(formatStr); serialized != s {
err = fmt.Errorf("asn1: time did not serialize back to the original value and may be invalid: given %q, but serialized as %q", s, serialized)
}
return
}
// 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
}
// Given a reflected Go type, getUniversalType returns the default tag number
// and expected compound flag.
func getUniversalType(t reflect.Type) (matchAny bool, tagNumber int, isCompound, ok bool) {
switch t {
case rawValueType:
return true, -1, false, true
case objectIdentifierType:
return false, asn1.TagOID, false, true
case bitStringType:
return false, asn1.TagBitString, false, true
case timeType:
return false, asn1.TagUTCTime, false, true
case enumeratedType:
return false, asn1.TagEnum, false, true
case bigIntType:
return false, asn1.TagInteger, false, true
}
switch t.Kind() {
case reflect.Bool:
return false, asn1.TagBoolean, false, true
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return false, asn1.TagInteger, false, true
case reflect.Struct:
return false, asn1.TagSequence, true, true
case reflect.Slice:
if t.Elem().Kind() == reflect.Uint8 {
return false, asn1.TagOctetString, false, true
}
if strings.HasSuffix(t.Name(), "SET") {
return false, asn1.TagSet, true, true
}
return false, asn1.TagSequence, true, true
case reflect.String:
return false, asn1.TagPrintableString, false, true
}
return false, 0, false, false
}
// parseInt treats the given bytes as a big-endian, signed integer and returns
// the result.
func parseInt32(bytes []byte) (int32, error) {
if err := checkInteger(bytes); err != nil {
return 0, err
}
ret64, err := parseInt64(bytes)
if err != nil {
return 0, err
}
if ret64 != int64(int32(ret64)) {
return 0, asn1.StructuralError{Msg: "integer too large"}
}
return int32(ret64), nil
}
var bigOne = big.NewInt(1)
// parseBigInt treats the given bytes as a big-endian, signed integer and returns
// the result.
func parseBigInt(bytes []byte) (*big.Int, error) {
if err := checkInteger(bytes); err != nil {
return nil, err
}
ret := new(big.Int)
if len(bytes) > 0 && bytes[0]&0x80 == 0x80 {
// This is a negative number.
notBytes := make([]byte, len(bytes))
for i := range notBytes {
notBytes[i] = ^bytes[i]
}
ret.SetBytes(notBytes)
ret.Add(ret, bigOne)
ret.Neg(ret)
return ret, nil
}
ret.SetBytes(bytes)
return ret, nil
}
func parseBool(bytes []byte) (ret bool, err error) {
if len(bytes) != 1 {
err = asn1.SyntaxError{Msg: "invalid boolean"}
return
}
// DER demands that "If the encoding represents the boolean value TRUE,
// its single contents octet shall have all eight bits set to one."
// Thus only 0 and 255 are valid encoded values.
switch bytes[0] {
case 0:
ret = false
case 0xff:
ret = true
default:
err = asn1.SyntaxError{Msg: "invalid boolean"}
}
return
}
// parseSequenceOf is used for SEQUENCE OF and SET OF values. It tries to parse
// 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 error) {
matchAny, expectedTag, compoundType, ok := getUniversalType(elemType)
if !ok {
err = asn1.StructuralError{Msg: "unknown Go type for slice"}
return
}
// First we iterate over the input and count the number of elements,
// checking that the types are correct in each case.
numElements := 0
for offset := 0; offset < len(bytes); {
var t tagAndLength
t, offset, err = parseTagAndLength(bytes, offset)
if err != nil {
return
}
switch t.tag {
case asn1.TagIA5String, asn1.TagGeneralString, asn1.TagT61String, asn1.TagUTF8String, asn1.TagNumericString, asn1.TagBMPString:
// We pretend that various other string types are
// PRINTABLE STRINGs so that a sequence of them can be
// parsed into a []string.
t.tag = asn1.TagPrintableString
case asn1.TagGeneralizedTime, asn1.TagUTCTime:
// Likewise, both time types are treated the same.
t.tag = asn1.TagUTCTime
}
if !matchAny && (t.class != classUniversal || t.isCompound != compoundType || t.tag != expectedTag) {
err = asn1.StructuralError{Msg: "sequence tag mismatch"}
return
}
if invalidLength(offset, t.length, len(bytes)) {
err = asn1.SyntaxError{Msg: "truncated sequence"}
return
}
offset += t.length
numElements++
}
ret = reflect.MakeSlice(sliceType, numElements, numElements)
params := fieldParameters{}
offset := 0
for i := 0; i < numElements; i++ {
offset, err = customParseField(ret.Index(i), bytes, offset, params)
if err != nil {
return
}
}
return
}