Skip to content

PKCS#7 ParsePKCS7() fails to decode degenerate SignedData #1425

@tyb-dev

Description

@tyb-dev

Problem

CFSSL's PKCS#7 parser fails to decode valid degenerate PKCS#7 structures (e.g., certs-only bundles created via OpenSSL) with the error:

asn1: syntax error: sequence truncated

This is due to a broken struct tag in crypto/pkcs7/pkcs7.go#L63:

Certificates     asn1.RawValue `asn1:"optional" asn1:"tag:0"`

This syntax is invalid in Go: Go only uses one tag string per struct field. The asn1 key appears twice, so only the last one is parsed.

Go Struct Tag Behavior

From Go's official reflection documentation:

By convention, tag strings are a concatenation of key:"value" pairs, separated by spaces. To retrieve the value for a key, use the Get method.
reflect.StructTag docs

From the Go source, the tag parser in encoding/asn1 uses:

t.Field(i).Tag.Get("asn1")

marshal.go line 961 (Go 1.21.0)

This means the line in CFSSL:

Certificates     asn1.RawValue `asn1:"optional" asn1:"tag:0"`

is treated equivalently to:

Certificates     asn1.RawValue `asn1:"tag:0"`

The optional modifier is ignored entirely.


ASN.1 Semantics

The CMS / PKCS#7 specification (RFC 5652 §5.1) defines the certificates field as:

certificates [0] IMPLICIT CertificateSet OPTIONAL

Where CertificateSet ::= SET OF CertificateChoices. This means the DER encoding is:

A0 ... 31 ...   # context-specific tag 0 (IMPLICIT), followed by SET (correct!)

But Go (with tag:0) expects:

A0 ... 30 ...   # context-specific tag 0 (IMPLICIT), followed by SEQUENCE (wrong!)

Because Go defaults to SEQUENCE unless set is specified.


Fix in Go

To parse a [0] IMPLICIT SET OF structure, the correct tag is:

asn1:"optional,set,tag:0"

This informs Go to expect:

  • Context-specific tag 0
  • IMPLICIT tag replacement (default behavior)
  • A SET (not a SEQUENCE)
  • Optionality

Proposed Fix

In crypto/pkcs7/pkcs7.go, change:

Certificates     asn1.RawValue `asn1:"optional" asn1:"tag:0"`

to:

Certificates     asn1.RawValue `asn1:"optional,set,tag:0"`

Or, better yet — parse the certs directly:

Certificates     []*x509.Certificate `asn1:"optional,set,tag:0"`

This will fix compatibility with all OpenSSL-generated .p7b bundles and bring CFSSL into alignment with the CMS standard. Caveat: I did not verify if this works.


Repro

Create a degenerate PKCS#7 structure:

openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem -days 1 -subj "/CN=example.com"
openssl crl2pkcs7 -nocrl -certfile cert.pem -outform DER -out certs-only.p7b

An example PKCS7 file is attached below (example.p7b). For the purpose of posting it on this issue, it has been base64 encoded. (To feed it into ParsePKCS7 you need to execute base64.StdEncoding.DecodeString on it first, or use the command line statements above to get a binary DER file which isn't base64 encoded).

MIIBygYJKoZIhvcNAQcCoIIBuzCCAbcCAQExADALBgkqhkiG9w0BBwGgggGfMIIB
mzCCAUGgAwIBAgIUcYmQ/QL987d0R3vNzzKPsahNt7kwCgYIKoZIzj0EAwIwIzEU
MBIGA1UEAwwLZXhhbXBsZS5jb20xCzAJBgNVBAYTAlVTMB4XDTI1MDgwNDEwMzEy
NloXDTI1MDgwNTEwMzEyNlowIzEUMBIGA1UEAwwLZXhhbXBsZS5jb20xCzAJBgNV
BAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9ik4neGlQRDogJDhgFQ/
kD2myoojA6i78FE4U0oL6Pe8CyRvkC2Vvr422NW9gXFhLMcdQbzHdCmb71CsWv3K
/aNTMFEwHQYDVR0OBBYEFCl6EWnoXJjjN7t8TRaXHfSvC6jBMB8GA1UdIwQYMBaA
FCl6EWnoXJjjN7t8TRaXHfSvC6jBMA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0E
AwIDSAAwRQIhAKiWWGvqZeIZej69JpEH5DU9cOFYgCdgIoqSRGn2ZA8UAiBPixlT
BXsBRGajsRPBHX19SetTmQ4m8GhykPptwYcoEDEA

Then call ParsePKCS7() on the result. It fails unless the struct tag is corrected.


Request

Please update the tag on Certificates in signedData to include set. This will fix a standards-compliant decoding failure and make ParsePKCS7() work on widely used PKCS#7 formats.

Thanks!

Context

Degenerate PKCS#7 is used for example in the EST protocol's cacerts endpoint RFC 7030. Tools such as OpenSSL or Windows Certificate Manager can open the above example.p7b file without any issues.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions