A personal Go library for CMS/PKCS#7 with Ed25519 support.
go-cms provides an idiomatic Go interface for creating and parsing
Cryptographic Message Syntax (CMS) messages, as specified in
RFC 5652.
Existing Go CMS libraries (mozilla/pkcs7, github.com/cloudflare/cfssl) do not support Ed25519 signatures. This library fills that gap with an implementation that passes OpenSSL verification for Ed25519-signed CMS messages.
⚠️ Important: This is an unreviewed personal project. While it passes OpenSSL interoperability tests, it has not undergone formal security review. Use at your own discretion.
- Ed25519 Signing: Create CMS
SignedDatastructures using Ed25519 keys. - Standards-Compliant: Aims for strict adherence to relevant RFCs for maximum interoperability.
- OpenSSL Compatible: Signatures generated by
go-cmscan be verified with standard OpenSSL tooling. - Clean API: A simple, Go-idiomatic interface that hides the complexity of ASN.1 encoding.
- Well-Tested: A high ratio of tests to code, including round-trip and interoperability tests.
- Zero Dependencies: Relies only on the Go standard library and
golang.org/x/crypto.
Current version: v0.0.1
The library is functional and the API is stable. All tests pass, including OpenSSL interoperability verification.
go get github.com/agentic-research/go-cmsThis example demonstrates signing a message with an Ed25519 key and generating a
detached CMS/PKCS#7 signature that can be verified with OpenSSL.
package main
import (
"crypto/ed25519"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"log"
"math/big"
"os"
"time"
"github.com/agentic-research/go-cms/pkg/cms"
)
func main() {
// 1. Generate a new Ed25519 key pair
pubKey, privKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
log.Fatalf("Failed to generate key: %v", err)
}
// 2. Create a self-signed certificate
template := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
Organization: []string{"Test Organization"},
Country: []string{"US"},
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(365 * 24 * time.Hour),
KeyUsage: x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning},
}
certDER, err := x509.CreateCertificate(rand.Reader, template, template, pubKey, privKey)
if err != nil {
log.Fatalf("Failed to create certificate: %v", err)
}
cert, err := x509.ParseCertificate(certDER)
if err != nil {
log.Fatalf("Failed to parse certificate: %v", err)
}
// 3. Sign the content
content := []byte("Hello, CMS!")
signature, err := cms.SignData(content, cert, privKey)
if err != nil {
log.Fatalf("Failed to sign content: %v", err)
}
// The output is a DER-encoded CMS message
fmt.Printf("Successfully generated CMS signature (%d bytes)\n", len(signature))
// 4. Write files for OpenSSL verification
if err := os.WriteFile("signature.der", signature, 0644); err != nil {
log.Fatal(err)
}
if err := os.WriteFile("content.txt", content, 0644); err != nil {
log.Fatal(err)
}
// Export certificate for OpenSSL
certPEM := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: certDER,
})
if err := os.WriteFile("cert.pem", certPEM, 0644); err != nil {
log.Fatal(err)
}
}You can verify the generated signature using the standard openssl command line
tool. After running the example above:
# Verify the signature with OpenSSL
openssl cms -verify \
-inform DER -in signature.der \
-content content.txt \
-certfile cert.pem \
-noverify \ # Use -noverify as the cert is self-signed
-binary # Important for detached signatures
# On success, the command will output:
# CMS Verification successful
# Hello, CMS!The library provides two main functions:
cms.SignData(data []byte, cert *x509.Certificate, privateKey ed25519.PrivateKey) ([]byte, error)- Creates a CMS signaturecms.Verify(cmsData, originalData []byte, opts VerifyOptions) ([]*x509.Certificate, error)- Verifies a CMS signature
For detailed API documentation, see pkg.go.dev.
The library enforces the following size limits for security:
- Maximum CMS signature size: 1MB (prevents memory exhaustion from malformed signatures)
- Maximum certificate size: 64KB (standard X.509 certificates are typically 1-4KB)
- Supported digest algorithm: SHA-256 only (MD5 and SHA-1 are rejected)
- Supported signature algorithm: Ed25519 only
This is a personal, open-source project. While developed with security best practices in mind, it comes with no guarantees.
For responsible disclosure of security vulnerabilities, please email me directly
at jamestexasgardner [at] gmail [dot] comand prefix the subject with[SECURITY]. I will endeavor to address all issues promptly. A formal SECURITY.md` file will be
created as the project matures. You can also reach out to me via @jamestexas on Github.
Contributions are welcome! If you are interested in helping, please see the contributing documentation for guidelines. Priority areas include adding support for parsing/verification and expanding algorithm support. Please ensure all contributions include appropriate tests.