Ed448 Keys (PEM, DER, and Raw, with PKCS8 and OpenSSH)[OpenSSH Home][Home]
X448 is based on Curve 448, and is used for key exchange with ECDH (Elliptic Curve Diffie Hellman). It supports a 224-bit security level, and where we use a 448-bit (56-byte) prime number of \(P = 2^{448} - 2^{224} - 1\). It has improved security over Curve 25519, and which has a 255-bit prime number (\(P = 2^{255} - 19\)). As with X25519, in X448 we use a Montgomery curve (\(v^2 = u^3 + A \times u^2 + u\)) with scalar multiplication. In X448, we use 56-byte string values, rather than 32-byte values for X25519. Overall, X448 uses a little-endian method to store an array of bytes, and has a value of \(A = 39,081\). The base point \(G\) is at (5, 355293926785 ... 42774147268105838985595290606362) and the paramaters based by on RFC 7748 [here].
|
Method
X448 is based on Curve 448, and is used for key exchange with ECDH (Elliptic Curve Diffie Hellman). It supports a 224-bit security level, and where we use a 448-bit (56-byte) prime number of \(P = 2^{448} - 2^{224} - 1\). It has improved security over Curve 25519, and which has a 255-bit prime number (\(P = 2^{255} - 19\)). As with X25519, in X448 we use a Montgomery curve (\(v^2 = u^3 + A \times u^2 + u\)) with scalar multiplication. In X448, we use 56-byte string values, rather than 32-byte values for X25519. Overall, X448 uses a little-endian method to store an array of bytes, and has a value of \(A = 39,081\). The base point \(G\) is at (5, 355293926785 ... 42774147268105838985595290606362) and the paramaters based by on RFC 7748 [here]. The private key a random scalar value (\(a\)), and the public key is:
\(Pk=a.G\)
and where \(G\) is the base point on the curve. This is normally an (\(x,y\)) point, but in Curve 25519 we only need to store the \(x\) value. Thus the private key is the same size as the public key.
If we try the raw format, we get:
Private key encoding: Encoding.Raw Private key format: PrivateFormat.Raw Public key encoding: Encoding.Raw Public key format: PublicFormat.Raw Private key: 79463db2792ceb727c7706d4bed9af3b9cbc89f60424947c222ebe72e43cce11290eb015286ff8b484d8903b7f08ba61900a83139de634d5f9 Public key: aed4d48fb2f01bc11ebedfe554e7eb4cac3de2c95f9a4c8f2297406538175d8c6e829b9c3760587a8b5c0c838bc8d20a0ab5c0a83382d26200
We see we have 114 hex characters which is 456 bits.
With PEM format we have:
Private key encoding: Encoding.PEM Private key format: PrivateFormat.PKCS8 Public key encoding: Encoding.PEM Public key format: PublicFormat.SubjectPublicKeyInfo Private key: -----BEGIN PRIVATE KEY----- MEcCAQAwBQYDK2VxBDsEOf5ArReYqDUmfaAYadsPWJXYFX8DVaWlZ09yBf5COv/K /5jNI0w/o4gmLLAOvZiLXEqCyNrc0yOvWg== -----END PRIVATE KEY----- Public key: -----BEGIN PUBLIC KEY----- MEMwBQYDK2VxAzoAGKnn41FsRqaKH+V/i46ZM0JKsK/P26S+zFUeSK2Ru9Gvfge0 DntcCDon8WHTdpPLwPF6M7hkRMWA -----END PUBLIC KEY-----
Coding
The code we can use is:
from cryptography.hazmat.primitives import serialization as crypto_serialization from cryptography.hazmat.primitives.asymmetric.ed448 import Ed448PrivateKey from cryptography.hazmat.backends import default_backend as crypto_default_backend import binascii import sys private_key_encoding= crypto_serialization.Encoding.PEM private_key_format= crypto_serialization.PrivateFormat.OpenSSH public_key_format= crypto_serialization.PublicFormat.OpenSSH # PEM or DER public_key_encode=0 public_key_form=0 private_key_encode=0 private_key_form=0 if (len(sys.argv)>1): private_key_encode=int(sys.argv[1]) if (len(sys.argv)>2): private_key_form=int(sys.argv[2]) if (len(sys.argv)>3): public_key_encode=int(sys.argv[3]) if (len(sys.argv)>4): public_key_form=int(sys.argv[4]) if (private_key_encode==0): private_key_encoding= crypto_serialization.Encoding.PEM elif (private_key_encode==1): private_key_encoding= crypto_serialization.Encoding.DER else: private_key_encoding= crypto_serialization.Encoding.Raw if (private_key_form==0): private_key_format= crypto_serialization.PrivateFormat.PKCS8 elif (private_key_form==1): private_key_format= crypto_serialization.PrivateFormat.OpenSSH else: private_key_format= crypto_serialization.PrivateFormat.Raw if (public_key_encode==0): public_key_encoding= crypto_serialization.Encoding.PEM elif (public_key_encode==1): public_key_encoding= crypto_serialization.Encoding.DER elif (public_key_encode==2): public_key_encoding= crypto_serialization.Encoding.OpenSSH else: public_key_encoding= crypto_serialization.Encoding.Raw if (public_key_form==0): public_key_format= crypto_serialization.PublicFormat.SubjectPublicKeyInfo elif (public_key_form==1): public_key_format= crypto_serialization.PublicFormat.OpenSSH else: public_key_format= crypto_serialization.PublicFormat.Raw # public_key_encoding= crypto_serialization.Encoding.OpenSSH # TraditionalOpenSSL, PKCS8, Raw, OpenSSH, PKCS12 # public_key_format = crypto_serialization.PublicFormat.PKCS1 # PKCS1, OpenSSH, Raw, SubjectPublicKeyInfo key=Ed448PrivateKey.generate() try: print("Private key encoding:\t",private_key_encoding) print("Private key format:\t",private_key_format) print("Public key encoding:\t",public_key_encoding) print("Public key format:\t",public_key_format) private_key = key.private_bytes(private_key_encoding,private_key_format,crypto_serialization.NoEncryption()) if (private_key_encoding== crypto_serialization.Encoding.DER or private_key_encoding== crypto_serialization.Encoding.Raw): print(f"\nPrivate key:\n{binascii.b2a_hex(private_key).decode()}") else: print(f"\nPrivate key:\n{private_key.decode()}") except Exception as e: print("Private key error: ",e) try: public_key = key.public_key().public_bytes(public_key_encoding,public_key_format) if (public_key_encoding== crypto_serialization.Encoding.DER or public_key_encoding== crypto_serialization.Encoding.Raw): print(f"\nPublic key:\n{binascii.b2a_hex(public_key).decode()}") else: print(f"\nPublic key:\n{public_key.decode()}") except Exception as e: print("Public key error: ",e)