Skip to content

x/crypto/cryptobyte: support reading implicitly-tagged values #64811

@aarongable

Description

@aarongable

Go version

go version go1.21.5 linux/amd64

What operating system and processor architecture are you using (go env)?

GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/home/aaron/.cache/go-build'
GOENV='/home/aaron/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/home/aaron/.local/share/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/aaron/.local/share/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.21.5'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='/dev/null'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build3470374486=/tmp/go-build -gno-record-gcc-switches'

What did you do?

Attempted to use ReadOptionalASN1Boolean to read the onlyContainsCACerts and onlyContainsUserCerts fields of a CRL's IssuingDistributionPoint extension.

You can see my attempt, failure, and resulting investigation here. I also have a Go playground which minimally reproduces the situation:

func example(extBytes []byte) error {
	idpExt := cryptobyte.String(extBytes)
	if !idpExt.ReadASN1(&idpExt, asn1.SEQUENCE) {
		return errors.New("Failed to read IssuingDistributionPoint distributionPoint")
	}

	var onlyContainsCACerts bool
	onlyContainsCACertsTag := asn1.Tag(2).ContextSpecific()
	if !idpExt.ReadOptionalASN1Boolean(&onlyContainsCACerts, onlyContainsCACertsTag, false) {
		return errors.New("Failed to read IssuingDistributionPoint onlyContainsCACerts")
	}

	return nil
}

func main() {
	// IMPLICIT encoding
	// Sequence of length 3, containing...
	// Context-specific tag 2 of length 1, containing...
	// A byte of all ones (the representation of the boolean TRUE).
	idpExtImplicit := []byte{0x30, 0x03, 0x82, 0x01, 0xFF}
	log.Println(example(idpExtImplicit))

	// EXPLICIT encoding
	// Sequence of length 5, containing...
	// Context-specific tag 2 of length 3, containing...
	// Boolean of length 1, containing...
	// A byte of all ones (the representation of the boolean TRUE).
	idpExtExplicit := []byte{0x30, 0x05, 0x82, 0x03, 0x01, 0x01, 0xFF}
	log.Println(example(idpExtExplicit))
}

What did you expect to see?

I expected ReadOptionalASN1Boolean to succeed when reading the onlyContainsCACerts field. That field is an optional boolean, so ReadOptionalASN1Boolean should successfully read it.

What did you see instead?

ReadOptionalASN1Boolean fails when attempting to read the onlyContainsCACerts field. This is because ReadOptionalASN1Boolean only works for explicitly-tagged fields. It's expecting to get a five-byte sequence, as in my second example above: a context-specific tag and length, wrapping a normal boolean tag, length, and value.

But the IssuingDistributionPoint extension is defined (in both X.509 and RFC 5280) in an ASN.1 module that sets DEFINITIONS IMPLICIT TAGS, so these fields are implicitly-tagged, not explicitly-tagged. They're only correctly encoded as a three bytes: a context-specific tag and length followed immediately by the single-byte boolean itself.

There are lots of IMPLICIT fields in x509, so it's somewhat concerning that the cryptobyte parser only has facilities for parsing EXPLICIT fields.

Note: Perhaps I should have expected the behavior I saw, because the documentation of the methods in question does say (emphasis added) "ReadOptionalASN1Boolean attempts to read an optional ASN.1 BOOLEAN explicitly tagged with tag...". But I think this single word is easy to miss, especially when it's being used as a term of art rather than as plain english. And regardless of the behavior of the existing methods, I still think it would be valuable to have methods which can parse implicitly-tagged fields.

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.ProposalProposal-Accepted

    Type

    No type

    Projects

    Status

    Accepted

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions