From 5bf771ac9637e6a1367a767fada74e101ab6dcba Mon Sep 17 00:00:00 2001 From: Abel Luck Date: Wed, 10 Sep 2025 13:48:52 +0200 Subject: [PATCH] Fix Ed25519 fingerprint to use base64 encoded public key bytes fixes #4 --- docs/resources/relay_identity_ed25519.md | 2 +- .../tor_relay_identity_ed25519_resource.go | 22 +++++++++---------- ...or_relay_identity_ed25519_resource_test.go | 20 ++++++++++++++++- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/docs/resources/relay_identity_ed25519.md b/docs/resources/relay_identity_ed25519.md index b1662d9..d3d8f33 100644 --- a/docs/resources/relay_identity_ed25519.md +++ b/docs/resources/relay_identity_ed25519.md @@ -52,5 +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 -- `public_key_fingerprint_sha256` (String) SHA256 fingerprint of the public key in hex format +- `public_key_fingerprint_sha256` (String) Base64 encoded public key bytes (32 bytes) without padding, used as the Tor Ed25519 fingerprint - `public_key_pem` (String) Public key data in PEM (RFC 1421) format diff --git a/internal/provider/tor_relay_identity_ed25519_resource.go b/internal/provider/tor_relay_identity_ed25519_resource.go index 3a58a8f..5d42bb1 100644 --- a/internal/provider/tor_relay_identity_ed25519_resource.go +++ b/internal/provider/tor_relay_identity_ed25519_resource.go @@ -7,10 +7,11 @@ 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" @@ -76,7 +77,7 @@ func (r *TorRelayIdentityEd25519Resource) Schema(ctx context.Context, req resour }, "public_key_fingerprint_sha256": schema.StringAttribute{ Computed: true, - MarkdownDescription: "SHA256 fingerprint of the public key in hex format", + MarkdownDescription: "Base64 encoded public key bytes (32 bytes) without padding, used as the Tor Ed25519 fingerprint", PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), }, @@ -125,12 +126,12 @@ func (r *TorRelayIdentityEd25519Resource) Create(ctx context.Context, req resour } data.PublicKeyPem = types.StringValue(publicKeyPem) - // Generate SHA256 fingerprint - sha256Fingerprint := r.generateSha256Fingerprint(publicKey) - data.PublicKeyFingerprintSha256 = types.StringValue(sha256Fingerprint) + // Generate Tor Ed25519 fingerprint (base64 encoded public key bytes without padding) + ed25519Fingerprint := r.generateEd25519Fingerprint(publicKey) + data.PublicKeyFingerprintSha256 = types.StringValue(ed25519Fingerprint) - // Generate ID from SHA256 fingerprint - data.Id = types.StringValue(fmt.Sprintf("ed25519-%s", sha256Fingerprint[:16])) + // Generate ID from Ed25519 fingerprint + data.Id = types.StringValue(fmt.Sprintf("ed25519-%s", ed25519Fingerprint[:16])) tflog.Trace(ctx, "created tor relay identity Ed25519 resource") @@ -181,8 +182,7 @@ func (r *TorRelayIdentityEd25519Resource) encodePublicKeyPEM(publicKey ed25519.P return string(publicKeyPem), nil } -func (r *TorRelayIdentityEd25519Resource) generateSha256Fingerprint(publicKey ed25519.PublicKey) string { - publicKeyBytes, _ := x509.MarshalPKIXPublicKey(publicKey) - sha256Sum := sha256.Sum256(publicKeyBytes) - return fmt.Sprintf("%X", sha256Sum) +func (r *TorRelayIdentityEd25519Resource) generateEd25519Fingerprint(publicKey ed25519.PublicKey) string { + fingerprint := base64.StdEncoding.EncodeToString(publicKey) + return strings.TrimRight(fingerprint, "=") } diff --git a/internal/provider/tor_relay_identity_ed25519_resource_test.go b/internal/provider/tor_relay_identity_ed25519_resource_test.go index 77f2e07..1d19f84 100644 --- a/internal/provider/tor_relay_identity_ed25519_resource_test.go +++ b/internal/provider/tor_relay_identity_ed25519_resource_test.go @@ -4,6 +4,7 @@ package provider import ( + "encoding/base64" "regexp" "testing" @@ -27,13 +28,30 @@ 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(`^[0-9A-F]{64}$`)), + resource.TestMatchResourceAttr("tor_relay_identity_ed25519.test", "public_key_fingerprint_sha256", regexp.MustCompile(`^[A-Za-z0-9+/]{43}$`)), ), }, }, }) } +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) + } +} + const testAccTorRelayIdentityEd25519ResourceConfig = ` resource "tor_relay_identity_ed25519" "test" {} `