Add hashed fingerprint functionality to RSA relay identity keys

fixes #3
This commit is contained in:
Abel Luck 2025-09-10 13:38:05 +02:00
parent 62b243c8e4
commit 4eadc8416e
5 changed files with 70 additions and 6 deletions

View file

@ -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

View file

@ -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
}
```
<!-- schema generated by tfplugindocs -->
@ -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

View file

@ -34,3 +34,8 @@ 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
}

View file

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

View file

@ -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" {}
`