From d9874b9cef75c7acd088d8fed000790bf0011cce Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Wed, 12 Aug 2026 12:24:53 -0700 Subject: [PATCH] F-8579 - Require explicit key manager TLS credentials --- .github/workflows/make-test.yml | 22 +++++- .gitignore | 1 + ChangeLog.md | 3 + README.md | 29 +++++--- certs/ca-cert.pem | 16 ----- certs/ca-key.pem | 7 -- certs/client-cert.pem | 17 ----- certs/client-key.pem | 7 -- certs/gen-certs.sh | 56 +++++++++++++-- certs/include.am | 11 +-- certs/server-cert.pem | 21 ------ certs/server-key.pem | 7 -- certs/server-rsa-cert.pem | 25 ------- certs/server-rsa-key.pem | 30 -------- src/keymanager.c | 110 ++++++++++++++++++++++++---- tests/credential-options.test | 123 ++++++++++++++++++++++++++++++++ tests/include.am | 3 + wolfkeymgr/keymanager.h | 12 ---- 18 files changed, 321 insertions(+), 179 deletions(-) delete mode 100644 certs/ca-cert.pem delete mode 100644 certs/ca-key.pem delete mode 100644 certs/client-cert.pem delete mode 100644 certs/client-key.pem delete mode 100644 certs/server-cert.pem delete mode 100644 certs/server-key.pem delete mode 100644 certs/server-rsa-cert.pem delete mode 100644 certs/server-rsa-key.pem create mode 100755 tests/credential-options.test diff --git a/.github/workflows/make-test.yml b/.github/workflows/make-test.yml index c352cd6..81deb64 100644 --- a/.github/workflows/make-test.yml +++ b/.github/workflows/make-test.yml @@ -58,10 +58,30 @@ jobs: run: sudo make install - name: wolfKeyMgr make dist run: make dist + - name: verify release credential generation + run: | + mkdir dist-test + tar xzf wolfkeymanager-*.tar.gz -C dist-test --strip-components=1 + cd dist-test + ./certs/gen-certs.sh + test -s certs/ca-key.pem + test -s certs/ca-cert.pem + test -s certs/client-key.pem + test -s certs/client-cert.pem + test -s certs/server-key.pem + test -s certs/server-cert.pem + test -s certs/server-rsa-key.pem + test -s certs/server-rsa-cert.pem + test -s certs/demo-password.txt # wolfKeyMgr examples + - name: generate demo credentials + run: ./certs/gen-certs.sh - name: start key manager service - run: sudo ./src/wolfkeymgr & + run: | + sudo ./src/wolfkeymgr \ + -k ./certs/server-rsa-key.pem -W ./certs/demo-password.txt \ + -c ./certs/server-rsa-cert.pem -A ./certs/ca-cert.pem & - name: run https server run: sudo ./examples/https/server & - name: run the middle box decryptor diff --git a/.gitignore b/.gitignore index 0fa1604..4f17b0b 100644 --- a/.gitignore +++ b/.gitignore @@ -60,6 +60,7 @@ certs/server-*.der certs/server-*.pem certs/client-*.der certs/client-*.pem +certs/demo-password.txt certs/serial.old certs/1*.pem tracefile.txt diff --git a/ChangeLog.md b/ChangeLog.md index 54e40db..4c1880b 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,6 @@ +# wolfKeyMgr v1.2 +* Require explicit TLS credentials and generate demo credentials locally. + # wolfKeyMgr v1.1 (Nov 9, 2021) * Added support for x448 (curve448). * Added argument support to HTTP server. diff --git a/README.md b/README.md index f2019cb..895c1e5 100644 --- a/README.md +++ b/README.md @@ -129,14 +129,16 @@ wolfKeyManager 1.1 -r Key renewal timeout, default 3600 -u Key renewal max use count, default 100 -t Thread pool size, default 16 --k TLS Server TLS Key, default ./certs/server-rsa-key.pem --w TLS Server Key Password, default wolfssl --c TLS Server Certificate, default ./certs/server-rsa-cert.pem --A TLS CA Certificate, default ./certs/ca-cert.pem +-k TLS Server Key (required) +-W TLS Server Key Password File (required) +-c TLS Server Certificate (required) +-A TLS CA Certificate (required) -K Key Type: SECP256R1, FFDHE_2048, X25519 or X448 (default SECP256R1) -v Vault file for key storage, default ./wolfkeymgr.vault ``` +The key manager refuses to start unless all four TLS credential options are +provided. The password file must be non-empty and accessible only by its owner. To exit the key manager use ctrl+c. ### ETS Test client @@ -242,11 +244,18 @@ password Private Key Password if required ## Demo Usage -1. Start the key manager: `./src/wolfkeymgr` -2. Run the HTTPS server `./examples/https/server` -3. Run the middle-box decryption `./examples/middlebox/decrypt` and use the default parameters. -4. Open a web browser to `https://localhost` or run the HTTP client example `./examples/https/client`. -5. In the middle-box decryption window you will see the decrypted HTTPS traffic. +1. Generate credentials for this demo: `./certs/gen-certs.sh` +2. Start the key manager: `./src/wolfkeymgr -k ./certs/server-rsa-key.pem -W ./certs/demo-password.txt -c ./certs/server-rsa-cert.pem -A ./certs/ca-cert.pem` +3. Run the HTTPS server `./examples/https/server` +4. Run the middle-box decryption `./examples/middlebox/decrypt` and use the default parameters. +5. Open a web browser to `https://localhost` or run the HTTP client example `./examples/https/client`. +6. In the middle-box decryption window you will see the decrypted HTTPS traffic. + +The generated credentials are for local demonstration only. Deployments must +use a private CA and unique server and client credentials. Credentials from +older wolfKeyMgr releases must be considered compromised and rotated. +For an existing demo directory, run `./certs/gen-certs.sh clean` before +generating replacement credentials. Notes: @@ -265,7 +274,7 @@ Notes: ### Demo example output ``` -% ./src/wolfkeymgr +% ./src/wolfkeymgr -k ./certs/server-rsa-key.pem -W ./certs/demo-password.txt -c ./certs/server-rsa-cert.pem -A ./certs/ca-cert.pem Aug 03 15:05:21 2021: [INFO] Starting Key Manager Aug 03 15:05:21 2021: [INFO] To exit use ctrl+c Aug 03 15:05:21 2021: [INFO] loaded CA certificate file ./certs/ca-cert.pem diff --git a/certs/ca-cert.pem b/certs/ca-cert.pem deleted file mode 100644 index 3457021..0000000 --- a/certs/ca-cert.pem +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIClTCCAjugAwIBAgIUZzNFzfT3gew6OMtFWGN5KCu962swCgYIKoZIzj0EAwIw -gZcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdT -ZWF0dGxlMRAwDgYDVQQKDAd3b2xmU1NMMRQwEgYDVQQLDAtEZXZlbG9wbWVudDEY -MBYGA1UEAwwPd3d3LndvbGZzc2wuY29tMR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdv -bGZzc2wuY29tMB4XDTIxMDIyNDAxMDUyN1oXDTQxMDIxOTAxMDUyN1owgZcxCzAJ -BgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxl -MRAwDgYDVQQKDAd3b2xmU1NMMRQwEgYDVQQLDAtEZXZlbG9wbWVudDEYMBYGA1UE -AwwPd3d3LndvbGZzc2wuY29tMR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdvbGZzc2wu -Y29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDeCw/DZVbeITxJBRaQpTbb4g -bBtL88ZqSt/sWhABdP21pVxBXHy6UEeW+vl6HrEZomAKEJ9USlnskQap37RmlaNj -MGEwHQYDVR0OBBYEFGd0eZ7H+R2lQl76gLEm6OeMDJZjMB8GA1UdIwQYMBaAFGd0 -eZ7H+R2lQl76gLEm6OeMDJZjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD -AgGGMAoGCCqGSM49BAMCA0gAMEUCIQDi1neivT9e9IZGKjEjAWXieOsE1pt3B96F -55LJEpk3ZwIgcPa8qKZ1EGX6Xz7w0SRxw/CiRHTlm4ZEaLcxTrF0eEI= ------END CERTIFICATE----- diff --git a/certs/ca-key.pem b/certs/ca-key.pem deleted file mode 100644 index 7792129..0000000 --- a/certs/ca-key.pem +++ /dev/null @@ -1,7 +0,0 @@ ------BEGIN ENCRYPTED PRIVATE KEY----- -MIHsMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAgaJGPnqcysUwICCAAw -DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEJfGjbWqwIEnod7fHoguu3oEgZAd -IoF3rmsiRP1cX74twJNSx90Iq2cXcJSkoGBkhs0PaXJjaLe8KmS8DX0JLSX9yMRC -2R0wPZT0eb8rvb2ShjBb06Nhexle7Nj4sXcBS7nD3zE4Y3LejaGBpPNko7WJRN8E -Pdi70KK9hCrrpiyerGSw0IzBWjOBiUbNcRV5o0BUv+yTbGkSVk/3xE4HJffFdY8= ------END ENCRYPTED PRIVATE KEY----- diff --git a/certs/client-cert.pem b/certs/client-cert.pem deleted file mode 100644 index 94e8d50..0000000 --- a/certs/client-cert.pem +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICqDCCAk+gAwIBAgICEAMwCgYIKoZIzj0EAwIwgZcxCzAJBgNVBAYTAlVTMRMw -EQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMRAwDgYDVQQKDAd3 -b2xmU1NMMRQwEgYDVQQLDAtEZXZlbG9wbWVudDEYMBYGA1UEAwwPd3d3LndvbGZz -c2wuY29tMR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdvbGZzc2wuY29tMB4XDTIxMDIy -NDAxMDUyN1oXDTMxMDIyMjAxMDUyN1owgY8xCzAJBgNVBAYTAlVTMRMwEQYDVQQI -DApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMRAwDgYDVQQKDAdFbGlwdGlj -MQwwCgYDVQQLDANFQ0MxGDAWBgNVBAMMD3d3dy53b2xmc3NsLmNvbTEfMB0GCSqG -SIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEH -A0IABJPL3WW6Oqw5vcOsitgci4RbSm+6CryZHnj1G1oWSHsU1VFwctBesxczv2KA -cmnYXnanN5xUCiS+Jk7aYGENQ++jgZAwgY0wCQYDVR0TBAIwADARBglghkgBhvhC -AQEEBAMCBaAwHQYDVR0OBBYEFGNRe0i70kUgyqrVLPR/FE7DAGlZMB8GA1UdIwQY -MBaAFGd0eZ7H+R2lQl76gLEm6OeMDJZjMA4GA1UdDwEB/wQEAwIF4DAdBgNVHSUE -FjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwCgYIKoZIzj0EAwIDRwAwRAIgQy5ItBCA -FPFjDQD1AuMd3tsSi/4hbxt9Xw/txgdOXOwCIGRL1VkpSbrPxhSeAR3yPTzaHrVU -MK5TxoCwj4EEHBik ------END CERTIFICATE----- diff --git a/certs/client-key.pem b/certs/client-key.pem deleted file mode 100644 index 3f219f0..0000000 --- a/certs/client-key.pem +++ /dev/null @@ -1,7 +0,0 @@ ------BEGIN ENCRYPTED PRIVATE KEY----- -MIHsMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAhl8MKolNu/vwICCAAw -DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEM3OtU3hDdLKWhc1fb9U67YEgZCB -uWIiqPe5VAZPJvz4d5N9aBhnog77forXzumfgiqPZ+f2KH34oj4g5ZiXqyJigKNu -7wDb1yPh9I1l/zTWhaIFam7xfa7oy3GUk7P7kRwIpkvcPuwnbv82r/Nrhqo0Hefy -yTHgwKdHM5hV0usbJ1bMYPl8Y1QF86XRz2FPglGNqaC6egT9VDi/A329nM6cfQQ= ------END ENCRYPTED PRIVATE KEY----- diff --git a/certs/gen-certs.sh b/certs/gen-certs.sh index b918d29..c0811fb 100755 --- a/certs/gen-certs.sh +++ b/certs/gen-certs.sh @@ -1,7 +1,36 @@ #!/bin/bash +set -euo pipefail +umask 077 + echo "Run from wolfkeymgr root" +reject_shipped_key() { + key_file="$1" + shipped_fingerprint="$2" + + if [ -f "$key_file" ]; then + if ! key_fingerprint=$(openssl pkey -in "$key_file" \ + -passin pass:'wolfssl' -pubout -outform DER 2>/dev/null | \ + openssl dgst -sha256 -r | awk '{print $1}'); then + echo "Refusing an existing key that could not be fingerprinted:" \ + "$key_file" >&2 + exit 1 + fi + if [ -z "$key_fingerprint" ]; then + echo "Refusing an existing key with an empty fingerprint:" \ + "$key_file" >&2 + exit 1 + fi + if [ "$key_fingerprint" = "$shipped_fingerprint" ]; then + echo "Refusing to reuse a credential shipped in an older release:" \ + "$key_file" >&2 + echo "Rotate the demo credentials with: ./certs/gen-certs.sh clean" >&2 + exit 1 + fi + fi +} + # Make sure required CA files exist and are populated rm -f ./certs/index.* touch ./certs/index.txt @@ -12,18 +41,31 @@ if [ ! -f ./certs/crlnumber ]; then echo 2000 > ./certs/crlnumber fi -if [ "$1" == "clean" ]; then +if [ "${1:-}" == "clean" ]; then rm -f ./certs/1*.pem rm -f ./certs/ca-*.pem rm -f ./certs/client-*.pem rm -f ./certs/client-*.der rm -f ./certs/server-*.pem rm -f ./certs/server-*.der + rm -f ./certs/demo-password.txt rm -f ./certs/*.old exit 0 fi +reject_shipped_key ./certs/ca-key.pem \ + e69c343c84239413663520f8ccf82635b1b8954b9e7a793f2ed3b2b5fd774e5c +reject_shipped_key ./certs/client-key.pem \ + 9ccdf5657ad96c0c14657937e012486ee0c8db8f735b1abcd28c875dd71c5d2e +reject_shipped_key ./certs/server-key.pem \ + 27dea57285ac6798c1c47ad6da9501da76073d54524760450bc5a6001d521d38 +reject_shipped_key ./certs/server-rsa-key.pem \ + 637b068c61c194f740b93dc0828de708f5f8efb87e7ee8e7822f6b158c5a7744 + +printf '%s\n' 'wolfssl' > ./certs/demo-password.txt +chmod 600 ./certs/demo-password.txt + # Script to generated a TLS server and client certificates # Keys use ECC and PKCS8 with password "wolfssl" @@ -31,7 +73,9 @@ fi # Generate ECC 256-bit CA if [ ! -f ./certs/ca-key.pem ]; then echo "Creating CA Key (SECP256R1)" - openssl ecparam -name prime256v1 -genkey -noout | openssl pkcs8 -topk8 -v2 aes-128-cbc -outform pem -out ./certs/ca-key.pem + openssl ecparam -name prime256v1 -genkey -noout | openssl pkcs8 \ + -topk8 -v2 aes-128-cbc -passout pass:'wolfssl' -outform pem \ + -out ./certs/ca-key.pem fi echo "Creating self signed root CA certificate" openssl req -config ./certs/ca-ecc.cnf -extensions v3_ca -x509 -nodes -key ./certs/ca-key.pem -passin pass:'wolfssl' \ @@ -41,7 +85,9 @@ openssl req -config ./certs/ca-ecc.cnf -extensions v3_ca -x509 -nodes -key ./cer # Client Key if [ ! -f ./certs/client-key.pem ]; then echo "Creating Client Key (SECP256R1)" - openssl ecparam -name prime256v1 -genkey -noout | openssl pkcs8 -topk8 -v2 aes-128-cbc -outform pem -out ./certs/client-key.pem + openssl ecparam -name prime256v1 -genkey -noout | openssl pkcs8 \ + -topk8 -v2 aes-128-cbc -passout pass:'wolfssl' -outform pem \ + -out ./certs/client-key.pem fi # Client Cert @@ -56,7 +102,9 @@ rm ./certs/client-cert.csr # Server Key if [ ! -f ./certs/server-key.pem ]; then echo "Creating Server Key (SECP256R1)" - openssl ecparam -name prime256v1 -genkey -noout | openssl pkcs8 -topk8 -v2 aes-128-cbc -outform pem -out ./certs/server-key.pem + openssl ecparam -name prime256v1 -genkey -noout | openssl pkcs8 \ + -topk8 -v2 aes-128-cbc -passout pass:'wolfssl' -outform pem \ + -out ./certs/server-key.pem fi # Server Cert diff --git a/certs/include.am b/certs/include.am index 351e76f..c879623 100644 --- a/certs/include.am +++ b/certs/include.am @@ -4,15 +4,6 @@ EXTRA_DIST += certs/gen-certs.sh -EXTRA_DIST += certs/ca-key.pem -EXTRA_DIST += certs/ca-cert.pem -EXTRA_DIST += certs/client-key.pem -EXTRA_DIST += certs/client-cert.pem -EXTRA_DIST += certs/server-key.pem -EXTRA_DIST += certs/server-cert.pem +EXTRA_DIST += certs/ca-ecc.cnf EXTRA_DIST += certs/test-cert.pem EXTRA_DIST += certs/test-key.pem - -# RSA -EXTRA_DIST += certs/server-rsa-key.pem -EXTRA_DIST += certs/server-rsa-cert.pem diff --git a/certs/server-cert.pem b/certs/server-cert.pem deleted file mode 100644 index 39dc14a..0000000 --- a/certs/server-cert.pem +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDbDCCAxGgAwIBAgICEAQwCgYIKoZIzj0EAwIwgZcxCzAJBgNVBAYTAlVTMRMw -EQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMRAwDgYDVQQKDAd3 -b2xmU1NMMRQwEgYDVQQLDAtEZXZlbG9wbWVudDEYMBYGA1UEAwwPd3d3LndvbGZz -c2wuY29tMR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdvbGZzc2wuY29tMB4XDTIxMDIy -NDAxMDUyN1oXDTMxMDIyMjAxMDUyN1owgY8xCzAJBgNVBAYTAlVTMRMwEQYDVQQI -DApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMRAwDgYDVQQKDAdFbGlwdGlj -MQwwCgYDVQQLDANFQ0MxGDAWBgNVBAMMD3d3dy53b2xmc3NsLmNvbTEfMB0GCSqG -SIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEH -A0IABJJXxA6+BjbQ+xDagzOizUqvBeBGT+4Jv+7hO7io2FSqkv5VYuMmS9kmE7oY -HUgMZGVCEdhpEl2eFiSihQz6HiOjggFRMIIBTTAJBgNVHRMEAjAAMBEGCWCGSAGG -+EIBAQQEAwIGQDAdBgNVHQ4EFgQUIfibLbR/ybFs6ooVzwNT0xTRuAMwDwYDVR0R -BAgwBocEfwAAATCB1wYDVR0jBIHPMIHMgBRndHmex/kdpUJe+oCxJujnjAyWY6GB -naSBmjCBlzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNV -BAcMB1NlYXR0bGUxEDAOBgNVBAoMB3dvbGZTU0wxFDASBgNVBAsMC0RldmVsb3Bt -ZW50MRgwFgYDVQQDDA93d3cud29sZnNzbC5jb20xHzAdBgkqhkiG9w0BCQEWEGlu -Zm9Ad29sZnNzbC5jb22CFGczRc3094HsOjjLRVhjeSgrvetrMA4GA1UdDwEB/wQE -AwIDqDATBgNVHSUEDDAKBggrBgEFBQcDATAKBggqhkjOPQQDAgNJADBGAiEA7kp2 -DyEQRe4W2psvfMFQVPZ82YT1EPbkp1d2kaajf2YCIQCH7tuvUQvol5vj9FiJET9A -gueqWHeJrkCR7gIBTe9xZg== ------END CERTIFICATE----- diff --git a/certs/server-key.pem b/certs/server-key.pem deleted file mode 100644 index fe479e9..0000000 --- a/certs/server-key.pem +++ /dev/null @@ -1,7 +0,0 @@ ------BEGIN ENCRYPTED PRIVATE KEY----- -MIHsMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAigTM4xxXajfAICCAAw -DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEKzLUxrQHvW6QF/pvVhVAvoEgZBT -A79IduRJNEsKj/6qR2XizYQ+9MazK4txO6dBxctI71yeAJ1CwLnmyjkpCuljdNwa -u4ei485u94LJuGKoC+gnyu4cI2kqIU890FjtGIpKH8M/D+2TkRSJOTcchrYdgGy/ -IHVlXFUiAuQwK2FLBZOJ5gEhgCN1XumOJdELbZ5UdQhBL3aiQTW0v6VqNvFsisc= ------END ENCRYPTED PRIVATE KEY----- diff --git a/certs/server-rsa-cert.pem b/certs/server-rsa-cert.pem deleted file mode 100644 index 2e4414a..0000000 --- a/certs/server-rsa-cert.pem +++ /dev/null @@ -1,25 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIENjCCA9ygAwIBAgICEAAwCgYIKoZIzj0EAwIwgZcxCzAJBgNVBAYTAlVTMRMw -EQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMRAwDgYDVQQKDAd3 -b2xmU1NMMRQwEgYDVQQLDAtEZXZlbG9wbWVudDEYMBYGA1UEAwwPd3d3LndvbGZz -c2wuY29tMR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdvbGZzc2wuY29tMB4XDTIxMDcy -MjE4NDcwOFoXDTMxMDcyMDE4NDcwOFowgY8xCzAJBgNVBAYTAlVTMRMwEQYDVQQI -DApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMRAwDgYDVQQKDAdFbGlwdGlj -MQwwCgYDVQQLDANSU0ExGDAWBgNVBAMMD3d3dy53b2xmc3NsLmNvbTEfMB0GCSqG -SIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBAJeWvOKDV2JjoWgSeuHJjvHEBxhRjxR8IOHJoO+YGhThGX2Ib8pa -FEPVFXa2LyRcfd4KzOw+oIXrMrUzYKmU7hedN8WpyfuurCLO560moiQNil9IdQFe -7fVlCmxpTvBwlHzJ4lx+E697mnozt+eM25qJE2h7XCZmN+jb1m+lyxpMAjolP7IU -olnH57pMNkccM6EaToXWA8auk3IQulBfToBdavKB5jEAcI3D+HtHF30klEcqcbA4 -FwkSzP6CdHsDZU9TtDVuyQjbhgTJgiRIO2MigYIOimLZJ83+A9cI8GqMRN8MrC0D -B4q7Vcu4C/M7Dhs/SM+H7MXo01t71nk22asCAwEAAaOCAVEwggFNMAkGA1UdEwQC -MAAwEQYJYIZIAYb4QgEBBAQDAgZAMB0GA1UdDgQWBBRjfckuJfv9Ztl0epw/DE/a -6ldZMjAPBgNVHREECDAGhwR/AAABMIHXBgNVHSMEgc8wgcyAFGd0eZ7H+R2lQl76 -gLEm6OeMDJZjoYGdpIGaMIGXMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGlu -Z3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEQMA4GA1UECgwHd29sZlNTTDEUMBIGA1UE -CwwLRGV2ZWxvcG1lbnQxGDAWBgNVBAMMD3d3dy53b2xmc3NsLmNvbTEfMB0GCSqG -SIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbYIUZzNFzfT3gew6OMtFWGN5KCu962sw -DgYDVR0PAQH/BAQDAgOoMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAoGCCqGSM49BAMC -A0gAMEUCIQC1rKX9GrvNhaWkqFkf8wYIF4TN8KPtUiGIWLBfiwzzlQIgdOd0O11g -61w+iJz3sVFhGKcpAyOqU42K+ShqlJhh0RE= ------END CERTIFICATE----- diff --git a/certs/server-rsa-key.pem b/certs/server-rsa-key.pem deleted file mode 100644 index 41875c1..0000000 --- a/certs/server-rsa-key.pem +++ /dev/null @@ -1,30 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: AES-128-CBC,68083920376B41E11FEEBBE255D2D307 - -i803oPiuFmT19NoY4LfI9bx0vwAaApoxELygMMsPxE/VphubgviUC3YYtb5ZowCd -i7ZlBN/Xg0sWYXm71hVdd+aRIBYuGg6+wC3Q80ISXwxNhEgnwJlNBPvp04L+RuTH -z8BgoC6EmGMQbjsI3dtKGiotApvT/rPsNmGRtQJ6QnnWtxweisKxTVk5mSW3DCnY -5cEw1OIbZ/PpHv014+KDY+MLHUi+VjFte3rYv0TMUXIe9jpLoC8lvug18V2W7a4E -RuUDDeWh2E2Nhqfqr8yhc/uaThwDYhXpvILFK6WW0tW8lN6SzSO8hFNFoqSZj6hR -pe9xos2NW0mV5VGuTtelQ6dXaFF4JjRh9OEpFsUEgG3olXECSjLx504ABO4rxP/c -ScH5CwzpuVFJHTBwSYDi/LDAxUDT8tBr2ZAcV8xvh8uFor5QRsTuI+GbtvnCc2v9 -NtSQ0WB2ew9GAwf21p0aXy1fQm8QtzN4qgoPmVdr3Qp7N50a7oVOge7N/hgYQvlV -fMJdGm//6FXNu9FcHsaAFkj5jpXEcK6CwrI7VETAGSIir4y0xnpbVocj+kNv8N4v -zQDQ1XOUAdRDOCnyrjXcU2ti/DGSrlKUb1b+tBUSJCLNXGEGHgHJKQuSQOk9C5l5 -SOrgRCazW0BFmRduCCPfssBWOBjQNTdOOFxgswzZmaQuY9mff1dndFV9OPgrPHDm -ONVPHf0dHD72FtMJbEuqeat5UnyaxwNZxXxnLSBbjdnPgFfjCZMQbik5nuzg4rZE -zqZsHPEZ7csoX+JfD7LXaDPu7nSfBpWYOOqOul6dOPM3tIW+xoqz+53ci7GATbBQ -PWBvpt2uaFO+Q2vz4/6QvocT49Sgv7Ic75DfJWkZ8mfsRGdHMei2DdWpsDXhKOyY -Lo8JlrBllCWb40Tow6PqcbgkN3HKmavJv+sXP23GW5mPAJPUiiNeH/BOXbLIqcF9 -AEHjwti6UA4Haze6227rfG5cDNRF8Rw45EIIigkDB9a1o4VcWvT6UlXXokRSxNdD -QC8NVQjJZOQdbgqH80IiPTY3if4y62Im66DcHi5lohl1slnscqxUd0SlxNTxEGQC -ieIK1kYs+r7cw7tabKrvUdDl98lQZFxcvdoTkTG3s+e4h4+QSw5JnZcD4eDVxHaH -P17aNOJ9T4RflwWPn5v5loa1Tp8o+EI+LLINQ+cUzlUW7sGt0GmCFnOLP/G2aPOt -BzYbm4dzVoGpfamsVYSMoORcudvflOYSYcyKgCegThwE+P5KLozgPdbAN2Qdm+qe -nOGpVrsOnCJdKkdyZki7orGqK1hslAqGJGt9TKqEhL6pdDvQZf6LaM/GZzgO2SB3 -lBvr3zSlYRbeadgYghzcbMnZAy0lSDuObB5U5wzPLqaq+1N2e6EhxWelmt1Bz4/q -QlUamvUvogX7rPOMnjnt7W90kD5uqLS6IqBI0NKL62Ho9QyhyZ46ooZ4/ChFRO2G -6hQ8hNDGF+UTQ8A0IIrzXnEuerhAy93Abv70Q5mDULQDbWdFxDOgfgqXwiX+EL/V -wszqU4MN2wtDBuZQnCVzBXGbzd+yR0w1qYKSPip+6d/g8N1g8ybRhiD83CVcODqI ------END RSA PRIVATE KEY----- diff --git a/src/keymanager.c b/src/keymanager.c index c612b4f..8d238f1 100644 --- a/src/keymanager.c +++ b/src/keymanager.c @@ -23,6 +23,73 @@ #include /* getopt */ #include /* SIGPIPE */ +#include +#include +#include + +#define WOLFKM_PASSWORD_MAX 255 + +static int LoadPasswordFile(const char* fileName, char* password, + size_t passwordSz) +{ + int ret = 0; + int fd; + size_t length = 0; + ssize_t readSz; + struct stat fileStat; + char input[WOLFKM_PASSWORD_MAX + 3]; + + if (fileName == NULL || password == NULL || passwordSz < 2) + return WOLFKM_BAD_ARGS; + + fd = open(fileName, O_RDONLY | O_NONBLOCK); + if (fd < 0) + return WOLFKM_BAD_FILE; + + if (fstat(fd, &fileStat) != 0 || !S_ISREG(fileStat.st_mode) || + (fileStat.st_mode & (S_IRWXG | S_IRWXO)) != 0) { + ret = WOLFKM_BAD_FILE; + } + while (ret == 0 && length < sizeof(input)) { + readSz = read(fd, input + length, sizeof(input) - length); + if (readSz > 0) { + length += (size_t)readSz; + } + else if (readSz == 0) { + break; + } + else if (errno != EINTR) { + ret = WOLFKM_BAD_FILE; + } + } + if (ret == 0 && length == sizeof(input)) + ret = WOLFKM_BAD_ARGS; + if (ret == 0 && length > 0 && input[length - 1] == '\n') { + length--; + if (length > 0 && input[length - 1] == '\r') + length--; + } + else if (ret == 0 && length > 0 && input[length - 1] == '\r') { + length--; + } + if (ret == 0) { + if (length == 0 || length >= passwordSz || + memchr(input, '\0', length) != NULL) { + ret = WOLFKM_BAD_ARGS; + } + else { + memcpy(password, input, length); + password[length] = '\0'; + } + } + + close(fd); + wolfKeyMgr_ForceZero(input, sizeof(input)); + if (ret != 0) + wolfKeyMgr_ForceZero(password, (word32)passwordSz); + + return ret; +} /* usage help */ static void Usage(void) @@ -42,10 +109,10 @@ static void Usage(void) printf("-u Key renewal max use count, default %d\n", WOLFKM_KEY_RENEW_MAX_USES); printf("-t Thread pool size, default %ld\n", sysconf(_SC_NPROCESSORS_CONF)); - printf("-k TLS Server TLS Key, default %s\n", WOLFKM_ETSSVC_KEY); - printf("-w TLS Server Key Password, default %s\n", WOLFKM_ETSSVC_KEY_PASSWORD); - printf("-c TLS Server Certificate, default %s\n", WOLFKM_ETSSVC_CERT); - printf("-A TLS CA Certificate, default %s\n", WOLFKM_ETSSVC_CA); + printf("-k TLS Server Key (required)\n"); + printf("-W TLS Server Key Password File (required)\n"); + printf("-c TLS Server Certificate (required)\n"); + printf("-A TLS CA Certificate (required)\n"); printf("-K Key Type: SECP256R1, FFDHE_2048, X25519 or X448 (default %s)\n", wolfEtsKeyGetTypeStr(WOLFKM_ETSSVC_DEF_KEY_TYPE)); printf("-v Vault file for key storage, default %s\n", WOLFKM_ETSSVC_VAULT); @@ -76,26 +143,29 @@ int main(int argc, char** argv) FILE* pidF = 0; SvcInfo* etsSvc = NULL; word32 timeoutSec = WOLFKM_DEFAULT_TIMEOUT; - const char* serverKey = WOLFKM_ETSSVC_KEY; - const char* serverKeyPass = WOLFKM_ETSSVC_KEY_PASSWORD; - const char* serverCert = WOLFKM_ETSSVC_CERT; - const char* caCert = WOLFKM_ETSSVC_CA; + const char* serverKey = NULL; + const char* serverKeyPassFile = NULL; + const char* serverCert = NULL; + const char* caCert = NULL; + char serverKeyPass[WOLFKM_PASSWORD_MAX + 1]; SignalArg sigArgInt, sigArgTerm; const char* vaultFile = WOLFKM_ETSSVC_VAULT; const char* listenPort = WOLFKM_ETSSVC_PORT; EtsSvcConfig config; memset(&config, 0, sizeof(config)); + memset(serverKeyPass, 0, sizeof(serverKeyPass)); config.keyTypeDef = WOLFKM_ETSSVC_DEF_KEY_TYPE; config.renewSec = WOLFKM_KEY_RENEW_TIMEOUT; config.maxUseCount = WOLFKM_KEY_RENEW_MAX_USES; /* argument processing */ - while ((ch = getopt(argc, argv, "?bis:t:o:f:l:k:w:c:A:r:u:K:v:p:P:")) != -1) { + opterr = 0; + while ((ch = getopt(argc, argv, "bis:t:o:f:l:k:W:c:A:r:u:K:v:p:P:")) != -1) { switch (ch) { case '?' : Usage(); - exit(EXIT_SUCCESS); + exit(optopt == '?' ? EXIT_SUCCESS : EX_USAGE); case 'b' : daemon = 1; break; @@ -137,8 +207,8 @@ int main(int argc, char** argv) case 'k': serverKey = optarg; break; - case 'w': - serverKeyPass = optarg; + case 'W': + serverKeyPassFile = optarg; break; case 'c': serverCert = optarg; @@ -190,6 +260,19 @@ int main(int argc, char** argv) } } + if (serverKey == NULL || serverKeyPassFile == NULL || serverCert == NULL || + caCert == NULL) { + fprintf(stderr, "TLS key, password file, certificate and CA are required\n"); + Usage(); + exit(EX_USAGE); + } + ret = LoadPasswordFile(serverKeyPassFile, serverKeyPass, + sizeof(serverKeyPass)); + if (ret != 0) { + fprintf(stderr, "TLS key password file must be non-empty and " + "accessible only by its owner\n"); + exit(EX_USAGE); + } /* Create daemon */ if (daemon) { if (logName == NULL) { @@ -268,6 +351,7 @@ int main(int argc, char** argv) ret = wolfKeyMgr_LoadKeyFile(etsSvc, serverKey, WOLFSSL_FILETYPE_PEM, serverKeyPass); + wolfKeyMgr_ForceZero(serverKeyPass, sizeof(serverKeyPass)); if (ret != 0) { XLOG(WOLFKM_LOG_ERROR, "Error %d loading ETS TLS key\n", ret); goto exit; @@ -314,6 +398,8 @@ int main(int argc, char** argv) wolfKeyMgr_ShowStats(etsSvc); exit: + wolfKeyMgr_ForceZero(serverKeyPass, sizeof(serverKeyPass)); + /* Cleanup pid file */ if (pidF) { fclose(pidF); diff --git a/tests/credential-options.test b/tests/credential-options.test new file mode 100755 index 0000000..4092612 --- /dev/null +++ b/tests/credential-options.test @@ -0,0 +1,123 @@ +#!/bin/sh + +set -eu + +test_dir=$(mktemp -d "${TMPDIR:-/tmp}/wolfkeymgr-credentials.XXXXXX") +trap 'rm -rf "$test_dir"' EXIT HUP INT TERM + +expect_usage() +{ + password_file="$1" + + set +e + ./src/wolfkeymgr -k unused-key.pem -W "$password_file" \ + -c unused-cert.pem -A unused-ca.pem >/dev/null 2>&1 + status=$? + set -e + test "$status" -eq 64 +} + +expect_password_accepted() +{ + password_file="$1" + output_file="$test_dir/accepted-password.log" + + set +e + ./src/wolfkeymgr -k unused-key.pem -W "$password_file" \ + -c unused-cert.pem -A unused-ca.pem \ + -p "$test_dir/accepted-password.pid" >"$output_file" 2>&1 + status=$? + set -e + test "$status" -ne 64 + grep -q 'loading ETS TLS CA cert' "$output_file" +} + +touch "$test_dir/empty-password" +chmod 600 "$test_dir/empty-password" +expect_usage "$test_dir/empty-password" + +printf '%s\n' password > "$test_dir/insecure-password" +chmod 644 "$test_dir/insecure-password" +expect_usage "$test_dir/insecure-password" + +awk 'BEGIN { for (i = 0; i < 256; i++) printf "x" }' \ + > "$test_dir/oversized-password" +chmod 600 "$test_dir/oversized-password" +expect_usage "$test_dir/oversized-password" + +printf 'pass\000word\n' > "$test_dir/nul-password" +chmod 600 "$test_dir/nul-password" +expect_usage "$test_dir/nul-password" + +awk 'BEGIN { for (i = 0; i < 255; i++) printf "x" }' \ + > "$test_dir/max-password" +printf '\r\n' >> "$test_dir/max-password" +chmod 600 "$test_dir/max-password" +expect_password_accepted "$test_dir/max-password" + +mkfifo "$test_dir/password-pipe" +expect_usage "$test_dir/password-pipe" + +set +e +./src/wolfkeymgr -w password >/dev/null 2>&1 +status=$? +set -e +test "$status" -eq 64 + +./src/wolfkeymgr '-?' >/dev/null 2>&1 + +set +e +./src/wolfkeymgr -W >/dev/null 2>&1 +status=$? +set -e +test "$status" -eq 64 + +mkdir -p "$test_dir/generator/certs" "$test_dir/bin" +cp certs/gen-certs.sh certs/ca-ecc.cnf certs/test-key.pem \ + certs/test-cert.pem "$test_dir/generator/certs/" +printf '%s\n' '#!/bin/sh' 'exit 1' > "$test_dir/bin/openssl" +chmod 700 "$test_dir/bin/openssl" +if (cd "$test_dir/generator" && \ + PATH="$test_dir/bin:$PATH" ./certs/gen-certs.sh >/dev/null 2>&1); then + exit 1 +fi + +mkdir -p "$test_dir/malformed/certs" +cp certs/gen-certs.sh certs/ca-ecc.cnf certs/test-key.pem \ + certs/test-cert.pem "$test_dir/malformed/certs/" +printf '%s\n' 'not a private key' > "$test_dir/malformed/certs/ca-key.pem" +if (cd "$test_dir/malformed" && \ + ./certs/gen-certs.sh >/dev/null 2>&1); then + exit 1 +fi + +mkdir -p "$test_dir/matched/certs" "$test_dir/matched-bin" +cp certs/gen-certs.sh certs/ca-ecc.cnf certs/test-key.pem \ + certs/test-cert.pem "$test_dir/matched/certs/" +printf '%s\n' 'controlled test key' > "$test_dir/matched/certs/ca-key.pem" +printf '%s\n' '#!/bin/sh' \ + 'if [ "$1" = pkey ]; then' \ + ' printf "%s\\n" controlled-public-key' \ + 'else' \ + ' printf "%s -\\n" e69c343c84239413663520f8ccf82635b1b8954b9e7a793f2ed3b2b5fd774e5c' \ + 'fi' > "$test_dir/matched-bin/openssl" +chmod 700 "$test_dir/matched-bin/openssl" +if (cd "$test_dir/matched" && PATH="$test_dir/matched-bin:$PATH" \ + ./certs/gen-certs.sh >"$test_dir/matched.log" 2>&1); then + exit 1 +fi +grep -q 'Rotate the demo credentials' "$test_dir/matched.log" + +mkdir -p "$test_dir/valid/certs" +cp certs/gen-certs.sh certs/ca-ecc.cnf certs/test-key.pem \ + certs/test-cert.pem "$test_dir/valid/certs/" +printf '%s\n' stale > "$test_dir/valid/certs/demo-password.txt" +chmod 644 "$test_dir/valid/certs/demo-password.txt" +(cd "$test_dir/valid" && ./certs/gen-certs.sh >/dev/null 2>&1) +test -s "$test_dir/valid/certs/ca-cert.pem" +test -s "$test_dir/valid/certs/client-cert.pem" +test -s "$test_dir/valid/certs/server-rsa-cert.pem" +if find "$test_dir/valid/certs/demo-password.txt" ! -perm 600 -print | \ + grep -q .; then + exit 1 +fi diff --git a/tests/include.am b/tests/include.am index 71a939b..6fe09c5 100644 --- a/tests/include.am +++ b/tests/include.am @@ -8,3 +8,6 @@ tests_unit_test_SOURCES = tests/unit_tests.c tests_unit_test_CFLAGS = $(AM_CFLAGS) tests_unit_test_LDADD = src/libwolfkeymgr.la $(LIB_STATIC_ADD) tests_unit_test_DEPENDENCIES = src/libwolfkeymgr.la + +check_SCRIPTS += tests/credential-options.test +EXTRA_DIST += tests/credential-options.test diff --git a/wolfkeymgr/keymanager.h b/wolfkeymgr/keymanager.h index e1f7689..37b486a 100644 --- a/wolfkeymgr/keymanager.h +++ b/wolfkeymgr/keymanager.h @@ -54,18 +54,6 @@ #ifndef WOLFKM_ETSSVC_PORT #define WOLFKM_ETSSVC_PORT "8119" #endif -#ifndef WOLFKM_ETSSVC_KEY_PASSWORD -#define WOLFKM_ETSSVC_KEY_PASSWORD "wolfssl" -#endif -#ifndef WOLFKM_ETSSVC_CA -#define WOLFKM_ETSSVC_CA "./certs/ca-cert.pem" -#endif -#ifndef WOLFKM_ETSSVC_KEY -#define WOLFKM_ETSSVC_KEY "./certs/server-rsa-key.pem" -#endif -#ifndef WOLFKM_ETSSVC_CERT -#define WOLFKM_ETSSVC_CERT "./certs/server-rsa-cert.pem" -#endif #ifndef WOLFKM_ETSSVC_VAULT #define WOLFKM_ETSSVC_VAULT "./wolfkeymgr.vault" #endif