diff --git a/lib/ocrypto/aes_gcm.go b/lib/ocrypto/aes_gcm.go index 77aaf647ea..765d3f05be 100644 --- a/lib/ocrypto/aes_gcm.go +++ b/lib/ocrypto/aes_gcm.go @@ -80,6 +80,9 @@ func (aesGcm AesGcm) EncryptWithIVAndTagSize(iv, data []byte, authTagSize int) ( // NOTE: This method use nonce of 12 bytes and auth tag as aes block size(16 bytes) // also expects IV as preamble of data. func (aesGcm AesGcm) Decrypt(data []byte) ([]byte, error) { // extract nonce and cipherText + if len(data) < GcmStandardNonceSize+aes.BlockSize { + return nil, errors.New("ciphertext too short") + } nonce, cipherText := data[:GcmStandardNonceSize], data[GcmStandardNonceSize:] gcm, err := cipher.NewGCMWithNonceSize(aesGcm.block, GcmStandardNonceSize) diff --git a/lib/ocrypto/asym_encryption.go b/lib/ocrypto/asym_encryption.go index 2d180b3de9..b0136ddb45 100644 --- a/lib/ocrypto/asym_encryption.go +++ b/lib/ocrypto/asym_encryption.go @@ -14,6 +14,7 @@ import ( "errors" "fmt" "io" + "strconv" "strings" "golang.org/x/crypto/hkdf" @@ -36,6 +37,9 @@ type PublicKeyEncryptor interface { // Type required to use the scheme for encryption - notably, if it procduces extra metadata. Type() SchemeType + // KeyType returns the key type, e.g. RSA or EC. + KeyType() KeyType + // For EC schemes, this method returns the public part of the ephemeral key. // Otherwise, it returns nil. EphemeralKey() []byte @@ -139,10 +143,38 @@ func (e AsymEncryption) Type() SchemeType { return RSA } +func (e AsymEncryption) KeyType() KeyType { + switch e.PublicKey.Size() { + case RSA2048Size / 8: //nolint:mnd // standard key size in bytes + return RSA2048Key + case RSA4096Size / 8: //nolint:mnd // large key size in bytes + return RSA4096Key + default: + bitlen := e.PublicKey.Size() * 8 //nolint:mnd // convert to bits + return KeyType("rsa:" + strconv.Itoa(bitlen)) + } +} + func (e ECEncryptor) Type() SchemeType { return EC } +func (e ECEncryptor) KeyType() KeyType { + switch e.pub.Curve() { + case ecdh.P256(): + return EC256Key + case ecdh.P384(): + return EC384Key + case ecdh.P521(): + return EC521Key + default: + if n, ok := e.pub.Curve().(fmt.Stringer); ok { + return KeyType("ec:" + n.String()) + } + return KeyType("ec:[unknown]") + } +} + func (e AsymEncryption) EphemeralKey() []byte { return nil } diff --git a/lib/ocrypto/asym_encryption_test.go b/lib/ocrypto/asym_encryption_test.go new file mode 100644 index 0000000000..d22cadeb8d --- /dev/null +++ b/lib/ocrypto/asym_encryption_test.go @@ -0,0 +1,137 @@ +package ocrypto + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestFromPublicPEM(t *testing.T) { + testCases := []struct { + name string + filename string + expectedType KeyType + }{ + { + name: "EC secp256r1 public key", + filename: "sample-ec-secp256r1-01-public.pem", + expectedType: EC256Key, + }, + { + name: "EC secp384r1 public key", + filename: "sample-ec-secp384r1-01-public.pem", + expectedType: EC384Key, + }, + { + name: "EC secp521r1 public key", + filename: "sample-ec-secp521r1-01-public.pem", + expectedType: EC521Key, + }, + { + name: "RSA 2048 public key", + filename: "sample-rsa-2048-01-public.pem", + expectedType: RSA2048Key, + }, + { + name: "RSA 4096 public key", + filename: "sample-rsa-4096-01-public.pem", + expectedType: RSA4096Key, + }, + { + name: "Unsupported RSA 1024 public key", + filename: "sample-rsa-1024-01-public.pem", + expectedType: KeyType("rsa:1024"), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Read the PEM file + testDataPath := filepath.Join("testdata", tc.filename) + pemData, err := os.ReadFile(testDataPath) + require.NoError(t, err, "Failed to read test file %s", tc.filename) + + // Load the public key using FromPublicPEM + encryptor, err := FromPublicPEM(string(pemData)) + require.NoError(t, err, "Failed to load public key from %s", tc.filename) + require.NotNil(t, encryptor, "Encryptor should not be nil") // Test that KeyType() returns the expected type + keyType := encryptor.KeyType() + assert.Equal(t, tc.expectedType, keyType, "KeyType() returned unexpected value for %s", tc.name) + + // Also test that we can get the public key back in PEM format + pubKeyPEM, err := encryptor.PublicKeyInPemFormat() + require.NoError(t, err, "Failed to get public key in PEM format") + assert.NotEmpty(t, pubKeyPEM, "Public key PEM should not be empty") + }) + } +} + +func TestFromPublicPEM_UnsupportedFiles(t *testing.T) { + testCases := []struct { + name string + filename string + }{ + { + name: "Unsupported EC secp256k1 public key", + filename: "sample-ec-secp256k1-01-public.pem", + }, + { + name: "Unsupported EC brainpoolP160r1 public key", + filename: "sample-ec-brainpoolP160r1-01-public.pem", + }, + { + name: "Loading a private key should fail", + filename: "sample-ec-secp256r1-01-private.pem", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Read the PEM file + testDataPath := filepath.Join("testdata", tc.filename) + pemData, err := os.ReadFile(testDataPath) + require.NoError(t, err, "Failed to read test file %s", tc.filename) + + // Load the public key using FromPublicPEM - should fail for unsupported curves + _, err = FromPublicPEM(string(pemData)) + assert.Error(t, err, "Expected error for unsupported curve %s", tc.name) + }) + } +} + +func TestFromPublicPEM_InvalidInput(t *testing.T) { + testCases := []struct { + name string + pemData string + errorMsg string + }{ + { + name: "Empty string", + pemData: "", + errorMsg: "failed to parse PEM formatted public key", + }, + { + name: "Invalid PEM format", + pemData: "not a pem file", + errorMsg: "failed to parse PEM formatted public key", + }, + { + name: "Invalid PEM content", + pemData: `-----BEGIN PUBLIC KEY----- +invalid base64 content!!! +-----END PUBLIC KEY-----`, + errorMsg: "failed to parse PEM formatted public key", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + _, err := FromPublicPEM(tc.pemData) + require.Error(t, err) + assert.Contains(t, err.Error(), tc.errorMsg) + }) + } +} diff --git a/lib/ocrypto/testdata/genkeys.sh b/lib/ocrypto/testdata/genkeys.sh new file mode 100755 index 0000000000..4199e8ac10 --- /dev/null +++ b/lib/ocrypto/testdata/genkeys.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# genkeys.sh +# Generate test keys for ocrypto unit tests + +set -e + +# Ensure we're in the correct directory +cd "$(dirname "$0")" + +echo "Generating test keys..." + +# Which EC curves we are using to generate keys +ec_curves=( + "secp256r1" + "secp384r1" + "secp521r1" + "secp256k1" + "brainpoolP160r1" +) + +# Generate EC keys +for curve_name in "${ec_curves[@]}"; do + echo "Generating EC $curve_name keys..." + openssl ecparam -name "$curve_name" -genkey -noout -out "sample-ec-$curve_name-01-private.pem" + openssl ec -in "sample-ec-$curve_name-01-private.pem" -pubout -out "sample-ec-$curve_name-01-public.pem" +done + +# What RSA bit lengths we want to test +rsa_bits=(2048 4096 1024) + +# Generate RSA keys +for bits in "${rsa_bits[@]}"; do + echo "Generating RSA $bits keys..." + openssl genpkey -algorithm RSA -out "sample-rsa-$bits-01-private.pem" -pkeyopt "rsa_keygen_bits:$bits" + openssl rsa -in "sample-rsa-$bits-01-private.pem" -pubout -out "sample-rsa-$bits-01-public.pem" +done + +echo "Test key generation complete!" +echo "Generated keys:" +ls -la sample-*.pem diff --git a/lib/ocrypto/testdata/sample-ec-brainpoolP160r1-01-private.pem b/lib/ocrypto/testdata/sample-ec-brainpoolP160r1-01-private.pem new file mode 100644 index 0000000000..b555c20355 --- /dev/null +++ b/lib/ocrypto/testdata/sample-ec-brainpoolP160r1-01-private.pem @@ -0,0 +1,4 @@ +-----BEGIN EC PRIVATE KEY----- +MFQCAQEEFHp8z00th6pcyzd45TD4mVz3iDRCoAsGCSskAwMCCAEBAaEsAyoABCSG +cu/mTV74q8bJUDDgA3gJ8nWojRgEt5WwkT9j0viJT2OLMWlf+7o= +-----END EC PRIVATE KEY----- diff --git a/lib/ocrypto/testdata/sample-ec-brainpoolP160r1-01-public.pem b/lib/ocrypto/testdata/sample-ec-brainpoolP160r1-01-public.pem new file mode 100644 index 0000000000..ddf160b9ad --- /dev/null +++ b/lib/ocrypto/testdata/sample-ec-brainpoolP160r1-01-public.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MEIwFAYHKoZIzj0CAQYJKyQDAwIIAQEBAyoABCSGcu/mTV74q8bJUDDgA3gJ8nWo +jRgEt5WwkT9j0viJT2OLMWlf+7o= +-----END PUBLIC KEY----- diff --git a/lib/ocrypto/testdata/sample-ec-secp256k1-01-private.pem b/lib/ocrypto/testdata/sample-ec-secp256k1-01-private.pem new file mode 100644 index 0000000000..340d0b30d0 --- /dev/null +++ b/lib/ocrypto/testdata/sample-ec-secp256k1-01-private.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHQCAQEEIDmKI51/sEYpwoPhQSOzcIGI+e0VE7Kux9bCfC8p0qJroAcGBSuBBAAK +oUQDQgAEadV+Y82QMEpZyzFtdu5K5LA7eO+3Yu0Ms/4Gic7O0IEi6S1SZOCzALJb +e6mC5IcF1nm/dDC7xCQVgMHughWVPw== +-----END EC PRIVATE KEY----- diff --git a/lib/ocrypto/testdata/sample-ec-secp256k1-01-public.pem b/lib/ocrypto/testdata/sample-ec-secp256k1-01-public.pem new file mode 100644 index 0000000000..1e92e806ac --- /dev/null +++ b/lib/ocrypto/testdata/sample-ec-secp256k1-01-public.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEadV+Y82QMEpZyzFtdu5K5LA7eO+3Yu0M +s/4Gic7O0IEi6S1SZOCzALJbe6mC5IcF1nm/dDC7xCQVgMHughWVPw== +-----END PUBLIC KEY----- diff --git a/lib/ocrypto/testdata/sample-ec-secp256r1-01-private.pem b/lib/ocrypto/testdata/sample-ec-secp256r1-01-private.pem new file mode 100644 index 0000000000..bc8cffee71 --- /dev/null +++ b/lib/ocrypto/testdata/sample-ec-secp256r1-01-private.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIP1yOQQkE1gy3ktmG3dsk/M3AcXYTf7DwRhFmSv8XJ4LoAoGCCqGSM49 +AwEHoUQDQgAENM4HHVFcRnCBycNVgYo1ArhtVz65ddbkQCCdIwsi8NFSHza6tZyM +s3LzQ1n46CnnXsgg/V26pYouAKOUcuRaOA== +-----END EC PRIVATE KEY----- diff --git a/lib/ocrypto/testdata/sample-ec-secp256r1-01-public.pem b/lib/ocrypto/testdata/sample-ec-secp256r1-01-public.pem new file mode 100644 index 0000000000..7faf4eaa53 --- /dev/null +++ b/lib/ocrypto/testdata/sample-ec-secp256r1-01-public.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENM4HHVFcRnCBycNVgYo1ArhtVz65 +ddbkQCCdIwsi8NFSHza6tZyMs3LzQ1n46CnnXsgg/V26pYouAKOUcuRaOA== +-----END PUBLIC KEY----- diff --git a/lib/ocrypto/testdata/sample-ec-secp384r1-01-private.pem b/lib/ocrypto/testdata/sample-ec-secp384r1-01-private.pem new file mode 100644 index 0000000000..6a93e5ad8c --- /dev/null +++ b/lib/ocrypto/testdata/sample-ec-secp384r1-01-private.pem @@ -0,0 +1,6 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDAJxnVCC6ZJpuIDKz4S2sBV1TWMC8j4slIDKUWd+zueNPRxAINfACpP +r10ZCFofJxqgBwYFK4EEACKhZANiAATXFxgvsn7o3/kwlWTTP9Ue44ddtX173FwB +7q8WCs5THpnhHwG4uepfQWlyI5owHwIpO7aqGaJ0zyswikTH8E6wOv2W2iLYMAgW +L2S+8GNimGcGLwwdg9kTggUxf+BPOFI= +-----END EC PRIVATE KEY----- diff --git a/lib/ocrypto/testdata/sample-ec-secp384r1-01-public.pem b/lib/ocrypto/testdata/sample-ec-secp384r1-01-public.pem new file mode 100644 index 0000000000..54e6d1686d --- /dev/null +++ b/lib/ocrypto/testdata/sample-ec-secp384r1-01-public.pem @@ -0,0 +1,5 @@ +-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE1xcYL7J+6N/5MJVk0z/VHuOHXbV9e9xc +Ae6vFgrOUx6Z4R8BuLnqX0FpciOaMB8CKTu2qhmidM8rMIpEx/BOsDr9ltoi2DAI +Fi9kvvBjYphnBi8MHYPZE4IFMX/gTzhS +-----END PUBLIC KEY----- diff --git a/lib/ocrypto/testdata/sample-ec-secp521r1-01-private.pem b/lib/ocrypto/testdata/sample-ec-secp521r1-01-private.pem new file mode 100644 index 0000000000..218a2ab28d --- /dev/null +++ b/lib/ocrypto/testdata/sample-ec-secp521r1-01-private.pem @@ -0,0 +1,7 @@ +-----BEGIN EC PRIVATE KEY----- +MIHcAgEBBEIAdSyAcDXV5TQHs1Q1jk41srxicAmZdMY2z7uXqBOysp2hpEq13lYk +cfSfpFE3zzu/iFf808AouXiK6EsBIqXu6SCgBwYFK4EEACOhgYkDgYYABABainiG +YYOEHe6kgIKvfZfQ8GygjvSCE3sjLsEQvf0d3KH07sGiHgUYJLEyhKI/I4a1rHvw +Z8oYuyxyNisSKAT+fAE7Xf/JpWFFeon60N1+/f/Bm+xZ3YKqriEktJnz8tsPFedM +w94tyYFptM69fHrKij+JfTnYtVzIm+rPHQrCOs/JaA== +-----END EC PRIVATE KEY----- diff --git a/lib/ocrypto/testdata/sample-ec-secp521r1-01-public.pem b/lib/ocrypto/testdata/sample-ec-secp521r1-01-public.pem new file mode 100644 index 0000000000..63cec2e919 --- /dev/null +++ b/lib/ocrypto/testdata/sample-ec-secp521r1-01-public.pem @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAWop4hmGDhB3upICCr32X0PBsoI70 +ghN7Iy7BEL39Hdyh9O7Boh4FGCSxMoSiPyOGtax78GfKGLsscjYrEigE/nwBO13/ +yaVhRXqJ+tDdfv3/wZvsWd2Cqq4hJLSZ8/LbDxXnTMPeLcmBabTOvXx6yoo/iX05 +2LVcyJvqzx0KwjrPyWg= +-----END PUBLIC KEY----- diff --git a/lib/ocrypto/testdata/sample-rsa-1024-01-private.pem b/lib/ocrypto/testdata/sample-rsa-1024-01-private.pem new file mode 100644 index 0000000000..be6790982b --- /dev/null +++ b/lib/ocrypto/testdata/sample-rsa-1024-01-private.pem @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANO/UmUeB9MY5z26 +uT/Qwdn1tHEySzewCsWnnDHMgQeQlCjB+6go1XXxpOYv4PiArnp+vINmj1TKkL2L +pFS1/JvD6d564ecba7SgpcH/iToRi13kuXwrocNrsT4zxWO+lpoR1G+CzJ5JSH0H +b+NG4QQ6DwQT6Nj6A5Zd/j6V0GSnAgMBAAECgYBWVnJgLIiAOG1BLDuQm6wPFTJH +3Xvx7uPVh+wWGg6aaQcgP0g/Xrb66laUTP1sFfwOklKHOXBD4Hx37NJKgBHJKt3b +CXs9FCDIqJak+9LLVBgbSuzJ+XUN4HSEfgXveJ7PLEyrxVpwYzIybgTEbO2IA+51 +fC67jnSw4pq7Eu10AQJBAP2xqKEfbdFHJ3FfLoGT0aSfst91sZFQUvQL8zs+2skJ +iWSpX+64QGyeVgKXqaevzDGmkMnIqXQdYyBGjJSG/wECQQDVrA3ZsxUu9Kma9oJ0 +Pxs4aGNQJGrgOVeVOa4LEYwia/BmZEq06w9FKLWbfXpZGsEIoiW97ZrnohxoqMqv +ywunAkEAu43TcELvClBDbcKDfFKPI9jZAfFd9GNg4IHRMZS3ZPdC9wNtI+xd3K92 +QPZk+86w9GgDFNrfxDNRrHPbzJa9AQJAZiBiTldGHLdcCXEhUSaIgCGEtl1xp9JA +hlaXVTsB28HzmTz+aBKhrdCTXMpQnB4pfVLi7zCOBYB6S5vBpNxLqQJBAJ7peIHi +8rWqOtSElK97nwtjrtnyeqHGXz2yI+awU9OyFDnMgBUuowwmIoApiMKTpZ+zfH8N +mtpo5D7DfLSqkNU= +-----END PRIVATE KEY----- diff --git a/lib/ocrypto/testdata/sample-rsa-1024-01-public.pem b/lib/ocrypto/testdata/sample-rsa-1024-01-public.pem new file mode 100644 index 0000000000..cb3bbb6ce6 --- /dev/null +++ b/lib/ocrypto/testdata/sample-rsa-1024-01-public.pem @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDTv1JlHgfTGOc9urk/0MHZ9bRx +Mks3sArFp5wxzIEHkJQowfuoKNV18aTmL+D4gK56fryDZo9UypC9i6RUtfybw+ne +euHnG2u0oKXB/4k6EYtd5Ll8K6HDa7E+M8VjvpaaEdRvgsyeSUh9B2/jRuEEOg8E +E+jY+gOWXf4+ldBkpwIDAQAB +-----END PUBLIC KEY----- diff --git a/lib/ocrypto/testdata/sample-rsa-2048-01-private.pem b/lib/ocrypto/testdata/sample-rsa-2048-01-private.pem new file mode 100644 index 0000000000..0c379cf5ab --- /dev/null +++ b/lib/ocrypto/testdata/sample-rsa-2048-01-private.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCzv7o3b1JcNFpM +pgUyIRatTqxTdCfA6PTltAQDFK37ZM/Zg1mC+QmNMaTXPxNXYlmNKzLnACKn/nEZ +15n+x/t2uHKSE2tM9hxQnBJkJrtm7zM6z5ztNO8fbqIFEB98bRzck7/WXFVfOrKS +Tc9tUku9xk8NRDSHKK0A6w/9Y51NnwwalLC8G66WonsXyRQOurcuxtcUHRKLOOo0 +XZ65vKsZgGfYXQpsWb+N6sf7yDA28yn7aLX53qUFXZmeV8+7KLTopEFZdnNUJNzu +WqRY1dHC8DLi1/mzsSf9pg7/uzuV34J/mbiPkYcXPXuLnvRbFBOZ7Nn9YH2prWVA +0ck7tTqFAgMBAAECggEAE6TbKsZDe7ir+q71J+iWVBviON+bnZlH9FeTTavjpLL8 +hSK19FqXmOLpRy3JRRZGP6eOMVEiOHZV8XNOzNmZqXyYZs7w/dDywLuBxgi2l9YB +5QY9+e18SZTbZ46+xigdjJyoDTr7iIP/cn5G8kVZajTDPGmtDO1c1Nobnf9WOF5/ +raFPN8ouWLaBoL3EQ2CKH/rCPLZIDVWQoawcKgtHshLDy4q5Hi94foAifKvK8cbl +sP2MzuDYWvZ3Bp/0+C5S4W2qbJdo4bv/XAkxzO0hujiaX+hJTa4ZZC2k8/9JgEYz +2P31NloH7Hofy5wKNtDv6nP+v6vBCRmRZm1/nw9TwQKBgQDiTOAxgvMBNxhF0zuo +CX3J7mlU7YSkGJQGhjdltbB4OwcZSaC1pUwN0x7O4lATBZHJ2LeYj2r/mMmaY2a0 ++XgF+eTwVzaiYv8GS6/G//ILnd6ufS8P1k1UmsT408OfMVtq9DrVNtAlaqLWl3Y5 +SJVf7zpykj2YFSXwogikyqZpxQKBgQDLVteg+FMk2QtcrKqfsNH3RIiSJ90X2PH4 +ZM4YaahuecT9BFRQPdCzkAICBjh17Bt0WigDGkZZ/I6izf91/E6chBa0TW59Dpq0 +ojyRVePHBpg85uH6g2bOxkCYlXQ4SIozHPe1bshAkSkkTCxPtJ6cq4yJObQmF68o +M7QUPTJZwQKBgQCX1gTGs5ngQtsiXmw0fsnLZw99UDAi+eq3xe39bD6PLOvCZ8hQ +mCvDStfs76PSX3ZF/AaTcgbUn+sEj5Ul8Aw71kNpjtq1cb6ytq2l06zPZok2gf/F +nIAeOAnY+hzS/wbbaCrhS/m0YSwI128XWEABMj4BCWYSWH4wSkeKaf3mEQKBgBXW +J6XzxQoJ/Pxg1pn7pTDGvVvkyAuNkr64JKHehuYGUa9STbOoT8dYyb5p6JpRVslx +/SYIJlH3m2HEeZC0HcUVMlL+lcT8UoTff12kOaff/21a5h2/CsVd6QX51tdMgvrm +O3vSf9LfQ+nP/Fo67WWpzpfWCJCmrnrEwqwBvmyBAoGBAKZ3YWGpSPnLV2TZpZ8m +NY0EykcKoghDwGFFtSPLb5hzSTiSs5eBEzNpuNnWdePdg3rTYG7I1ojhTkYngDn9 +2GPb7XHPADZFvQdsZ2V9D4ahio8sWUtlcZq+KLjt86KVBJdswgfuiUKGwITPZfOG +JJAivPZMBCoF0NFP2mCyibAj +-----END PRIVATE KEY----- diff --git a/lib/ocrypto/testdata/sample-rsa-2048-01-public.pem b/lib/ocrypto/testdata/sample-rsa-2048-01-public.pem new file mode 100644 index 0000000000..3250938aec --- /dev/null +++ b/lib/ocrypto/testdata/sample-rsa-2048-01-public.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs7+6N29SXDRaTKYFMiEW +rU6sU3QnwOj05bQEAxSt+2TP2YNZgvkJjTGk1z8TV2JZjSsy5wAip/5xGdeZ/sf7 +drhykhNrTPYcUJwSZCa7Zu8zOs+c7TTvH26iBRAffG0c3JO/1lxVXzqykk3PbVJL +vcZPDUQ0hyitAOsP/WOdTZ8MGpSwvBuulqJ7F8kUDrq3LsbXFB0SizjqNF2eubyr +GYBn2F0KbFm/jerH+8gwNvMp+2i1+d6lBV2ZnlfPuyi06KRBWXZzVCTc7lqkWNXR +wvAy4tf5s7En/aYO/7s7ld+Cf5m4j5GHFz17i570WxQTmezZ/WB9qa1lQNHJO7U6 +hQIDAQAB +-----END PUBLIC KEY----- diff --git a/lib/ocrypto/testdata/sample-rsa-4096-01-private.pem b/lib/ocrypto/testdata/sample-rsa-4096-01-private.pem new file mode 100644 index 0000000000..1c0ff9f12a --- /dev/null +++ b/lib/ocrypto/testdata/sample-rsa-4096-01-private.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +0cj686LykQUuApreCxj5YP0tSF0q2Q+6 +-----END PRIVATE KEY----- diff --git a/lib/ocrypto/testdata/sample-rsa-4096-01-public.pem b/lib/ocrypto/testdata/sample-rsa-4096-01-public.pem new file mode 100644 index 0000000000..4d969fe107 --- /dev/null +++ b/lib/ocrypto/testdata/sample-rsa-4096-01-public.pem @@ -0,0 +1,14 @@ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAoVzF5xTtCtUE3FNBSdzc +h1NnZ2xi30moT7rPidyN5ha/qNTzAv+S1xDa927yW2sZwMSF4BuzISUY3DvoRqAp +S5lnwQhUasAdbunDkzliB5DfoOklWe8yjyv/3EoX3/O9g/6UQ4lJxU8WruVn5OAk +s1EXIW0RdceLukEjj9xwTwAVuKL/2SPXSJwoIZW+uZc+VzlanZ8AO0H2mDrMiqXI +Aqv8792z0YjAofJA/Wt8PZTN877sa1Lrf5CjazJTGgcs9XkbU6lLAsQuc4gJlutt +Npn3Gw1/Y3DZqrwCBqiOBPljncRNY1B4/DnrgowQvoAV5o51NRDWU3DWii61G/cl +SJ54WrTP/zVrgTr+q1P1Y4O9Wd1EuFUYhfU9cdfuNtfPrs2owzPFZT8vBI007sNa +eUG0inIwfpsQd0q4/OCzSGRqTLWHhDXOorW/qhTv1rHzLvxPKfjgV5y9sbTvwQYP +OqqJexsske+6UcTdVDWD3be/LBgmfCNOtCIBxGgLmEY7wU/HTfVI5PrjWqlTn6g0 +s97+N0GKMBhrcK4Uj1oknJWWZ2DTRJ7kU8cUaM3x6Br7nhupkORS6uSfnBHFv64V +FszZAIASe5wm8SZL2TslQu4v1+N7ZzXI9mYogaWWLZcudNMApLymGmvFAJMlxv8o +mjgFWEMOKFWDfxYmIMj5ybMCAwEAAQ== +-----END PUBLIC KEY----- diff --git a/service/internal/security/basic_manager.go b/service/internal/security/basic_manager.go index 5fd61a483f..b388259833 100644 --- a/service/internal/security/basic_manager.go +++ b/service/internal/security/basic_manager.go @@ -26,6 +26,7 @@ const ( ristrettoCacheTTL = 30 ) +// BasicManager is implements the SecurityProvider for wrapped keys stored internally (provider mode). type BasicManager struct { l *logger.Logger rootKey []byte diff --git a/service/internal/security/in_process_provider.go b/service/internal/security/in_process_provider.go index 3ecbc1f11b..a31c5717ba 100644 --- a/service/internal/security/in_process_provider.go +++ b/service/internal/security/in_process_provider.go @@ -156,28 +156,29 @@ func (a *InProcessProvider) FindKeyByAlgorithm(_ context.Context, algorithm stri // FindKeyByID finds a key by ID func (a *InProcessProvider) FindKeyByID(_ context.Context, id trust.KeyIdentifier) (trust.KeyDetails, error) { - // Try to determine the algorithm by checking if the key works with known algorithms - for _, alg := range []string{AlgorithmECP256R1, AlgorithmRSA2048} { - // This is a hack since the original provider doesn't have a way to check if a key exists - if alg == AlgorithmECP256R1 { - if _, err := a.cryptoProvider.ECPublicKey(string(id)); err == nil { - return &KeyDetailsAdapter{ - id: id, - algorithm: ocrypto.KeyType(alg), - legacy: a.legacyKeys[string(id)], - cryptoProvider: a.cryptoProvider, - }, nil - } - } else if alg == AlgorithmRSA2048 { - if _, err := a.cryptoProvider.RSAPublicKey(string(id)); err == nil { - return &KeyDetailsAdapter{ - id: id, - algorithm: ocrypto.KeyType(alg), - legacy: a.legacyKeys[string(id)], - cryptoProvider: a.cryptoProvider, - }, nil - } + if k, err := a.cryptoProvider.RSAPublicKey(string(id)); err == nil { + e, err := ocrypto.FromPublicPEM(k) + if err != nil { + return nil, fmt.Errorf("failed to parse rsa public key while scanning for [%s]: %w", id, err) + } + return &KeyDetailsAdapter{ + id: id, + algorithm: e.KeyType(), + legacy: a.legacyKeys[string(id)], + cryptoProvider: a.cryptoProvider, + }, nil + } + if k, err := a.cryptoProvider.ECPublicKey(string(id)); err == nil { + e, err := ocrypto.FromPublicPEM(k) + if err != nil { + return nil, fmt.Errorf("failed to parse ec public key while scanning for [%s]: %w", id, err) } + return &KeyDetailsAdapter{ + id: id, + algorithm: e.KeyType(), + legacy: a.legacyKeys[string(id)], + cryptoProvider: a.cryptoProvider, + }, nil } return nil, ErrCertNotFound } diff --git a/service/kas/kas.go b/service/kas/kas.go index 8989d6bbce..316e617db0 100644 --- a/service/kas/kas.go +++ b/service/kas/kas.go @@ -61,7 +61,8 @@ func NewRegistration() *serviceregistry.Service[kasconnect.AccessServiceHandler] } } - if kasCfg.Preview.KeyManagement { + useKeyManagement := kasCfg.Preview.KeyManagement + if useKeyManagement { srp.Logger.Info("preview feature: key management is enabled") kasURL, err := determineKASURL(srp, kasCfg) @@ -89,7 +90,8 @@ func NewRegistration() *serviceregistry.Service[kasconnect.AccessServiceHandler] // Explicitly set the default manager for session key generation. // This should be configurable, e.g., defaulting to BasicManager or an HSM if available. p.KeyDelegator.SetDefaultMode(security.BasicManagerName) // Example: default to BasicManager - } else { + } + if !useKeyManagement || len(kasCfg.Keyring) > 0 || kasCfg.ECCertID != "" || kasCfg.RSACertID != "" { // Set up both the legacy CryptoProvider and the new SecurityProvider kasCfg.UpgradeMapToKeyring(srp.OTDF.CryptoProvider) p.CryptoProvider = srp.OTDF.CryptoProvider diff --git a/service/kas/key_indexer.go b/service/kas/key_indexer.go index 33686f42e9..9ef80da7b8 100644 --- a/service/kas/key_indexer.go +++ b/service/kas/key_indexer.go @@ -256,9 +256,13 @@ func convertPEMToJWK(_ string) (string, error) { } func (p *KeyAdapter) ExportPrivateKey(_ context.Context) (*trust.PrivateKey, error) { + privateKey := p.key.GetKey().GetPrivateKeyCtx().GetWrappedKey() + if privateKey == "" { + return nil, fmt.Errorf("private key is not exportable. Key ID: %s, KAS: %s", p.key.GetKey().GetKeyId(), p.key.GetKasUri()) + } return &trust.PrivateKey{ WrappingKeyID: trust.KeyIdentifier(p.key.GetKey().GetPrivateKeyCtx().GetKeyId()), - WrappedKey: p.key.GetKey().GetPrivateKeyCtx().GetWrappedKey(), + WrappedKey: privateKey, }, nil }