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) } }