Fix Ed25519 fingerprint to use base64 encoded public key bytes

fixes #4
This commit is contained in:
Abel Luck 2025-09-10 13:48:52 +02:00
parent b406226f0f
commit 5bf771ac96
3 changed files with 31 additions and 13 deletions

View file

@ -52,5 +52,5 @@ output "public_key_fingerprint_sha256" {
- `algorithm` (String) Name of the algorithm used when generating the private key (always 'Ed25519') - `algorithm` (String) Name of the algorithm used when generating the private key (always 'Ed25519')
- `id` (String) Unique identifier based on public key fingerprint - `id` (String) Unique identifier based on public key fingerprint
- `private_key_pem` (String, Sensitive) Private key data in PEM (RFC 1421) format - `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 - `public_key_pem` (String) Public key data in PEM (RFC 1421) format

View file

@ -7,10 +7,11 @@ import (
"context" "context"
"crypto/ed25519" "crypto/ed25519"
"crypto/rand" "crypto/rand"
"crypto/sha256"
"crypto/x509" "crypto/x509"
"encoding/base64"
"encoding/pem" "encoding/pem"
"fmt" "fmt"
"strings"
"github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema" "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{ "public_key_fingerprint_sha256": schema.StringAttribute{
Computed: true, 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{ PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(), stringplanmodifier.UseStateForUnknown(),
}, },
@ -125,12 +126,12 @@ func (r *TorRelayIdentityEd25519Resource) Create(ctx context.Context, req resour
} }
data.PublicKeyPem = types.StringValue(publicKeyPem) data.PublicKeyPem = types.StringValue(publicKeyPem)
// Generate SHA256 fingerprint // Generate Tor Ed25519 fingerprint (base64 encoded public key bytes without padding)
sha256Fingerprint := r.generateSha256Fingerprint(publicKey) ed25519Fingerprint := r.generateEd25519Fingerprint(publicKey)
data.PublicKeyFingerprintSha256 = types.StringValue(sha256Fingerprint) data.PublicKeyFingerprintSha256 = types.StringValue(ed25519Fingerprint)
// Generate ID from SHA256 fingerprint // Generate ID from Ed25519 fingerprint
data.Id = types.StringValue(fmt.Sprintf("ed25519-%s", sha256Fingerprint[:16])) data.Id = types.StringValue(fmt.Sprintf("ed25519-%s", ed25519Fingerprint[:16]))
tflog.Trace(ctx, "created tor relay identity Ed25519 resource") tflog.Trace(ctx, "created tor relay identity Ed25519 resource")
@ -181,8 +182,7 @@ func (r *TorRelayIdentityEd25519Resource) encodePublicKeyPEM(publicKey ed25519.P
return string(publicKeyPem), nil return string(publicKeyPem), nil
} }
func (r *TorRelayIdentityEd25519Resource) generateSha256Fingerprint(publicKey ed25519.PublicKey) string { func (r *TorRelayIdentityEd25519Resource) generateEd25519Fingerprint(publicKey ed25519.PublicKey) string {
publicKeyBytes, _ := x509.MarshalPKIXPublicKey(publicKey) fingerprint := base64.StdEncoding.EncodeToString(publicKey)
sha256Sum := sha256.Sum256(publicKeyBytes) return strings.TrimRight(fingerprint, "=")
return fmt.Sprintf("%X", sha256Sum)
} }

View file

@ -4,6 +4,7 @@
package provider package provider
import ( import (
"encoding/base64"
"regexp" "regexp"
"testing" "testing"
@ -27,13 +28,30 @@ func TestAccTorRelayIdentityEd25519Resource(t *testing.T) {
// Verify PEM format // 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", "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_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 = ` const testAccTorRelayIdentityEd25519ResourceConfig = `
resource "tor_relay_identity_ed25519" "test" {} resource "tor_relay_identity_ed25519" "test" {}
` `