Skip to content

agentic-research/go-cms

Repository files navigation

🛡️ go-cms

Version

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.

Features

  • Ed25519 Signing: Create CMS SignedData structures using Ed25519 keys.
  • Standards-Compliant: Aims for strict adherence to relevant RFCs for maximum interoperability.
  • OpenSSL Compatible: Signatures generated by go-cms can 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.

Status

Current version: v0.0.1

The library is functional and the API is stable. All tests pass, including OpenSSL interoperability verification.

Installation

go get github.com/agentic-research/go-cms

Quick Start

This 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)
    }
}

Verifying with OpenSSL

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!

API Documentation

The library provides two main functions:

  • cms.SignData(data []byte, cert *x509.Certificate, privateKey ed25519.PrivateKey) ([]byte, error) - Creates a CMS signature
  • cms.Verify(cmsData, originalData []byte, opts VerifyOptions) ([]*x509.Certificate, error) - Verifies a CMS signature

For detailed API documentation, see pkg.go.dev.

Limits

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

Security

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.

Contributing

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.

About

🛡️ A modern Go library for CMS/PKCS#7 messages with Ed25519 support.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages