mirror of
https://github.com/sonr-io/common.git
synced 2026-01-12 04:09:13 +00:00
271 lines
8.1 KiB
Go
271 lines
8.1 KiB
Go
package common
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"github.com/sonr-io/common/webauthn"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// Test WebAuthn Challenge generation
|
|
func TestNewChallenge(t *testing.T) {
|
|
challenge, err := NewChallenge()
|
|
require.NoError(t, err, "NewChallenge should not return an error")
|
|
assert.NotEmpty(t, challenge, "Challenge should not be empty")
|
|
|
|
// Decode to verify it's valid base64
|
|
decoded, err := base64.RawURLEncoding.DecodeString(challenge)
|
|
require.NoError(t, err, "Challenge should be valid base64 URL encoding")
|
|
assert.Len(t, decoded, 32, "Challenge should be 32 bytes when decoded")
|
|
}
|
|
|
|
func TestNewChallenge_Uniqueness(t *testing.T) {
|
|
challenge1, err1 := NewChallenge()
|
|
challenge2, err2 := NewChallenge()
|
|
|
|
require.NoError(t, err1)
|
|
require.NoError(t, err2)
|
|
assert.NotEqual(t, challenge1, challenge2, "Two challenges should be unique")
|
|
}
|
|
|
|
// Test Challenge Length
|
|
func TestChallengeLength(t *testing.T) {
|
|
length := ChallengeLength()
|
|
assert.Equal(t, 32, length, "Challenge length should be 32 bytes")
|
|
}
|
|
|
|
// Test VerifyOrigin
|
|
func TestVerifyOrigin(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
origin string
|
|
allowedOrigins []string
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "Valid origin",
|
|
origin: "https://example.com",
|
|
allowedOrigins: []string{"https://example.com", "https://test.com"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Valid origin with port",
|
|
origin: "https://example.com:8080",
|
|
allowedOrigins: []string{"https://example.com:8080"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Origin not in allowed list",
|
|
origin: "https://malicious.com",
|
|
allowedOrigins: []string{"https://example.com"},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "Invalid origin format",
|
|
origin: "not-a-url",
|
|
allowedOrigins: []string{"https://example.com"},
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := VerifyOrigin(tt.origin, tt.allowedOrigins)
|
|
if tt.expectError {
|
|
assert.Error(t, err)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Test Base64 URL Encoding/Decoding
|
|
func TestEncodeDecodeBase64URL(t *testing.T) {
|
|
testData := []byte("Hello, WebAuthn!")
|
|
|
|
// Encode
|
|
encoded := EncodeBase64URL(testData)
|
|
assert.NotEmpty(t, encoded, "Encoded data should not be empty")
|
|
assert.NotContains(t, encoded, "=", "URL-safe base64 should not contain padding")
|
|
|
|
// Decode
|
|
decoded, err := DecodeBase64URL(encoded)
|
|
require.NoError(t, err, "Decoding should not return an error")
|
|
assert.Equal(t, testData, decoded, "Decoded data should match original")
|
|
}
|
|
|
|
func TestDecodeBase64URL_InvalidInput(t *testing.T) {
|
|
invalidBase64 := "not valid base64!!!"
|
|
_, err := DecodeBase64URL(invalidBase64)
|
|
assert.Error(t, err, "Should return error for invalid base64")
|
|
}
|
|
|
|
// Test Credential Creation Marshal/Unmarshal
|
|
func TestMarshalUnmarshalCredentialCreation(t *testing.T) {
|
|
// Create a sample credential creation response
|
|
original := &webauthn.CredentialCreationResponse{
|
|
PublicKeyCredential: webauthn.PublicKeyCredential{
|
|
Credential: webauthn.Credential{
|
|
ID: "test-credential-id",
|
|
Type: "public-key",
|
|
},
|
|
RawID: webauthn.URLEncodedBase64("raw-id-bytes"),
|
|
},
|
|
}
|
|
|
|
// Marshal
|
|
data, err := MarshalCredentialCreation(original)
|
|
require.NoError(t, err, "Marshaling should not return an error")
|
|
assert.NotEmpty(t, data, "Marshaled data should not be empty")
|
|
|
|
// Verify it's valid JSON
|
|
var jsonCheck map[string]interface{}
|
|
err = json.Unmarshal(data, &jsonCheck)
|
|
require.NoError(t, err, "Marshaled data should be valid JSON")
|
|
|
|
// Unmarshal
|
|
unmarshaled, err := UnmarshalCredentialCreation(data)
|
|
require.NoError(t, err, "Unmarshaling should not return an error")
|
|
assert.Equal(t, original.ID, unmarshaled.ID, "Credential ID should match")
|
|
assert.Equal(t, original.Type, unmarshaled.Type, "Credential type should match")
|
|
}
|
|
|
|
func TestUnmarshalCredentialCreation_InvalidJSON(t *testing.T) {
|
|
invalidJSON := []byte("{invalid json")
|
|
_, err := UnmarshalCredentialCreation(invalidJSON)
|
|
assert.Error(t, err, "Should return error for invalid JSON")
|
|
}
|
|
|
|
// Test Credential Assertion Marshal/Unmarshal
|
|
func TestMarshalUnmarshalCredentialAssertion(t *testing.T) {
|
|
// Create a sample credential assertion response
|
|
original := &webauthn.CredentialAssertionResponse{
|
|
PublicKeyCredential: webauthn.PublicKeyCredential{
|
|
Credential: webauthn.Credential{
|
|
ID: "test-assertion-id",
|
|
Type: "public-key",
|
|
},
|
|
RawID: webauthn.URLEncodedBase64("assertion-raw-id"),
|
|
},
|
|
}
|
|
|
|
// Marshal
|
|
data, err := MarshalCredentialAssertion(original)
|
|
require.NoError(t, err, "Marshaling should not return an error")
|
|
assert.NotEmpty(t, data, "Marshaled data should not be empty")
|
|
|
|
// Verify it's valid JSON
|
|
var jsonCheck map[string]interface{}
|
|
err = json.Unmarshal(data, &jsonCheck)
|
|
require.NoError(t, err, "Marshaled data should be valid JSON")
|
|
|
|
// Unmarshal
|
|
unmarshaled, err := UnmarshalCredentialAssertion(data)
|
|
require.NoError(t, err, "Unmarshaling should not return an error")
|
|
assert.Equal(t, original.ID, unmarshaled.ID, "Assertion ID should match")
|
|
assert.Equal(t, original.Type, unmarshaled.Type, "Assertion type should match")
|
|
}
|
|
|
|
func TestUnmarshalCredentialAssertion_InvalidJSON(t *testing.T) {
|
|
invalidJSON := []byte("{invalid json")
|
|
_, err := UnmarshalCredentialAssertion(invalidJSON)
|
|
assert.Error(t, err, "Should return error for invalid JSON")
|
|
}
|
|
|
|
// Test ParseCredentialCreation with valid minimal data
|
|
func TestParseCredentialCreation(t *testing.T) {
|
|
// Create a minimal valid credential creation response
|
|
challenge := make([]byte, 32)
|
|
rand.Read(challenge)
|
|
|
|
clientDataJSON := map[string]interface{}{
|
|
"type": "webauthn.create",
|
|
"challenge": base64.RawURLEncoding.EncodeToString(challenge),
|
|
"origin": "https://example.com",
|
|
}
|
|
clientDataBytes, _ := json.Marshal(clientDataJSON)
|
|
|
|
// Create a minimal attestation object (this would normally come from an authenticator)
|
|
// For testing, we'll create a structure that can be parsed
|
|
credResponse := &webauthn.CredentialCreationResponse{
|
|
PublicKeyCredential: webauthn.PublicKeyCredential{
|
|
Credential: webauthn.Credential{
|
|
ID: base64.RawURLEncoding.EncodeToString([]byte("test-credential-id")),
|
|
Type: "public-key",
|
|
},
|
|
RawID: webauthn.URLEncodedBase64("test-credential-id"),
|
|
},
|
|
AttestationResponse: webauthn.AuthenticatorAttestationResponse{
|
|
AuthenticatorResponse: webauthn.AuthenticatorResponse{
|
|
ClientDataJSON: clientDataBytes,
|
|
},
|
|
// Note: In a real scenario, AttestationObject would need to be properly formatted
|
|
},
|
|
}
|
|
|
|
data, err := json.Marshal(credResponse)
|
|
require.NoError(t, err)
|
|
|
|
// This will fail because we don't have a valid attestation object,
|
|
// but it tests that the function is wired up correctly
|
|
_, err = ParseCredentialCreation(data)
|
|
// We expect an error because the attestation object is not valid
|
|
assert.Error(t, err, "Should return error for incomplete attestation data")
|
|
}
|
|
|
|
// Test ParseCredentialAssertion with invalid data
|
|
func TestParseCredentialAssertion_InvalidData(t *testing.T) {
|
|
invalidData := []byte(`{"id": "test", "type": "invalid"}`)
|
|
_, err := ParseCredentialAssertion(invalidData)
|
|
assert.Error(t, err, "Should return error for invalid assertion data")
|
|
}
|
|
|
|
// Benchmark tests
|
|
func BenchmarkNewChallenge(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_, _ = NewChallenge()
|
|
}
|
|
}
|
|
|
|
func BenchmarkEncodeBase64URL(b *testing.B) {
|
|
data := make([]byte, 32)
|
|
rand.Read(data)
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
_ = EncodeBase64URL(data)
|
|
}
|
|
}
|
|
|
|
func BenchmarkDecodeBase64URL(b *testing.B) {
|
|
data := make([]byte, 32)
|
|
rand.Read(data)
|
|
encoded := EncodeBase64URL(data)
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
_, _ = DecodeBase64URL(encoded)
|
|
}
|
|
}
|
|
|
|
func BenchmarkMarshalCredentialCreation(b *testing.B) {
|
|
cred := &webauthn.CredentialCreationResponse{
|
|
PublicKeyCredential: webauthn.PublicKeyCredential{
|
|
Credential: webauthn.Credential{
|
|
ID: "test-id",
|
|
Type: "public-key",
|
|
},
|
|
},
|
|
}
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
_, _ = MarshalCredentialCreation(cred)
|
|
}
|
|
}
|