cnsa

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 20, 2025 License: MIT Imports: 13 Imported by: 0

README

CNSA

CNSA Lite for Go

A partial implementation of NSA's Commercial National Security Algorithm Suite (CNSA) 2.0 in Go. This is a "lite" version designed to harden the existing classical baseline algorithms only.

Summary

This package imposes additional restrictions atop Go's FIPS 140-3 module, aiming to approximate the CNSA 2.0 requirements for classical baseline algorithms only. It is a best-effort approach operating within the constraints of Go's standard libraries and does not achieve formal compliance with CNSA 2.0 at this time.

Most notably, it does not include the future quantum-resistant algorithms specified in CNSA 2.0, such as ML-KEM-1024 and ML-DSA-87, which are central to the suite's objectives. Additionally, Go currently lacks a mechanism to disable the AES-128 variant within the TLS 1.3 cipher suite list (TLS_AES_128_GCM_SHA256).

Despite these known limitations, this package can still be viewed as providing additional hardening above and beyond what's provided in FIPS 140-3, working in the general direction of CNSA 2.0 and attempting to satisfy several of the key requirements outlined in the discussion section of the most recent update to DISA STIG V-265634.

TLS Hardening Details

This package provides a hardened *tls.Config object to achieve the following objectives:

  • Enforces TLS 1.3 Only:
    • Connections are restricted exclusively to TLS 1.3. TLS 1.2 and all prior versions are disabled.
  • Restricts Key Exchange Mechanism:
    • Mandates Elliptic Curve Diffie-Hellman Ephemeral key exchange using the P-384 curve (ECDHE P-384).
    • Traditional RSA key exchange (key transport) is not used, as it is deprecated in TLS 1.3.
  • Governs Certificate Cryptography:
    • Public Keys: Requires that the public keys in the server's certificate (and any peer certificates, if mTLS is used) are either:
      • RSA with a minimum key length of 3072 bits, or
      • ECDSA using the P-384 curve.
    • Signature Algorithms: Mandates that all certificates in the verified chain (including the server's own certificate and any intermediate/peer certificates) are signed using SHA-384 (e.g., SHA384WithRSA, ECDSAWithSHA384, SHA384WithRSAPSS).
  • Manages TLS 1.3 Cipher Suites (within Go's FIPS mode limitations):
    • Relies on Go's FIPS 140-3 mode (activated by GODEBUG=fips140=on) being enabled. This inherently disables certain cipher suites not permitted by FIPS, such as TLS_CHACHA20_POLY1305_SHA256.
    • The primary targeted CNSA 2.0 compliant cipher suite is TLS_AES_256_GCM_SHA384.
    • Limitation: Go's standard library (as of version 1.24) does not provide a direct mechanism to remove TLS_AES_128_GCM_SHA256 from the list of available TLS 1.3 cipher suites. This suite is allowed by FIPS 140-3 but not by CNSA 2.0.
    • Workaround: An optional function (cnsa.WithRejectAES128()) is provided, which can be used to configure the *tls.Config to reject any connection that successfully negotiates TLS_AES_128_GCM_SHA256 after the handshake is complete.
  • Enhances Session Security:
    • Disables TLS session tickets by default. This is a conservative security measure, though session tickets can be re-enabled via an option (cnsa.WithSessionTicketsEnabled()) if permitted by specific operational requirements.
    • Prohibits TLS renegotiation (set to tls.RenegotiateNever).
  • Server Cipher Suite Preference:
    • The PreferServerCipherSuites flag is set to true. While its impact is reduced in TLS 1.3 (where cipher suite selection is more deterministic), it's included as a general hardening practice.

It's crucial to understand that this package provides a best-effort hardening towards CNSA 2.0 for classical algorithms and does not achieve formal compliance, particularly due to the aforementioned cipher suite limitation and the absence of quantum-resistant algorithms.

TLS Key Requirements

Component NSA Suite B Cryptography (2005) CNSA 1.0 (2016) CNSA 2.0 (2022) - Classical Baseline CNSA 2.0 (2022) - Future Quantum-Resistant
Symmetric Crypto AES-128 and AES-256 AES-256 AES-256 AES-256
Hash / MAC SHA-256 and SHA-384 SHA-384 SHA-384 SHA-384
Key Exchange (KE) ECDH P-256, P-384 DH/RSA >= 3072, ECDH P-384 DH/RSA >= 3072, ECDH P-384 CRYSTALS-Kyber (ML-KEM)
Digital Signatures (DS) ECDSA P-256, P-384 RSA >= 3072, ECDSA P-384 RSA >= 3072, ECDSA P-384 CRYSTALS-Dilithium (ML-DSA)
Software/Firmware Signatures (As above) (As above) (As above, noting transition) SPHINCS+ (SLH-DSA), Dilithium
TLS Profile TLS 1.2 (AES-GCM/ECC) TLS >= 1.2 (CNSA 1.0 suites) TLS >= 1.3 (using classical suites) TLS >= 1.3 (Initially Hybrid KE, then QR KE/DS)
Primary Focus ECC Adoption Stronger Classical Algorithms Maintain Strong Classical Baseline During Transition Mandated Transition to PQC/QR

TLS Example Usage

//go:debug fips140=on
package main

import (
	"crypto/fips140"
	"log"
	"net/http"

	"github.com/juburr/cnsa"
)

func main() {
	if !fips140.Enabled() {
		log.Fatalf("FIPS 140-3 mode must be enabled (set GODEBUG=fips140=on)")
	}

	cfg, err := cnsa.NewTLSConfig(
		cnsa.WithX509KeyPair("server.crt", "server.key"),
	)
	if err != nil {
		log.Fatalf("Failed to create TLS config: %v", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/plain")
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("Hello, CNSA World!"))
	})

	svr := &http.Server{
		Addr:      "localhost:443",
		Handler:   mux,
		TLSConfig: cfg,
	}

	log.Println("Server is listening on https://localhost:443")
	log.Fatal(svr.ListenAndServeTLS("", ""))
}

Documentation

Overview

Package cnsa provides configurations for Go's cryptographic libraries, aiming to adhere to NSA's Commercial National Security Algorithm Suite (CNSA) 2.0 requirements for classical baseline algorithms.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ExampleAuthorizedKeysCallback

func ExampleAuthorizedKeysCallback(authorizedKeysMap map[string]bool) func(ssh.ConnMetadata, ssh.PublicKey) (*ssh.Permissions, error)

ExampleAuthorizedKeysCallback returns an ssh.PublicKeyCallback function suitable for use with `WithCNSAPublicKeyAuth`. It checks if the public key presented by the client exists in the `authorizedKeysMap`. This map should be pre-populated by `LoadAuthorizedKeysFromFile` using a CNSA-compliant validator.

func LoadAuthorizedKeysFromFile

func LoadAuthorizedKeysFromFile(filePath string, validator func(key ssh.PublicKey) error) (map[string]bool, error)

LoadAuthorizedKeysFromFile loads public keys from a file path, parsing the content in standard authorized_keys format (one key per line, comments allowed). It validates each parsed key using the provided `validator` function (e.g., ValidateSSHPublicKey). If a non-compliant key is found, it returns an error. Returns a map where keys are the base64-encoded wire format of the authorized public keys.

func LoadPrivateHostKeyFromFile

func LoadPrivateHostKeyFromFile(keyFile string) (ssh.Signer, error)

LoadPrivateHostKeyFromFile loads a private key (PEM format) from the specified file, validates its public component against CNSA 2.0 requirements using ValidateSSHPublicKey, and returns an ssh.Signer.

func NewSSHClientConfig

func NewSSHClientConfig(username string, options ...sshClientOption) (*ssh.ClientConfig, error)

NewSSHClientConfig returns an ssh.ClientConfig configured with cryptographic algorithms adhering to CNSA 2.0 classical baseline requirements. Requires Go's FIPS 140-3 mode to be enabled (`GODEBUG=fips140=only`) and a non-empty username.

Defaults:

  • KEX: ecdh-sha2-nistp384
  • Ciphers: aes256-gcm@openssh.com
  • MACs: hmac-sha2-384
  • HostKeyAlgorithms (accepted host key signature types): ecdsa-sha2-nistp384, rsa-sha2-512

CRITICAL: A HostKeyCallback MUST be provided via options (e.g., using WithCNSACompliantHostKeyCallback wrapping a known_hosts implementation) to ensure server authenticity and prevent MITM attacks. CRITICAL: Authentication methods (e.g., `ssh.PublicKeys(...)`) MUST be configured by the caller by setting the `Auth` field on the returned config or via options.

func NewSSHServerConfig

func NewSSHServerConfig(options ...sshServerOption) (*ssh.ServerConfig, error)

NewSSHServerConfig returns an ssh.ServerConfig configured with cryptographic algorithms adhering to CNSA 2.0 classical baseline requirements. Requires Go's FIPS 140-3 mode to be enabled (`GODEBUG=fips140=only`).

Defaults:

  • KEX: ecdh-sha2-nistp384
  • Ciphers: aes256-gcm@openssh.com
  • MACs: hmac-sha2-384
  • Password authentication: DISABLED
  • Public key authentication: DISABLED (must be enabled via WithCNSAPublicKeyAuth)

CRITICAL: Host keys MUST be added via options (e.g., WithHostKeyFile). The server will fail the handshake if no host keys are configured.

func NewTLSConfig

func NewTLSConfig(options ...tlsOption) (*tls.Config, error)

NewTLSConfig returns a TLS configuration that adheres to CNSA 2.0, keeping only TLS 1.3 with "TLS_AES_256_GCM_SHA384" as the cipher suite with a key exchange of ECDHE with P-384 or RSA with at least 3072 bits.

func ValidateSSHPublicKey

func ValidateSSHPublicKey(key ssh.PublicKey) error

ValidateSSHPublicKey checks if an ssh.PublicKey meets CNSA 2.0 requirements for key type and size/curve (ECDSA P-384 or RSA >= 3072 bits).

func ValidateTLSCertificate

func ValidateTLSCertificate(cert *tls.Certificate) error

ValidateTLSCertificate verifies that the actual server certificate loaded by the TLS server meets CNSA 2.0 requirements as well. This is important because the other functions only verify the certificate chain of peer certificates, not the server certificate itself.

func ValidateX509Certificate

func ValidateX509Certificate(cert *x509.Certificate) error

ValidateX509Certificate checks if an X509 certificate meets CNSA 2.0 requirements

func WithCNSACompliantHostKeyCallback

func WithCNSACompliantHostKeyCallback(underlyingCallback ssh.HostKeyCallback) sshClientOption

WithCNSACompliantHostKeyCallback returns an sshClientOption that sets a HostKeyCallback. The configured callback first validates the server's host key against CNSA 2.0 requirements using ValidateSSHPublicKey. If the key is compliant, it then invokes the provided `underlyingCallback` (e.g., `knownhosts.New` callback) to verify host authenticity. An `underlyingCallback` MUST be provided to prevent TOFU attacks.

func WithCNSAPublicKeyAuth

func WithCNSAPublicKeyAuth(authorizedKeysCallback func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error)) sshServerOption

WithCNSAPublicKeyAuth returns an sshServerOption that configures the PublicKeyCallback. The callback first validates the presented public key against CNSA 2.0 requirements. If compliant, it delegates to the provided `authorizedKeysCallback` to determine if the key is authorized for the user. If `authorizedKeysCallback` is nil, this option effectively disables public key auth unless the default behavior (accepting any compliant key) is explicitly desired (NOT RECOMMENDED).

func WithHostKeyFile

func WithHostKeyFile(keyFile string) sshServerOption

WithHostKeyFile returns an sshServerOption that loads a host key from the given file path and adds it to the server config using the AddHostKey method. The key is validated for CNSA 2.0 compliance before being added.

func WithMutualTLS

func WithMutualTLS(clientCAPool *x509.CertPool) tlsOption

WithMutualTLS configures the server to require and verify client certificates signed by a CA present in the provided clientCAPool. While not required or discussed in CNSA 2.0, other government standards (e.g., NIST/CMMC) do require mTLS in certain contexts. This function is provided as an optional convenience for those who need it. It is not required for CNSA 2.0 compliance.

func WithMutualTLSFromFile

func WithMutualTLSFromFile(clientCAFile string) tlsOption

WithMutualTLSFromFile configures the server to require and verify client certificates signed by a CA present in the specified clientCAFile (PEM format). This is convenience wrapper around WithMutualTLS that loads the CA file into a CertPool. It is not required for CNSA 2.0 compliance.

func WithRejectAES128

func WithRejectAES128() tlsOption

WithRejectAES128 adds a VerifyConnection hook that will reject any TLS 1.3 handshake negotiating the AES-128 cipher suite, as it is not allowed by CNSA 2.0, but we have no way to disable it directly in the Go standard library. This is a workaround (or dare I say, dirty hack), and is not clean because it allows the negotiation to complete before severing the connection. I don't recommend using this in production, but offer it merely as a convenience for those who have hard requirements to reject AES-128 cipher suites.

func WithSessionTicketsEnabled

func WithSessionTicketsEnabled() tlsOption

WithSessionTicketsEnabled enables session tickets in the TLS configuration. Although th is is not required or recommended by FIPS or CNSA 2.0, it isn't explicitly prohibited, and may be allowed by a specific ATO. We default to disabling session tickets to be conservative and avoid any potential security issues. This optional function can be used to re-enable session tickets.

func WithX509KeyPair

func WithX509KeyPair(certFile, keyFile string) tlsOption

WithX509KeyPair loads a certificate and private key from the specified files and adds them to the TLS configuration.

Types

This section is empty.

Directories

Path Synopsis
examples
ssh_server command
tls_server command