cmd/ca: prevent out-of-memory on 32bit systems (#1834) (#1852)

(cherry picked from commit 6fcb926334)

Co-authored-by: sn0w <me@sn0w.re>
This commit is contained in:
Wade Simmons
2026-08-20 16:52:50 -04:00
committed by GitHub
co-authored by sn0w
parent 0a916b1d4b
commit c2ad75e459
2 changed files with 42 additions and 7 deletions
+17 -2
View File
@@ -8,6 +8,7 @@ import (
"fmt" "fmt"
"io" "io"
"math" "math"
"math/bits"
"net/netip" "net/netip"
"os" "os"
"strings" "strings"
@@ -44,6 +45,20 @@ type caFlags struct {
} }
func newCaFlags() *caFlags { func newCaFlags() *caFlags {
// prevent running out of memory on 32-bit systems by defaulting to
// RFC9106's recommendation for memory-constrained environments
var (
defaultArgonMemory uint
defaultArgonIterations uint
)
if bits.UintSize == 32 {
defaultArgonMemory = 64 * 1024
defaultArgonIterations = 3
} else {
defaultArgonMemory = 2 * 1024 * 1024
defaultArgonIterations = 1
}
cf := caFlags{set: flag.NewFlagSet("ca", flag.ContinueOnError)} cf := caFlags{set: flag.NewFlagSet("ca", flag.ContinueOnError)}
cf.set.Usage = func() {} cf.set.Usage = func() {}
cf.name = cf.set.String("name", "", "Required: name of the certificate authority") cf.name = cf.set.String("name", "", "Required: name of the certificate authority")
@@ -55,9 +70,9 @@ func newCaFlags() *caFlags {
cf.groups = cf.set.String("groups", "", "Optional: comma separated list of groups. This will limit which groups subordinate certs can use") cf.groups = cf.set.String("groups", "", "Optional: comma separated list of groups. This will limit which groups subordinate certs can use")
cf.networks = cf.set.String("networks", "", "Optional: comma separated list of ip address and network in CIDR notation. This will limit which ip addresses and networks subordinate certs can use in networks") cf.networks = cf.set.String("networks", "", "Optional: comma separated list of ip address and network in CIDR notation. This will limit which ip addresses and networks subordinate certs can use in networks")
cf.unsafeNetworks = cf.set.String("unsafe-networks", "", "Optional: comma separated list of ip address and network in CIDR notation. This will limit which ip addresses and networks subordinate certs can use in unsafe networks") cf.unsafeNetworks = cf.set.String("unsafe-networks", "", "Optional: comma separated list of ip address and network in CIDR notation. This will limit which ip addresses and networks subordinate certs can use in unsafe networks")
cf.argonMemory = cf.set.Uint("argon-memory", 2*1024*1024, "Optional: Argon2 memory parameter (in KiB) used for encrypted private key passphrase") cf.argonMemory = cf.set.Uint("argon-memory", defaultArgonMemory, "Optional: Argon2 memory parameter (in KiB) used for encrypted private key passphrase")
cf.argonParallelism = cf.set.Uint("argon-parallelism", 4, "Optional: Argon2 parallelism parameter used for encrypted private key passphrase") cf.argonParallelism = cf.set.Uint("argon-parallelism", 4, "Optional: Argon2 parallelism parameter used for encrypted private key passphrase")
cf.argonIterations = cf.set.Uint("argon-iterations", 1, "Optional: Argon2 iterations parameter used for encrypted private key passphrase") cf.argonIterations = cf.set.Uint("argon-iterations", defaultArgonIterations, "Optional: Argon2 iterations parameter used for encrypted private key passphrase")
cf.encryption = cf.set.Bool("encrypt", false, "Optional: prompt for passphrase and write out-key in an encrypted format") cf.encryption = cf.set.Bool("encrypt", false, "Optional: prompt for passphrase and write out-key in an encrypted format")
cf.curve = cf.set.String("curve", "25519", "EdDSA/ECDSA Curve (25519, P256)") cf.curve = cf.set.String("curve", "25519", "EdDSA/ECDSA Curve (25519, P256)")
cf.p11url = p11Flag(cf.set) cf.p11url = p11Flag(cf.set)
+25 -5
View File
@@ -7,7 +7,9 @@ import (
"bytes" "bytes"
"encoding/pem" "encoding/pem"
"errors" "errors"
"math/bits"
"os" "os"
"strconv"
"strings" "strings"
"testing" "testing"
"time" "time"
@@ -22,6 +24,18 @@ func Test_caSummary(t *testing.T) {
} }
func Test_caHelp(t *testing.T) { func Test_caHelp(t *testing.T) {
var (
defaultArgonMemory string
defaultArgonIterations string
)
if bits.UintSize == 32 {
defaultArgonMemory = strconv.Itoa(64 * 1024)
defaultArgonIterations = strconv.Itoa(3)
} else {
defaultArgonMemory = strconv.Itoa(2 * 1024 * 1024)
defaultArgonIterations = strconv.Itoa(1)
}
ob := &bytes.Buffer{} ob := &bytes.Buffer{}
caHelp(ob) caHelp(ob)
assert.Equal( assert.Equal(
@@ -29,9 +43,9 @@ func Test_caHelp(t *testing.T) {
"Usage of "+os.Args[0]+" ca <flags>: create a self signed certificate authority\n"+ "Usage of "+os.Args[0]+" ca <flags>: create a self signed certificate authority\n"+
" Pass \"-\" to any path flag to read from stdin or write to stdout.\n"+ " Pass \"-\" to any path flag to read from stdin or write to stdout.\n"+
" -argon-iterations uint\n"+ " -argon-iterations uint\n"+
" \tOptional: Argon2 iterations parameter used for encrypted private key passphrase (default 1)\n"+ " \tOptional: Argon2 iterations parameter used for encrypted private key passphrase (default "+defaultArgonIterations+")\n"+
" -argon-memory uint\n"+ " -argon-memory uint\n"+
" \tOptional: Argon2 memory parameter (in KiB) used for encrypted private key passphrase (default 2097152)\n"+ " \tOptional: Argon2 memory parameter (in KiB) used for encrypted private key passphrase (default "+defaultArgonMemory+")\n"+
" -argon-parallelism uint\n"+ " -argon-parallelism uint\n"+
" \tOptional: Argon2 parallelism parameter used for encrypted private key passphrase (default 4)\n"+ " \tOptional: Argon2 parallelism parameter used for encrypted private key passphrase (default 4)\n"+
" -curve string\n"+ " -curve string\n"+
@@ -188,10 +202,16 @@ func Test_ca(t *testing.T) {
k, _ := pem.Decode(rb) k, _ := pem.Decode(rb)
ned, err := cert.UnmarshalNebulaEncryptedData(k.Bytes) ned, err := cert.UnmarshalNebulaEncryptedData(k.Bytes)
require.NoError(t, err) require.NoError(t, err)
// we won't know salt in advance, so just check start of string
assert.Equal(t, uint32(2*1024*1024), ned.EncryptionMetadata.Argon2Parameters.Memory) if bits.UintSize == 32 {
assert.Equal(t, uint32(64*1024), ned.EncryptionMetadata.Argon2Parameters.Memory)
assert.Equal(t, uint32(3), ned.EncryptionMetadata.Argon2Parameters.Iterations)
} else {
assert.Equal(t, uint32(2*1024*1024), ned.EncryptionMetadata.Argon2Parameters.Memory)
assert.Equal(t, uint32(1), ned.EncryptionMetadata.Argon2Parameters.Iterations)
}
assert.Equal(t, uint8(4), ned.EncryptionMetadata.Argon2Parameters.Parallelism) assert.Equal(t, uint8(4), ned.EncryptionMetadata.Argon2Parameters.Parallelism)
assert.Equal(t, uint32(1), ned.EncryptionMetadata.Argon2Parameters.Iterations)
// verify the key is valid and decrypt-able // verify the key is valid and decrypt-able
var curve cert.Curve var curve cert.Curve