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.
Go version
go version go1.21.5 linux/amd64
What operating system and processor architecture are you using (
go env)?What did you do?
Attempted to use
ReadOptionalASN1Booleanto read theonlyContainsCACertsandonlyContainsUserCertsfields of a CRL'sIssuingDistributionPointextension.You can see my attempt, failure, and resulting investigation here. I also have a Go playground which minimally reproduces the situation:
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.