mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-02 17:31:39 +00:00
366 lines
8.4 KiB
Go
366 lines
8.4 KiB
Go
// Package keeper provides integration tests for JWK verification
|
|
package keeper
|
|
|
|
import (
|
|
"crypto"
|
|
"crypto/ecdsa"
|
|
"crypto/ed25519"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/sha256"
|
|
"crypto/sha512"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"math/big"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/crypto/sha3"
|
|
)
|
|
|
|
// TestECJWKVerification tests EC JWK verification with multiple curves
|
|
func TestECJWKVerification(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
curve elliptic.Curve
|
|
crv string
|
|
}{
|
|
{"P-256", elliptic.P256(), "P-256"},
|
|
{"P-384", elliptic.P384(), "P-384"},
|
|
{"P-521", elliptic.P521(), "P-521"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Generate EC key pair
|
|
priv, err := ecdsa.GenerateKey(tt.curve, rand.Reader)
|
|
require.NoError(t, err)
|
|
|
|
// Create JWK
|
|
jwk := map[string]any{
|
|
"kty": "EC",
|
|
"crv": tt.crv,
|
|
"x": base64.RawURLEncoding.EncodeToString(priv.X.Bytes()),
|
|
"y": base64.RawURLEncoding.EncodeToString(priv.Y.Bytes()),
|
|
}
|
|
|
|
// Create test message and signature
|
|
message := []byte("test message")
|
|
var hash []byte
|
|
switch tt.crv {
|
|
case "P-256":
|
|
h := sha256.Sum256(message)
|
|
hash = h[:]
|
|
case "P-384":
|
|
h := sha3.Sum384(message)
|
|
hash = h[:]
|
|
case "P-521":
|
|
h := sha512.Sum512(message)
|
|
hash = h[:]
|
|
}
|
|
|
|
sig, err := ecdsa.SignASN1(rand.Reader, priv, hash)
|
|
require.NoError(t, err)
|
|
|
|
// Test verification
|
|
k := Keeper{}
|
|
valid, err := k.verifyWithJWKEC(jwk, sig)
|
|
require.NoError(t, err)
|
|
require.True(t, valid, "EC signature verification failed for %s", tt.name)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestRSAJWKVerification tests RSA JWK verification with different key sizes
|
|
func TestRSAJWKVerification(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
keySize int
|
|
alg string
|
|
}{
|
|
{"RS256-2048", 2048, "RS256"},
|
|
{"RS384-3072", 3072, "RS384"},
|
|
{"RS512-4096", 4096, "RS512"},
|
|
{"PS256-2048", 2048, "PS256"},
|
|
{"PS384-3072", 3072, "PS384"},
|
|
{"PS512-4096", 4096, "PS512"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Generate RSA key pair
|
|
priv, err := rsa.GenerateKey(rand.Reader, tt.keySize)
|
|
require.NoError(t, err)
|
|
|
|
// Create JWK
|
|
jwk := map[string]any{
|
|
"kty": "RSA",
|
|
"alg": tt.alg,
|
|
"n": base64.RawURLEncoding.EncodeToString(priv.N.Bytes()),
|
|
"e": base64.RawURLEncoding.EncodeToString(
|
|
big.NewInt(int64(priv.PublicKey.E)).Bytes(),
|
|
),
|
|
}
|
|
|
|
// Create test message and signature
|
|
message := []byte("test message")
|
|
var hash []byte
|
|
var hashFunc crypto.Hash
|
|
|
|
switch tt.alg {
|
|
case "RS256", "PS256":
|
|
h := sha256.Sum256(message)
|
|
hash = h[:]
|
|
hashFunc = crypto.SHA256
|
|
case "RS384", "PS384":
|
|
h := sha3.Sum384(message)
|
|
hash = h[:]
|
|
hashFunc = crypto.SHA384
|
|
case "RS512", "PS512":
|
|
h := sha512.Sum512(message)
|
|
hash = h[:]
|
|
hashFunc = crypto.SHA512
|
|
}
|
|
|
|
var sig []byte
|
|
if tt.alg[:2] == "PS" {
|
|
// PSS signature
|
|
opts := &rsa.PSSOptions{
|
|
SaltLength: rsa.PSSSaltLengthEqualsHash,
|
|
Hash: hashFunc,
|
|
}
|
|
sig, err = rsa.SignPSS(rand.Reader, priv, hashFunc, hash, opts)
|
|
} else {
|
|
// PKCS#1 v1.5 signature
|
|
sig, err = rsa.SignPKCS1v15(rand.Reader, priv, hashFunc, hash)
|
|
}
|
|
require.NoError(t, err)
|
|
|
|
// Test verification
|
|
k := Keeper{}
|
|
valid, err := k.verifyWithJWKRSA(jwk, sig)
|
|
require.NoError(t, err)
|
|
require.True(t, valid, "RSA signature verification failed for %s", tt.name)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestOKPJWKVerification tests Ed25519 JWK verification
|
|
func TestOKPJWKVerification(t *testing.T) {
|
|
// Generate Ed25519 key pair
|
|
pub, priv, err := ed25519.GenerateKey(rand.Reader)
|
|
require.NoError(t, err)
|
|
|
|
// Create JWK
|
|
jwk := map[string]any{
|
|
"kty": "OKP",
|
|
"crv": "Ed25519",
|
|
"x": base64.RawURLEncoding.EncodeToString(pub),
|
|
}
|
|
|
|
// Create test message and signature
|
|
message := []byte("test message")
|
|
sig := ed25519.Sign(priv, message)
|
|
|
|
// Test verification
|
|
k := Keeper{}
|
|
valid, err := k.verifyWithJWKOKP(jwk, sig)
|
|
require.NoError(t, err)
|
|
require.True(t, valid, "Ed25519 signature verification failed")
|
|
}
|
|
|
|
// TestMultiAlgorithmDetection tests the main JWK verification router
|
|
func TestMultiAlgorithmDetection(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
jwk map[string]any
|
|
err bool
|
|
}{
|
|
{
|
|
name: "EC key",
|
|
jwk: map[string]any{
|
|
"kty": "EC",
|
|
"crv": "P-256",
|
|
"x": base64.RawURLEncoding.EncodeToString(make([]byte, 32)),
|
|
"y": base64.RawURLEncoding.EncodeToString(make([]byte, 32)),
|
|
},
|
|
err: false,
|
|
},
|
|
{
|
|
name: "RSA key",
|
|
jwk: map[string]any{
|
|
"kty": "RSA",
|
|
"n": base64.RawURLEncoding.EncodeToString(make([]byte, 256)),
|
|
"e": base64.RawURLEncoding.EncodeToString([]byte{1, 0, 1}),
|
|
},
|
|
err: false,
|
|
},
|
|
{
|
|
name: "OKP key",
|
|
jwk: map[string]any{
|
|
"kty": "OKP",
|
|
"crv": "Ed25519",
|
|
"x": base64.RawURLEncoding.EncodeToString(make([]byte, 32)),
|
|
},
|
|
err: false,
|
|
},
|
|
{
|
|
name: "Unsupported key type",
|
|
jwk: map[string]any{
|
|
"kty": "INVALID",
|
|
},
|
|
err: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
jwkStr, err := json.Marshal(tt.jwk)
|
|
require.NoError(t, err)
|
|
|
|
k := Keeper{}
|
|
_, err = k.verifyWithJWK(string(jwkStr), []byte("dummy signature"))
|
|
|
|
if tt.err {
|
|
require.Error(t, err, "Expected error for %s", tt.name)
|
|
} else {
|
|
// Note: Will fail signature verification but should parse correctly
|
|
if err != nil {
|
|
require.NotContains(t, err.Error(), "unsupported JWK key type")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestInvalidJWKHandling tests error handling for invalid JWKs
|
|
func TestInvalidJWKHandling(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
jwk map[string]any
|
|
err string
|
|
}{
|
|
{
|
|
name: "Missing curve in EC JWK",
|
|
jwk: map[string]any{
|
|
"kty": "EC",
|
|
"x": "test",
|
|
"y": "test",
|
|
},
|
|
err: "missing or invalid 'crv' parameter",
|
|
},
|
|
{
|
|
name: "Missing x coordinate in EC JWK",
|
|
jwk: map[string]any{
|
|
"kty": "EC",
|
|
"crv": "P-256",
|
|
"y": "test",
|
|
},
|
|
err: "missing or invalid 'x' coordinate",
|
|
},
|
|
{
|
|
name: "Missing modulus in RSA JWK",
|
|
jwk: map[string]any{
|
|
"kty": "RSA",
|
|
"e": "AQAB",
|
|
},
|
|
err: "missing or invalid 'n' (modulus)",
|
|
},
|
|
{
|
|
name: "Small RSA key",
|
|
jwk: map[string]any{
|
|
"kty": "RSA",
|
|
"n": base64.RawURLEncoding.EncodeToString(make([]byte, 128)), // 1024 bits
|
|
"e": "AQAB",
|
|
},
|
|
err: "RSA key size too small",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
k := Keeper{}
|
|
|
|
switch tt.jwk["kty"] {
|
|
case "EC":
|
|
_, err := k.verifyWithJWKEC(tt.jwk, []byte{})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), tt.err)
|
|
case "RSA":
|
|
_, err := k.verifyWithJWKRSA(tt.jwk, []byte{})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), tt.err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// BenchmarkECJWKVerification benchmarks EC JWK verification
|
|
func BenchmarkECJWKVerification(b *testing.B) {
|
|
curves := []struct {
|
|
name string
|
|
curve elliptic.Curve
|
|
crv string
|
|
}{
|
|
{"P256", elliptic.P256(), "P-256"},
|
|
{"P384", elliptic.P384(), "P-384"},
|
|
{"P521", elliptic.P521(), "P-521"},
|
|
}
|
|
|
|
for _, c := range curves {
|
|
b.Run(c.name, func(b *testing.B) {
|
|
// Setup
|
|
priv, _ := ecdsa.GenerateKey(c.curve, rand.Reader)
|
|
jwk := map[string]any{
|
|
"kty": "EC",
|
|
"crv": c.crv,
|
|
"x": base64.RawURLEncoding.EncodeToString(priv.X.Bytes()),
|
|
"y": base64.RawURLEncoding.EncodeToString(priv.Y.Bytes()),
|
|
}
|
|
|
|
message := []byte("test message")
|
|
h := sha256.Sum256(message)
|
|
sig, _ := ecdsa.SignASN1(rand.Reader, priv, h[:])
|
|
|
|
k := Keeper{}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, _ = k.verifyWithJWKEC(jwk, sig)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// BenchmarkRSAJWKVerification benchmarks RSA JWK verification
|
|
func BenchmarkRSAJWKVerification(b *testing.B) {
|
|
keySizes := []int{2048, 3072, 4096}
|
|
|
|
for _, size := range keySizes {
|
|
b.Run(fmt.Sprintf("RSA%d", size), func(b *testing.B) {
|
|
// Setup
|
|
priv, _ := rsa.GenerateKey(rand.Reader, size)
|
|
jwk := map[string]any{
|
|
"kty": "RSA",
|
|
"n": base64.RawURLEncoding.EncodeToString(priv.N.Bytes()),
|
|
"e": base64.RawURLEncoding.EncodeToString(
|
|
big.NewInt(int64(priv.PublicKey.E)).Bytes(),
|
|
),
|
|
}
|
|
|
|
message := []byte("test message")
|
|
h := sha256.Sum256(message)
|
|
sig, _ := rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA256, h[:])
|
|
|
|
k := Keeper{}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, _ = k.verifyWithJWKRSA(jwk, sig)
|
|
}
|
|
})
|
|
}
|
|
}
|