From 4eadc8416e79f0d4a1b90dbdc5b6f747db2a2719 Mon Sep 17 00:00:00 2001 From: Abel Luck Date: Wed, 10 Sep 2025 13:38:05 +0200 Subject: [PATCH] Add hashed fingerprint functionality to RSA relay identity keys fixes #3 --- README.md | 5 +++ docs/resources/relay_identity_rsa.md | 6 +++ .../tor_relay_identity_rsa/resource.tf | 5 +++ .../tor_relay_identity_rsa_resource.go | 42 ++++++++++++++++--- .../tor_relay_identity_rsa_resource_test.go | 18 ++++++++ 5 files changed, 70 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3ff504d..5a7a157 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,11 @@ output "rsa_identity_pem" { sensitive = true } +output "rsa_fingerprint_hashed" { + description = "Hashed RSA fingerprint for privacy in monitoring systems" + value = tor_relay_identity_rsa.bridge.public_key_fingerprint_sha1_hashed +} + output "ed25519_identity_pem" { description = "Ed25519 identity private key for bridge configuration" value = tor_relay_identity_ed25519.bridge.private_key_pem diff --git a/docs/resources/relay_identity_rsa.md b/docs/resources/relay_identity_rsa.md index b67920d..6b1599b 100644 --- a/docs/resources/relay_identity_rsa.md +++ b/docs/resources/relay_identity_rsa.md @@ -49,6 +49,11 @@ output "public_key_fingerprint_sha256" { description = "SHA256 fingerprint of the RSA public key" value = tor_relay_identity_rsa.example.public_key_fingerprint_sha256 } + +output "public_key_fingerprint_sha1_hashed" { + description = "Hashed SHA1 fingerprint of the RSA public key for privacy in monitoring systems" + value = tor_relay_identity_rsa.example.public_key_fingerprint_sha1_hashed +} ``` @@ -60,5 +65,6 @@ output "public_key_fingerprint_sha256" { - `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_sha1` (String) SHA1 fingerprint of the public key in hex format +- `public_key_fingerprint_sha1_hashed` (String) SHA1 hash of the binary form of the SHA1 fingerprint in hex format - `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 diff --git a/examples/resources/tor_relay_identity_rsa/resource.tf b/examples/resources/tor_relay_identity_rsa/resource.tf index d88113f..76c6203 100644 --- a/examples/resources/tor_relay_identity_rsa/resource.tf +++ b/examples/resources/tor_relay_identity_rsa/resource.tf @@ -33,4 +33,9 @@ output "public_key_fingerprint_sha1" { output "public_key_fingerprint_sha256" { description = "SHA256 fingerprint of the RSA public key" value = tor_relay_identity_rsa.example.public_key_fingerprint_sha256 +} + +output "public_key_fingerprint_sha1_hashed" { + description = "Hashed SHA1 fingerprint of the RSA public key for privacy in monitoring systems" + value = tor_relay_identity_rsa.example.public_key_fingerprint_sha1_hashed } \ No newline at end of file diff --git a/internal/provider/tor_relay_identity_rsa_resource.go b/internal/provider/tor_relay_identity_rsa_resource.go index ed34264..7cc1e8b 100644 --- a/internal/provider/tor_relay_identity_rsa_resource.go +++ b/internal/provider/tor_relay_identity_rsa_resource.go @@ -10,6 +10,7 @@ import ( "crypto/sha1" "crypto/sha256" "crypto/x509" + "encoding/hex" "encoding/pem" "fmt" @@ -30,12 +31,13 @@ func NewTorRelayIdentityRsaResource() resource.Resource { type TorRelayIdentityRsaResource struct{} type TorRelayIdentityRsaResourceModel struct { - Id types.String `tfsdk:"id"` - Algorithm types.String `tfsdk:"algorithm"` - PrivateKeyPem types.String `tfsdk:"private_key_pem"` - PublicKeyPem types.String `tfsdk:"public_key_pem"` - PublicKeyFingerprintSha1 types.String `tfsdk:"public_key_fingerprint_sha1"` - PublicKeyFingerprintSha256 types.String `tfsdk:"public_key_fingerprint_sha256"` + Id types.String `tfsdk:"id"` + Algorithm types.String `tfsdk:"algorithm"` + PrivateKeyPem types.String `tfsdk:"private_key_pem"` + PublicKeyPem types.String `tfsdk:"public_key_pem"` + PublicKeyFingerprintSha1 types.String `tfsdk:"public_key_fingerprint_sha1"` + PublicKeyFingerprintSha256 types.String `tfsdk:"public_key_fingerprint_sha256"` + PublicKeyFingerprintSha1Hashed types.String `tfsdk:"public_key_fingerprint_sha1_hashed"` } func (r *TorRelayIdentityRsaResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { @@ -90,6 +92,13 @@ func (r *TorRelayIdentityRsaResource) Schema(ctx context.Context, req resource.S stringplanmodifier.UseStateForUnknown(), }, }, + "public_key_fingerprint_sha1_hashed": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "SHA1 hash of the binary form of the SHA1 fingerprint in hex format", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, }, } } @@ -146,6 +155,15 @@ func (r *TorRelayIdentityRsaResource) Create(ctx context.Context, req resource.C data.PublicKeyFingerprintSha1 = types.StringValue(sha1Fingerprint) data.PublicKeyFingerprintSha256 = types.StringValue(sha256Fingerprint) + // Generate hashed fingerprint + hashedFingerprint, err := r.generateHashedFingerprint(sha1Fingerprint) + if err != nil { + resp.Diagnostics.AddError("Hashed Fingerprint Generation Error", + fmt.Sprintf("Unable to generate hashed fingerprint: %s", err)) + return + } + data.PublicKeyFingerprintSha1Hashed = types.StringValue(hashedFingerprint) + // Generate ID from SHA1 fingerprint data.Id = types.StringValue(fmt.Sprintf("rsa-%s", sha1Fingerprint[:16])) @@ -206,3 +224,15 @@ func (r *TorRelayIdentityRsaResource) generateFingerprints(publicKey *rsa.Public return sha1Fingerprint, sha256Fingerprint, nil } + +func (r *TorRelayIdentityRsaResource) generateHashedFingerprint(fingerprint string) (string, error) { + fingerprintBytes, err := hex.DecodeString(fingerprint) + if err != nil { + return "", fmt.Errorf("failed to decode fingerprint hex: %w", err) + } + + hashedSum := sha1.Sum(fingerprintBytes) + hashedFingerprint := fmt.Sprintf("%X", hashedSum) + + return hashedFingerprint, nil +} diff --git a/internal/provider/tor_relay_identity_rsa_resource_test.go b/internal/provider/tor_relay_identity_rsa_resource_test.go index 757615f..58da2b9 100644 --- a/internal/provider/tor_relay_identity_rsa_resource_test.go +++ b/internal/provider/tor_relay_identity_rsa_resource_test.go @@ -27,11 +27,13 @@ func TestAccTorRelayIdentityRsaResource(t *testing.T) { resource.TestCheckResourceAttrSet("tor_relay_identity_rsa.test", "public_key_pem"), resource.TestCheckResourceAttrSet("tor_relay_identity_rsa.test", "public_key_fingerprint_sha1"), resource.TestCheckResourceAttrSet("tor_relay_identity_rsa.test", "public_key_fingerprint_sha256"), + resource.TestCheckResourceAttrSet("tor_relay_identity_rsa.test", "public_key_fingerprint_sha1_hashed"), // Verify PEM format resource.TestMatchResourceAttr("tor_relay_identity_rsa.test", "private_key_pem", regexp.MustCompile(`^-----BEGIN RSA PRIVATE KEY-----`)), resource.TestMatchResourceAttr("tor_relay_identity_rsa.test", "public_key_pem", regexp.MustCompile(`^-----BEGIN PUBLIC KEY-----`)), resource.TestMatchResourceAttr("tor_relay_identity_rsa.test", "public_key_fingerprint_sha1", regexp.MustCompile(`^[0-9A-F]{40}$`)), resource.TestMatchResourceAttr("tor_relay_identity_rsa.test", "public_key_fingerprint_sha256", regexp.MustCompile(`^[0-9A-F]{64}$`)), + resource.TestMatchResourceAttr("tor_relay_identity_rsa.test", "public_key_fingerprint_sha1_hashed", regexp.MustCompile(`^[0-9A-F]{40}$`)), ), }, }, @@ -79,6 +81,22 @@ OhEqFiIYW5mI//JWsqSZZxy4nMqgejKkrRgOOQbL0NE= } } +func TestHashedFingerprintGeneration(t *testing.T) { + testFingerprint := "DA5CEC632A9A544394403BD533E1A7BDE2F26EDD" + expectedHashedFingerprint := "922650D27357BE307B3B322A5ABC3E9585AF776F" + + resource := &TorRelayIdentityRsaResource{} + hashedFingerprint, err := resource.generateHashedFingerprint(testFingerprint) + if err != nil { + t.Fatalf("failed to generate hashed fingerprint: %v", err) + } + + if hashedFingerprint != expectedHashedFingerprint { + t.Errorf("Hashed fingerprint mismatch:\nExpected: %s\nActual: %s", + expectedHashedFingerprint, hashedFingerprint) + } +} + const testAccTorRelayIdentityRsaResourceConfig = ` resource "tor_relay_identity_rsa" "test" {} `