Fix incorrect RSA identity key fingerprint generation

The fingerprint calculation was using PKIX encoding instead of the
required PKCS1 DER encoding for RSA public keys. This affected both
the relay identity resource and obfs4 node ID derivation.

- Use x509.MarshalPKCS1PublicKey instead of x509.MarshalPKIXPublicKey
- Add test case with known fingerprint vector to prevent regression
- Update both generateFingerprints and deriveNodeIdFromRsaKey functions

fixes #2
This commit is contained in:
Abel Luck 2025-09-10 13:01:01 +02:00
parent 005634ff1c
commit 83df31ec80
5 changed files with 45 additions and 12 deletions

View file

@ -31,7 +31,6 @@ func TestTorObfs4BridgeLineDataSource(t *testing.T) {
// Check computed values are generated
resource.TestCheckResourceAttrSet("data.tor_obfs4_bridge_line.test", "bridge_line"),
// Check bridge line format
resource.TestMatchResourceAttr("data.tor_obfs4_bridge_line.test", "bridge_line",
regexp.MustCompile(`^Bridge obfs4 192\.0\.2\.1:443 [0-9a-f]{40} cert=[A-Za-z0-9+/]+ iat-mode=[0-2]$`)),

View file

@ -406,11 +406,7 @@ func (r *TorObfs4StateResource) deriveNodeIdFromRsaKey(rsaPrivateKeyPem string)
return nil, fmt.Errorf("failed to parse RSA private key: %w", err)
}
// Extract the public key and encode it
publicKeyBytes, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey)
if err != nil {
return nil, fmt.Errorf("failed to marshal public key: %w", err)
}
publicKeyBytes := x509.MarshalPKCS1PublicKey(&privateKey.PublicKey)
// Generate SHA1 hash of public key (this is the relay fingerprint/node ID)
hash := sha1.Sum(publicKeyBytes)

View file

@ -27,7 +27,6 @@ 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-----`)),
// Verify fingerprint format (64 hex characters for SHA256)
resource.TestMatchResourceAttr("tor_relay_identity_ed25519.test", "public_key_fingerprint_sha256", regexp.MustCompile(`^[0-9a-f]{64}$`)),
),
},

View file

@ -196,10 +196,7 @@ func (r *TorRelayIdentityRsaResource) encodePublicKeyPEM(publicKey *rsa.PublicKe
}
func (r *TorRelayIdentityRsaResource) generateFingerprints(publicKey *rsa.PublicKey) (string, string, error) {
publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey)
if err != nil {
return "", "", err
}
publicKeyBytes := x509.MarshalPKCS1PublicKey(publicKey)
sha1Sum := sha1.Sum(publicKeyBytes)
sha256Sum := sha256.Sum256(publicKeyBytes)

View file

@ -4,6 +4,8 @@
package provider
import (
"crypto/x509"
"encoding/pem"
"regexp"
"testing"
@ -28,7 +30,6 @@ func TestAccTorRelayIdentityRsaResource(t *testing.T) {
// 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-----`)),
// Verify fingerprint formats (hex strings)
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}$`)),
),
@ -37,6 +38,47 @@ func TestAccTorRelayIdentityRsaResource(t *testing.T) {
})
}
func TestRsaFingerprintGeneration(t *testing.T) {
testRsaPrivateKeyPem := `-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDxZrR1gsB00rE5Rift0y35j/F5Jt03ExlW0H7fUI+4W2MyZ+pE
0CM512o1tshfpDVxtVrnJuI8dFAVgO9Ct7aEKYTNk++g9llF+Q0M4nIrKftv/4Fo
MlFHwa+C41LGiucauaagiatiESNWNK3FhOLx1Who9duUN5SVVFyXpApPrQIDAQAB
AoGAJya2G9zh49CMB7L2JN88NJ6A1lpURGtnj6nu+b7yID9KHlG2MATlwarLQfzs
EH7sYA2+uYCX7qAaoPIxW8u54PLKbgVD3Kh7eXBO0isIx4FPMHD4khv5CLiNVopI
VwStg9Fv3FZ6h2+2FTVfLK4+xuwfyoShUwfEp3eV7c8YRSECQQD1ntdJrokM2zWR
hf+Cl/L62tl58hSYVZoSrR+b0cTQlbN1rnYTvY+1jXbBP8fKFBoAHKZ1xMCY/m67
H7qt+nalAkEA+5o4GQb6YCHYJZ/lhGQFaSGnWnKE16MsW1xLimQY8gOMbg3AYcXk
B8fylmp/XpNG1/PC+M6m5C0DjI85eKYuaQJBAJ2eOPmHj1s4sL+aBcWATOS93CFt
P9oh1KV3g3kyu+I+rtMuCYfRdY9EIJkSnNsI20aHHCsm/5EudVCPo/RRbiECQEQu
psUhfvhOM6T+j9QwxsaWuCNqpVVKgtq/SDlYpunuzD+GunvEhOcW6Eaa1alrf+dF
x7BlUBTFnhCZP5nSbwECQGgUr7jW/xrwbkDAP3+ql6o0yyhLMtvIqAKk3fUWxPXO
OhEqFiIYW5mI//JWsqSZZxy4nMqgejKkrRgOOQbL0NE=
-----END RSA PRIVATE KEY-----`
expectedSha1Fingerprint := "da5cec632a9a544394403bd533e1a7bde2f26edd"
block, _ := pem.Decode([]byte(testRsaPrivateKeyPem))
if block == nil {
t.Fatal("failed to decode PEM block")
}
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
t.Fatalf("failed to parse RSA private key: %v", err)
}
resource := &TorRelayIdentityRsaResource{}
sha1Fingerprint, _, err := resource.generateFingerprints(&privateKey.PublicKey)
if err != nil {
t.Fatalf("failed to generate fingerprint: %v", err)
}
if sha1Fingerprint != expectedSha1Fingerprint {
t.Errorf("SHA1 fingerprint mismatch:\nExpected: %s\nActual: %s",
expectedSha1Fingerprint, sha1Fingerprint)
}
}
const testAccTorRelayIdentityRsaResourceConfig = `
resource "tor_relay_identity_rsa" "test" {}
`