// Copyright (c) 2025-2026 Probo Inc . // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package keys_test import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rsa" "errors" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.probo.inc/probo/pkg/crypto/keys" ) func TestGenerate(t *testing.T) { tests := []struct { name string keyType keys.Type checkFunc func(t *testing.T, key any) expectError bool }{ { name: "EC256", keyType: keys.TypeEC256, checkFunc: func(t *testing.T, key any) { ecKey, ok := key.(*ecdsa.PrivateKey) require.True(t, ok, "expected *ecdsa.PrivateKey, got %T", key) assert.Equal(t, elliptic.P256(), ecKey.Curve, "expected P256 curve") }, }, { name: "EC384", keyType: keys.TypeEC384, checkFunc: func(t *testing.T, key any) { ecKey, ok := key.(*ecdsa.PrivateKey) require.True(t, ok, "expected *ecdsa.PrivateKey, got %T", key) assert.Equal(t, elliptic.P384(), ecKey.Curve, "expected P384 curve") }, }, { name: "RSA2048", keyType: keys.TypeRSA2048, checkFunc: func(t *testing.T, key any) { rsaKey, ok := key.(*rsa.PrivateKey) require.True(t, ok, "expected *rsa.PrivateKey, got %T", key) bitSize := rsaKey.N.BitLen() assert.GreaterOrEqual(t, bitSize, 2047, "RSA key too small") assert.LessOrEqual(t, bitSize, 2048, "RSA key too large") }, }, { name: "RSA4096", keyType: keys.TypeRSA4096, checkFunc: func(t *testing.T, key any) { rsaKey, ok := key.(*rsa.PrivateKey) require.True(t, ok, "expected *rsa.PrivateKey, got %T", key) bitSize := rsaKey.N.BitLen() assert.GreaterOrEqual(t, bitSize, 4095, "RSA key too small") assert.LessOrEqual(t, bitSize, 4096, "RSA key too large") }, }, { name: "Invalid key type", keyType: keys.Type("INVALID"), expectError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { key, err := keys.Generate(tt.keyType) if tt.expectError { assert.Error(t, err, "expected error") return } require.NoError(t, err, "unexpected error") require.NotNil(t, key, "expected key, got nil") // Verify the key can be used for signing (crypto.Signer has Public() method) assert.NotNil(t, key.Public(), "key.Public() returned nil") if tt.checkFunc != nil { tt.checkFunc(t, key) } }) } } func TestGenerateConcurrency(t *testing.T) { // Test that key generation is safe for concurrent use keyTypes := []keys.Type{ keys.TypeEC256, keys.TypeEC384, keys.TypeRSA2048, keys.TypeRSA4096, } for _, keyType := range keyTypes { t.Run(string(keyType), func(t *testing.T) { t.Parallel() const numGoroutines = 10 errorsChan := make(chan error, numGoroutines) for range numGoroutines { go func() { key, err := keys.Generate(keyType) if err != nil { errorsChan <- err return } if key == nil { errorsChan <- errors.New("generated key is nil") return } errorsChan <- nil }() } for range numGoroutines { err := <-errorsChan assert.NoError(t, err, "concurrent generation failed") } }) } } func BenchmarkGenerate(b *testing.B) { benchmarks := []struct { name string keyType keys.Type }{ {"EC256", keys.TypeEC256}, {"EC384", keys.TypeEC384}, {"RSA2048", keys.TypeRSA2048}, {"RSA4096", keys.TypeRSA4096}, } for _, bm := range benchmarks { b.Run(bm.name, func(b *testing.B) { for i := 0; i < b.N; i++ { _, err := keys.Generate(bm.keyType) if err != nil { b.Fatal(err) } } }) } }