296 lines
6.8 KiB
Go
296 lines
6.8 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/elliptic"
|
|
"crypto/x509"
|
|
"encoding/pem"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestGenerateKeys(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
|
|
// Create test directories
|
|
keyDir := filepath.Join(tmpDir, "keys")
|
|
err := os.MkdirAll(keyDir, 0755)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Create test config
|
|
key1Path := filepath.Join(keyDir, "test-log-1.key")
|
|
key2Path := filepath.Join(keyDir, "test-log-2.key")
|
|
|
|
configContent := `logs:
|
|
- shortname: "test-log-1"
|
|
secret: "` + key1Path + `"
|
|
- shortname: "test-log-2"
|
|
secret: "` + key2Path + `"`
|
|
|
|
configFile := filepath.Join(tmpDir, "test-config.yaml")
|
|
err = os.WriteFile(configFile, []byte(configContent), 0644)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Run generateKeys
|
|
generateKeys(configFile, false, true, false)
|
|
|
|
// Verify first key was created
|
|
if _, err := os.Stat(key1Path); os.IsNotExist(err) {
|
|
t.Error("Expected first key file to be created")
|
|
}
|
|
|
|
// Verify second key was created
|
|
if _, err := os.Stat(key2Path); os.IsNotExist(err) {
|
|
t.Error("Expected second key file to be created")
|
|
}
|
|
|
|
// Verify key format
|
|
keyContent, err := os.ReadFile(key1Path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
keyStr := string(keyContent)
|
|
if !strings.Contains(keyStr, "-----BEGIN EC PRIVATE KEY-----") {
|
|
t.Error("Expected EC private key PEM header")
|
|
}
|
|
if !strings.Contains(keyStr, "-----END EC PRIVATE KEY-----") {
|
|
t.Error("Expected EC private key PEM footer")
|
|
}
|
|
|
|
// Verify key can be parsed
|
|
block, _ := pem.Decode(keyContent)
|
|
if block == nil {
|
|
t.Error("Failed to decode PEM block")
|
|
}
|
|
|
|
privKey, err := x509.ParseECPrivateKey(block.Bytes)
|
|
if err != nil {
|
|
t.Errorf("Failed to parse EC private key: %v", err)
|
|
}
|
|
|
|
// Verify it's a P-256 key
|
|
if privKey.Curve != elliptic.P256() {
|
|
t.Error("Expected P-256 curve")
|
|
}
|
|
|
|
// Verify file permissions
|
|
info, err := os.Stat(key1Path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
perm := info.Mode().Perm()
|
|
expected := os.FileMode(0600)
|
|
if perm != expected {
|
|
t.Errorf("Expected file mode %o, got %o", expected, perm)
|
|
}
|
|
}
|
|
|
|
func TestGenerateKeysExistingKey(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
|
|
// Create existing key file
|
|
keyPath := filepath.Join(tmpDir, "existing.key")
|
|
existingContent := `-----BEGIN EC PRIVATE KEY-----
|
|
MHcCAQEEIExistingKeyContent
|
|
-----END EC PRIVATE KEY-----`
|
|
|
|
err := os.WriteFile(keyPath, []byte(existingContent), 0600)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Create test config
|
|
configContent := `logs:
|
|
- shortname: "test-log"
|
|
secret: "` + keyPath + `"`
|
|
|
|
configFile := filepath.Join(tmpDir, "test-config.yaml")
|
|
err = os.WriteFile(configFile, []byte(configContent), 0644)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Run generateKeys
|
|
generateKeys(configFile, false, true, false)
|
|
|
|
// Verify existing key was not overwritten
|
|
keyContent, err := os.ReadFile(keyPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if string(keyContent) != existingContent {
|
|
t.Error("Expected existing key to be preserved")
|
|
}
|
|
}
|
|
|
|
func TestGenerateKeysWithoutWriteFlag(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
|
|
keyPath := filepath.Join(tmpDir, "test.key")
|
|
|
|
configContent := `logs:
|
|
- shortname: "test-log"
|
|
secret: "` + keyPath + `"`
|
|
|
|
configFile := filepath.Join(tmpDir, "test-config.yaml")
|
|
err := os.WriteFile(configFile, []byte(configContent), 0644)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Run generateKeys without write flag
|
|
generateKeys(configFile, false, false, false)
|
|
|
|
// Verify key was not created
|
|
if _, err := os.Stat(keyPath); !os.IsNotExist(err) {
|
|
t.Error("Expected key file to not be created without --write flag")
|
|
}
|
|
}
|
|
|
|
func TestGenerateKeysCreateDirectory(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
|
|
// Key path with non-existent directory
|
|
keyDir := filepath.Join(tmpDir, "subdir", "keys")
|
|
keyPath := filepath.Join(keyDir, "test.key")
|
|
|
|
configContent := `logs:
|
|
- shortname: "test-log"
|
|
secret: "` + keyPath + `"`
|
|
|
|
configFile := filepath.Join(tmpDir, "test-config.yaml")
|
|
err := os.WriteFile(configFile, []byte(configContent), 0644)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Run generateKeys
|
|
generateKeys(configFile, false, true, false)
|
|
|
|
// Verify directory was created
|
|
if _, err := os.Stat(keyDir); os.IsNotExist(err) {
|
|
t.Error("Expected directory to be created")
|
|
}
|
|
|
|
// Verify key was created
|
|
if _, err := os.Stat(keyPath); os.IsNotExist(err) {
|
|
t.Error("Expected key file to be created")
|
|
}
|
|
}
|
|
|
|
func TestGenerateKeysMultipleRuns(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
|
|
key1Path := filepath.Join(tmpDir, "key1.key")
|
|
key2Path := filepath.Join(tmpDir, "key2.key")
|
|
|
|
configContent := `logs:
|
|
- shortname: "test-log-1"
|
|
secret: "` + key1Path + `"
|
|
- shortname: "test-log-2"
|
|
secret: "` + key2Path + `"`
|
|
|
|
configFile := filepath.Join(tmpDir, "test-config.yaml")
|
|
err := os.WriteFile(configFile, []byte(configContent), 0644)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// First run - should create both keys
|
|
generateKeys(configFile, false, true, false)
|
|
|
|
// Read first key content
|
|
key1Content, err := os.ReadFile(key1Path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
key2Content, err := os.ReadFile(key2Path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Second run - should not overwrite existing keys
|
|
generateKeys(configFile, false, true, false)
|
|
|
|
// Verify keys are unchanged
|
|
key1Content2, err := os.ReadFile(key1Path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
key2Content2, err := os.ReadFile(key2Path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if string(key1Content) != string(key1Content2) {
|
|
t.Error("First key should not have been overwritten")
|
|
}
|
|
|
|
if string(key2Content) != string(key2Content2) {
|
|
t.Error("Second key should not have been overwritten")
|
|
}
|
|
}
|
|
|
|
func TestECKeyGeneration(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
keyPath := filepath.Join(tmpDir, "test.key")
|
|
|
|
configContent := `logs:
|
|
- shortname: "test-log"
|
|
secret: "` + keyPath + `"`
|
|
|
|
configFile := filepath.Join(tmpDir, "test-config.yaml")
|
|
err := os.WriteFile(configFile, []byte(configContent), 0644)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
generateKeys(configFile, false, true, false)
|
|
|
|
// Read and parse the generated key
|
|
keyContent, err := os.ReadFile(keyPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
block, _ := pem.Decode(keyContent)
|
|
if block == nil {
|
|
t.Fatal("Failed to decode PEM block")
|
|
}
|
|
|
|
if block.Type != "EC PRIVATE KEY" {
|
|
t.Errorf("Expected block type 'EC PRIVATE KEY', got %s", block.Type)
|
|
}
|
|
|
|
privKey, err := x509.ParseECPrivateKey(block.Bytes)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse EC private key: %v", err)
|
|
}
|
|
|
|
// Verify public key can be derived
|
|
pubKey := &privKey.PublicKey
|
|
if pubKey.X == nil || pubKey.Y == nil {
|
|
t.Error("Public key coordinates should not be nil")
|
|
}
|
|
|
|
// Verify key is on the correct curve
|
|
if !pubKey.Curve.IsOnCurve(pubKey.X, pubKey.Y) {
|
|
t.Error("Public key is not on the curve")
|
|
}
|
|
|
|
// Verify we can marshal the public key (for log ID computation)
|
|
_, err = x509.MarshalPKIXPublicKey(pubKey)
|
|
if err != nil {
|
|
t.Errorf("Failed to marshal public key: %v", err)
|
|
}
|
|
} |