diff --git a/README.md b/README.md index a8fefa5..5a7a157 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,6 @@ resource "local_sensitive_file" "family_key" { resource "tor_relay_identity_rsa" "bridge" {} resource "tor_relay_identity_ed25519" "bridge" {} -# Note: Ed25519 keys are available in both PEM format (private_key_pem, public_key_pem) -# and Tor's binary format (private_key_tor, public_key_tor) resource "tor_obfs4_state" "bridge" { rsa_identity_private_key = tor_relay_identity_rsa.bridge.private_key_pem @@ -88,17 +86,11 @@ output "rsa_fingerprint_hashed" { } output "ed25519_identity_pem" { - description = "Ed25519 identity private key for bridge configuration (PEM format)" + description = "Ed25519 identity private key for bridge configuration" value = tor_relay_identity_ed25519.bridge.private_key_pem sensitive = true } -output "ed25519_identity_tor" { - description = "Ed25519 identity private key in Tor's binary format (base64 encoded)" - value = tor_relay_identity_ed25519.bridge.private_key_tor - sensitive = true -} - output "obfs4_state_json" { description = "Complete obfs4 state for bridge runtime" value = tor_obfs4_state.bridge.state_json diff --git a/docs/resources/relay_identity_ed25519.md b/docs/resources/relay_identity_ed25519.md index 79b32df..b1662d9 100644 --- a/docs/resources/relay_identity_ed25519.md +++ b/docs/resources/relay_identity_ed25519.md @@ -52,7 +52,5 @@ output "public_key_fingerprint_sha256" { - `algorithm` (String) Name of the algorithm used when generating the private key (always 'Ed25519') - `id` (String) Unique identifier based on public key fingerprint - `private_key_pem` (String, Sensitive) Private key data in PEM (RFC 1421) format -- `private_key_tor` (String, Sensitive) Private key data in Tor's binary format, base64 encoded -- `public_key_fingerprint_sha256` (String) Base64 encoded public key bytes (32 bytes) without padding, used as the Tor Ed25519 fingerprint +- `public_key_fingerprint_sha256` (String) SHA256 fingerprint of the public key in hex format - `public_key_pem` (String) Public key data in PEM (RFC 1421) format -- `public_key_tor` (String) Public key data in Tor's binary format, base64 encoded diff --git a/e2e-tests/obfs4/main.tf b/e2e-tests/obfs4/main.tf index 01e21b5..08a3d97 100644 --- a/e2e-tests/obfs4/main.tf +++ b/e2e-tests/obfs4/main.tf @@ -26,13 +26,6 @@ resource "tor_obfs4_state" "bridge" { ed25519_identity_private_key = tor_relay_identity_ed25519.bridge.private_key_pem } -# Alternative: obfs4 state could also use Tor format keys (demonstration only) -# resource "tor_obfs4_state" "bridge_alt" { -# rsa_identity_private_key = tor_relay_identity_rsa.bridge.private_key_pem -# ed25519_identity_private_key = tor_relay_identity_ed25519.bridge.private_key_pem -# # Note: private_key_tor could be used here as well for ed25519 keys -# } - # Generate bridge line for client distribution data "tor_obfs4_bridge_line" "bridge" { ip_address = "203.0.113.1" @@ -53,27 +46,11 @@ output "rsa_fingerprint_sha256" { value = tor_relay_identity_rsa.bridge.public_key_fingerprint_sha256 } -output "rsa_fingerprint_sha1_hashed" { - description = "RSA identity fingerprint (SHA1) hashed for privacy" - value = tor_relay_identity_rsa.bridge.public_key_fingerprint_sha1_hashed -} - output "ed25519_fingerprint_sha256" { description = "Ed25519 identity fingerprint (SHA256)" value = tor_relay_identity_ed25519.bridge.public_key_fingerprint_sha256 } -output "ed25519_private_key_tor" { - description = "Ed25519 private key in Tor binary format (base64)" - value = tor_relay_identity_ed25519.bridge.private_key_tor - sensitive = true -} - -output "ed25519_public_key_tor" { - description = "Ed25519 public key in Tor binary format (base64)" - value = tor_relay_identity_ed25519.bridge.public_key_tor -} - output "obfs4_certificate" { description = "obfs4 certificate for bridge line" value = tor_obfs4_state.bridge.certificate diff --git a/e2e-tests/tor-family/main.tf b/e2e-tests/tor-family/main.tf index f2417a4..e851a18 100644 --- a/e2e-tests/tor-family/main.tf +++ b/e2e-tests/tor-family/main.tf @@ -17,10 +17,6 @@ resource "tor_family_identity" "this" { family_name = "MyFamily" } -resource "tor_relay_identity_rsa" "this" {} - -resource "tor_relay_identity_ed25519" "this" {} - resource "local_sensitive_file" "family_key" { content_base64 = tor_family_identity.this.secret_key filename = "./data/keys/MyKey.secret_family_key" @@ -46,34 +42,3 @@ output "family_id" { description = "Family ID for the bridge" value = tor_family_identity.this.id } - -output "rsa_fingerprint_sha1" { - description = "RSA identity fingerprint (SHA1) - should be uppercase hex" - value = tor_relay_identity_rsa.this.public_key_fingerprint_sha1 -} - -output "rsa_fingerprint_sha1_hashed" { - description = "RSA identity fingerprint (SHA1) hashed for privacy monitoring" - value = tor_relay_identity_rsa.this.public_key_fingerprint_sha1_hashed -} - -output "rsa_fingerprint_sha256" { - description = "RSA identity fingerprint (SHA256)" - value = tor_relay_identity_rsa.this.public_key_fingerprint_sha256 -} - -output "ed25519_fingerprint_sha256" { - description = "ED25519 identity fingerprint (base64 encoded public key bytes)" - value = tor_relay_identity_ed25519.this.public_key_fingerprint_sha256 -} - -output "ed25519_private_key_tor" { - description = "ED25519 private key in Tor binary format (base64 encoded)" - value = tor_relay_identity_ed25519.this.private_key_tor - sensitive = true -} - -output "ed25519_public_key_tor" { - description = "ED25519 public key in Tor binary format (base64 encoded)" - value = tor_relay_identity_ed25519.this.public_key_tor -} diff --git a/flake.nix b/flake.nix index 46453b4..363a18f 100644 --- a/flake.nix +++ b/flake.nix @@ -8,7 +8,6 @@ let supportedSystems = [ "x86_64-linux" - "aarch64-darwin" ]; forEachSupportedSystem = f: diff --git a/internal/provider/tor_relay_identity_ed25519_resource.go b/internal/provider/tor_relay_identity_ed25519_resource.go index f513097..3a58a8f 100644 --- a/internal/provider/tor_relay_identity_ed25519_resource.go +++ b/internal/provider/tor_relay_identity_ed25519_resource.go @@ -7,11 +7,10 @@ import ( "context" "crypto/ed25519" "crypto/rand" + "crypto/sha256" "crypto/x509" - "encoding/base64" "encoding/pem" "fmt" - "strings" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" @@ -34,8 +33,6 @@ type TorRelayIdentityEd25519ResourceModel struct { Algorithm types.String `tfsdk:"algorithm"` PrivateKeyPem types.String `tfsdk:"private_key_pem"` PublicKeyPem types.String `tfsdk:"public_key_pem"` - PrivateKeyTor types.String `tfsdk:"private_key_tor"` - PublicKeyTor types.String `tfsdk:"public_key_tor"` PublicKeyFingerprintSha256 types.String `tfsdk:"public_key_fingerprint_sha256"` } @@ -77,24 +74,9 @@ func (r *TorRelayIdentityEd25519Resource) Schema(ctx context.Context, req resour stringplanmodifier.UseStateForUnknown(), }, }, - "private_key_tor": schema.StringAttribute{ - Computed: true, - Sensitive: true, - MarkdownDescription: "Private key data in Tor's binary format, base64 encoded", - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - }, - "public_key_tor": schema.StringAttribute{ - Computed: true, - MarkdownDescription: "Public key data in Tor's binary format, base64 encoded", - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - }, "public_key_fingerprint_sha256": schema.StringAttribute{ Computed: true, - MarkdownDescription: "Base64 encoded public key bytes (32 bytes) without padding, used as the Tor Ed25519 fingerprint", + MarkdownDescription: "SHA256 fingerprint of the public key in hex format", PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), }, @@ -143,19 +125,12 @@ func (r *TorRelayIdentityEd25519Resource) Create(ctx context.Context, req resour } data.PublicKeyPem = types.StringValue(publicKeyPem) - // Encode keys in Tor format - privateKeyTor := r.encodeTorPrivateKey(privateKey) - data.PrivateKeyTor = types.StringValue(privateKeyTor) + // Generate SHA256 fingerprint + sha256Fingerprint := r.generateSha256Fingerprint(publicKey) + data.PublicKeyFingerprintSha256 = types.StringValue(sha256Fingerprint) - publicKeyTor := r.encodeTorPublicKey(publicKey) - data.PublicKeyTor = types.StringValue(publicKeyTor) - - // Generate Tor Ed25519 fingerprint (base64 encoded public key bytes without padding) - ed25519Fingerprint := r.generateEd25519Fingerprint(publicKey) - data.PublicKeyFingerprintSha256 = types.StringValue(ed25519Fingerprint) - - // Generate ID from Ed25519 fingerprint - data.Id = types.StringValue(fmt.Sprintf("ed25519-%s", ed25519Fingerprint[:16])) + // Generate ID from SHA256 fingerprint + data.Id = types.StringValue(fmt.Sprintf("ed25519-%s", sha256Fingerprint[:16])) tflog.Trace(ctx, "created tor relay identity Ed25519 resource") @@ -206,37 +181,8 @@ func (r *TorRelayIdentityEd25519Resource) encodePublicKeyPEM(publicKey ed25519.P return string(publicKeyPem), nil } -func (r *TorRelayIdentityEd25519Resource) generateEd25519Fingerprint(publicKey ed25519.PublicKey) string { - fingerprint := base64.StdEncoding.EncodeToString(publicKey) - return strings.TrimRight(fingerprint, "=") -} - -func (r *TorRelayIdentityEd25519Resource) encodeTorPrivateKey(privateKey ed25519.PrivateKey) string { - // Tor Ed25519 private key format: - // "== ed25519v1-secret: type0 ==" + null bytes + 64 bytes key data - header := "== ed25519v1-secret: type0 ==" - - // Create 96-byte buffer: 32 bytes header + null padding + 64 bytes key - torKey := make([]byte, 96) - copy(torKey, header) - - // Copy the private key (64 bytes) starting at offset 32 - copy(torKey[32:], privateKey) - - return base64.StdEncoding.EncodeToString(torKey) -} - -func (r *TorRelayIdentityEd25519Resource) encodeTorPublicKey(publicKey ed25519.PublicKey) string { - // Tor Ed25519 public key format: - // "== ed25519v1-public: type0 ==" + null bytes + 32 bytes key data - header := "== ed25519v1-public: type0 ==" - - // Create 64-byte buffer: 32 bytes header + null padding + 32 bytes key - torKey := make([]byte, 64) - copy(torKey, header) - - // Copy the public key (32 bytes) starting at offset 32 - copy(torKey[32:], publicKey) - - return base64.StdEncoding.EncodeToString(torKey) +func (r *TorRelayIdentityEd25519Resource) generateSha256Fingerprint(publicKey ed25519.PublicKey) string { + publicKeyBytes, _ := x509.MarshalPKIXPublicKey(publicKey) + sha256Sum := sha256.Sum256(publicKeyBytes) + return fmt.Sprintf("%X", sha256Sum) } diff --git a/internal/provider/tor_relay_identity_ed25519_resource_test.go b/internal/provider/tor_relay_identity_ed25519_resource_test.go index f293060..77f2e07 100644 --- a/internal/provider/tor_relay_identity_ed25519_resource_test.go +++ b/internal/provider/tor_relay_identity_ed25519_resource_test.go @@ -4,7 +4,6 @@ package provider import ( - "encoding/base64" "regexp" "testing" @@ -28,67 +27,13 @@ func TestAccTorRelayIdentityEd25519Resource(t *testing.T) { // Verify PEM format resource.TestMatchResourceAttr("tor_relay_identity_ed25519.test", "private_key_pem", regexp.MustCompile(`^-----BEGIN PRIVATE KEY-----`)), resource.TestMatchResourceAttr("tor_relay_identity_ed25519.test", "public_key_pem", regexp.MustCompile(`^-----BEGIN PUBLIC KEY-----`)), - resource.TestMatchResourceAttr("tor_relay_identity_ed25519.test", "public_key_fingerprint_sha256", regexp.MustCompile(`^[A-Za-z0-9+/]{43}$`)), + resource.TestMatchResourceAttr("tor_relay_identity_ed25519.test", "public_key_fingerprint_sha256", regexp.MustCompile(`^[0-9A-F]{64}$`)), ), }, }, }) } -func TestTorEd25519FingerprintGeneration(t *testing.T) { - testPubKeyBase64 := "PT0gZWQyNTUxOXYxLXB1YmxpYzogdHlwZTAgPT0AAAB6UEqfT3OvqdpNfw/rbOucsc5AXRUw4lcy/SaWxruoYA==" - expectedFingerprint := "elBKn09zr6naTX8P62zrnLHOQF0VMOJXMv0mlsa7qGA" - - pubKeyBytes, err := base64.StdEncoding.DecodeString(testPubKeyBase64) - if err != nil { - t.Fatalf("Failed to decode test public key: %v", err) - } - - actualFingerprint := base64.StdEncoding.EncodeToString(pubKeyBytes[32:]) - actualFingerprint = regexp.MustCompile(`=*$`).ReplaceAllString(actualFingerprint, "") - - if actualFingerprint != expectedFingerprint { - t.Errorf("Expected fingerprint %s, got %s", expectedFingerprint, actualFingerprint) - } -} - -func TestAccTorRelayIdentityEd25519TorFormat(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, - Steps: []resource.TestStep{ - { - Config: testAccTorRelayIdentityEd25519ResourceConfig, - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrSet("tor_relay_identity_ed25519.test", "private_key_tor"), - resource.TestCheckResourceAttrSet("tor_relay_identity_ed25519.test", "public_key_tor"), - resource.TestMatchResourceAttr("tor_relay_identity_ed25519.test", "private_key_tor", regexp.MustCompile(`^[A-Za-z0-9+/]+=*$`)), - resource.TestMatchResourceAttr("tor_relay_identity_ed25519.test", "public_key_tor", regexp.MustCompile(`^[A-Za-z0-9+/]+=*$`)), - ), - }, - }, - }) -} - -func TestTorEd25519FormatEncoding(t *testing.T) { - testPubKeyBase64 := "PT0gZWQyNTUxOXYxLXB1YmxpYzogdHlwZTAgPT0AAAB6UEqfT3OvqdpNfw/rbOucsc5AXRUw4lcy/SaWxruoYA==" - - pubKeyBytes, err := base64.StdEncoding.DecodeString(testPubKeyBase64) - if err != nil { - t.Fatalf("Failed to decode test public key: %v", err) - } - - if len(pubKeyBytes) != 64 { - t.Errorf("Expected public key to be 64 bytes, got %d", len(pubKeyBytes)) - } - - expectedHeader := "== ed25519v1-public: type0 ==" - actualHeader := string(pubKeyBytes[:len(expectedHeader)]) - if actualHeader != expectedHeader { - t.Errorf("Expected header %q, got %q", expectedHeader, actualHeader) - } -} - const testAccTorRelayIdentityEd25519ResourceConfig = ` resource "tor_relay_identity_ed25519" "test" {} `