parent
5bf771ac96
commit
f1cccbe22b
4 changed files with 102 additions and 1 deletions
10
README.md
10
README.md
|
@ -58,6 +58,8 @@ resource "local_sensitive_file" "family_key" {
|
|||
resource "tor_relay_identity_rsa" "bridge" {}
|
||||
|
||||
resource "tor_relay_identity_ed25519" "bridge" {}
|
||||
# Note: Ed25519 keys are available in both PEM format (private_key_pem, public_key_pem)
|
||||
# and Tor's binary format (private_key_tor, public_key_tor)
|
||||
|
||||
resource "tor_obfs4_state" "bridge" {
|
||||
rsa_identity_private_key = tor_relay_identity_rsa.bridge.private_key_pem
|
||||
|
@ -86,11 +88,17 @@ output "rsa_fingerprint_hashed" {
|
|||
}
|
||||
|
||||
output "ed25519_identity_pem" {
|
||||
description = "Ed25519 identity private key for bridge configuration"
|
||||
description = "Ed25519 identity private key for bridge configuration (PEM format)"
|
||||
value = tor_relay_identity_ed25519.bridge.private_key_pem
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "ed25519_identity_tor" {
|
||||
description = "Ed25519 identity private key in Tor's binary format (base64 encoded)"
|
||||
value = tor_relay_identity_ed25519.bridge.private_key_tor
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "obfs4_state_json" {
|
||||
description = "Complete obfs4 state for bridge runtime"
|
||||
value = tor_obfs4_state.bridge.state_json
|
||||
|
|
|
@ -52,5 +52,7 @@ 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
|
||||
- `private_key_tor` (String, Sensitive) Private key data in Tor's binary format, base64 encoded
|
||||
- `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_tor` (String) Public key data in Tor's binary format, base64 encoded
|
||||
|
|
|
@ -34,6 +34,8 @@ type TorRelayIdentityEd25519ResourceModel struct {
|
|||
Algorithm types.String `tfsdk:"algorithm"`
|
||||
PrivateKeyPem types.String `tfsdk:"private_key_pem"`
|
||||
PublicKeyPem types.String `tfsdk:"public_key_pem"`
|
||||
PrivateKeyTor types.String `tfsdk:"private_key_tor"`
|
||||
PublicKeyTor types.String `tfsdk:"public_key_tor"`
|
||||
PublicKeyFingerprintSha256 types.String `tfsdk:"public_key_fingerprint_sha256"`
|
||||
}
|
||||
|
||||
|
@ -75,6 +77,21 @@ func (r *TorRelayIdentityEd25519Resource) Schema(ctx context.Context, req resour
|
|||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
"private_key_tor": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Sensitive: true,
|
||||
MarkdownDescription: "Private key data in Tor's binary format, base64 encoded",
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
"public_key_tor": schema.StringAttribute{
|
||||
Computed: true,
|
||||
MarkdownDescription: "Public key data in Tor's binary format, base64 encoded",
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
"public_key_fingerprint_sha256": schema.StringAttribute{
|
||||
Computed: true,
|
||||
MarkdownDescription: "Base64 encoded public key bytes (32 bytes) without padding, used as the Tor Ed25519 fingerprint",
|
||||
|
@ -126,6 +143,13 @@ func (r *TorRelayIdentityEd25519Resource) Create(ctx context.Context, req resour
|
|||
}
|
||||
data.PublicKeyPem = types.StringValue(publicKeyPem)
|
||||
|
||||
// Encode keys in Tor format
|
||||
privateKeyTor := r.encodeTorPrivateKey(privateKey)
|
||||
data.PrivateKeyTor = types.StringValue(privateKeyTor)
|
||||
|
||||
publicKeyTor := r.encodeTorPublicKey(publicKey)
|
||||
data.PublicKeyTor = types.StringValue(publicKeyTor)
|
||||
|
||||
// Generate Tor Ed25519 fingerprint (base64 encoded public key bytes without padding)
|
||||
ed25519Fingerprint := r.generateEd25519Fingerprint(publicKey)
|
||||
data.PublicKeyFingerprintSha256 = types.StringValue(ed25519Fingerprint)
|
||||
|
@ -186,3 +210,33 @@ func (r *TorRelayIdentityEd25519Resource) generateEd25519Fingerprint(publicKey e
|
|||
fingerprint := base64.StdEncoding.EncodeToString(publicKey)
|
||||
return strings.TrimRight(fingerprint, "=")
|
||||
}
|
||||
|
||||
func (r *TorRelayIdentityEd25519Resource) encodeTorPrivateKey(privateKey ed25519.PrivateKey) string {
|
||||
// Tor Ed25519 private key format:
|
||||
// "== ed25519v1-secret: type0 ==" + null bytes + 64 bytes key data
|
||||
header := "== ed25519v1-secret: type0 =="
|
||||
|
||||
// Create 96-byte buffer: 32 bytes header + null padding + 64 bytes key
|
||||
torKey := make([]byte, 96)
|
||||
copy(torKey, header)
|
||||
|
||||
// Copy the private key (64 bytes) starting at offset 32
|
||||
copy(torKey[32:], privateKey)
|
||||
|
||||
return base64.StdEncoding.EncodeToString(torKey)
|
||||
}
|
||||
|
||||
func (r *TorRelayIdentityEd25519Resource) encodeTorPublicKey(publicKey ed25519.PublicKey) string {
|
||||
// Tor Ed25519 public key format:
|
||||
// "== ed25519v1-public: type0 ==" + null bytes + 32 bytes key data
|
||||
header := "== ed25519v1-public: type0 =="
|
||||
|
||||
// Create 64-byte buffer: 32 bytes header + null padding + 32 bytes key
|
||||
torKey := make([]byte, 64)
|
||||
copy(torKey, header)
|
||||
|
||||
// Copy the public key (32 bytes) starting at offset 32
|
||||
copy(torKey[32:], publicKey)
|
||||
|
||||
return base64.StdEncoding.EncodeToString(torKey)
|
||||
}
|
||||
|
|
|
@ -52,6 +52,43 @@ func TestTorEd25519FingerprintGeneration(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestAccTorRelayIdentityEd25519TorFormat(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccTorRelayIdentityEd25519ResourceConfig,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("tor_relay_identity_ed25519.test", "private_key_tor"),
|
||||
resource.TestCheckResourceAttrSet("tor_relay_identity_ed25519.test", "public_key_tor"),
|
||||
resource.TestMatchResourceAttr("tor_relay_identity_ed25519.test", "private_key_tor", regexp.MustCompile(`^[A-Za-z0-9+/]+=*$`)),
|
||||
resource.TestMatchResourceAttr("tor_relay_identity_ed25519.test", "public_key_tor", regexp.MustCompile(`^[A-Za-z0-9+/]+=*$`)),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestTorEd25519FormatEncoding(t *testing.T) {
|
||||
testPubKeyBase64 := "PT0gZWQyNTUxOXYxLXB1YmxpYzogdHlwZTAgPT0AAAB6UEqfT3OvqdpNfw/rbOucsc5AXRUw4lcy/SaWxruoYA=="
|
||||
|
||||
pubKeyBytes, err := base64.StdEncoding.DecodeString(testPubKeyBase64)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to decode test public key: %v", err)
|
||||
}
|
||||
|
||||
if len(pubKeyBytes) != 64 {
|
||||
t.Errorf("Expected public key to be 64 bytes, got %d", len(pubKeyBytes))
|
||||
}
|
||||
|
||||
expectedHeader := "== ed25519v1-public: type0 =="
|
||||
actualHeader := string(pubKeyBytes[:len(expectedHeader)])
|
||||
if actualHeader != expectedHeader {
|
||||
t.Errorf("Expected header %q, got %q", expectedHeader, actualHeader)
|
||||
}
|
||||
}
|
||||
|
||||
const testAccTorRelayIdentityEd25519ResourceConfig = `
|
||||
resource "tor_relay_identity_ed25519" "test" {}
|
||||
`
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue