uploader-bot/scripts/generate_node_keys.py

74 lines
2.2 KiB
Python

#!/usr/bin/env python3
"""
Generate Ed25519 keypair for node with stable printable outputs.
Primary purpose: provide a portable alternative to OpenSSL-based generation.
If 'cryptography' is unavailable, gracefully degrade with exit code 0 but no output, so caller can fallback.
Outputs JSON to stdout on success:
{
"private_key_pem": "...",
"public_key_pem": "...",
"public_key_hex": "....",
"node_id": "node-...",
}
Exit codes:
0 - success OR graceful degrade (no output) when cryptography not available
1 - unexpected error
Note: We intentionally avoid printing anything besides the JSON on success.
"""
import sys
import json
try:
from cryptography.hazmat.primitives.asymmetric import ed25519
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
except Exception:
# cryptography not available - graceful degradation: no output, caller will fallback
sys.exit(0)
def main():
try:
# Generate Ed25519 keypair
private_key = ed25519.Ed25519PrivateKey.generate()
public_key = private_key.public_key()
private_pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption(),
).decode("utf-8")
public_pem = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
).decode("utf-8")
# Extract raw 32-byte public key
public_der = public_key.public_bytes(
encoding=serialization.Encoding.DER,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
)
# The last 32 bytes are the raw key for Ed25519
public_key_hex = public_der[-32:].hex()
node_id = f"node-{public_key_hex[:16]}"
print(json.dumps({
"private_key_pem": private_pem,
"public_key_pem": public_pem,
"public_key_hex": public_key_hex,
"node_id": node_id,
}))
return 0
except Exception:
# Do not spam stdout; caller will fallback to OpenSSL path
return 1
if __name__ == "__main__":
sys.exit(main())