Ed25519 - Edwards-curve Digital Signature Algorithm (EdDSA) - uses Curve 25519 and SHA-512 to produce an EdDSA signature. In this case we will perform the core operations in the signing and verification. With this we calculate the hash of the message (\(h\)) and have a public key (\(pk\)) and a private key (\(sk\)). We then generate a signature of (\(R,s\)). The public key (\(pk\)) and (\(R,s\)) is then used to check the signature. It is standardized an RFC [RFC 8032] and is based on the Schnorr's signature scheme and uses (possibly twisted) Edwards curves. For Ed25519 we use 32 byte values for both the private key and the public key, and 64 bytes for the signature. Overall, SHA512 is used as the hashing method for the signature.
Cryptography Next Generation (CNG) for EdDSA with .NET and C# |
Theory
With EdDSA, Alice will sign a message with her private key, and then Bob will use her public key to verify that she signed the message (and that the message has now changed):
Key generation
Alice generates a random 32-byte secret key (\(sk\)) and then creates a public key of:
\(pk=sk \cdot G\)
and where \(G\) is the base point of the curve.
Signing
Alice creates a SHA-512 hash of her private key:
\(h=\textrm{HASH}(sk)\)
Create \(r\) from the upper 32 bytes of hash and the message:
\( r = \textrm{HASH}(h[32:] \: || \: m))\)
And where "||" represents a concatenation of the byte array values. Next she matches \(r\) onto curve with:
\(R=r \cdot G\)
Next Alice computes \(s\) with:
\(s=r + (\textrm{HASH}(R \: || \: pk \: || \: m)) \cdot sk\)
The signature is (\(R,s\)). The values of \(R\) and \(s\) are 32 bytes long, and thus the signature is 64 bytes long.
Verifying
Bob creates \(S\) using \(R\), \(pk\) and \(m\):
\(S =\textrm{HASH}(R \: || \: pk \: || \: m) \)
And next creates two verification values:
\(v_1=s \cdot G\)
\(v_2=R+ pk \cdot S\)
If \(v_1==v_2\) the signature checks. This is because:
\( \begin{align} v_1&=s.G\\ &= (r + (\textrm{HASH}(R \: || \: pk \: || \: m)) \cdot sk) \cdot G \\ &= rG + sk \cdot G \cdot (\textrm{HASH}(R \: || \: pk \: || \: m)) \\ &= R+ pk \cdot S \\ &= v_2 \end{align} \)
Coding
The coding is:
namespace CngEdDCSA { using System.Security.Cryptography; using System.Text; class Program { static void Main(string[] args) { var message="Hello"; var hash="SHA256"; try { if (args.Length >0) message=args[0]; if (args.Length >1) hash=args[1]; var msg=Encoding.ASCII.GetBytes(message); var keyCreationParameters = new CngKeyCreationParameters(); keyCreationParameters.ExportPolicy = CngExportPolicies.AllowExport; keyCreationParameters.KeyUsage = CngKeyUsages.Signing; keyCreationParameters.ExportPolicy = CngExportPolicies.AllowPlaintextExport; /// Note if we change "null" to "AliceKeyPair" it would provide us with a named key and which is persistent. var CngPrivateKey = CngKey.Create(CngAlgorithm.ECDsaP256,null, keyCreationParameters); var alice = new ECDsaCng(CngPrivateKey); var alicePrivate = alice.Key.Export(CngKeyBlobFormat.EccPrivateBlob); var h = SHA256.HashData(msg); alice.HashAlgorithm = CngAlgorithm.Sha256; if (hash=="MD5") { h = MD5.HashData(msg); alice.HashAlgorithm = CngAlgorithm.MD5;} if (hash=="SHA1") { h = SHA1.HashData(msg); alice.HashAlgorithm = CngAlgorithm.Sha1;} if (hash=="SHA384") { h = SHA384.HashData(msg); alice.HashAlgorithm = CngAlgorithm.Sha384;} if (hash=="SHA512"){ h = SHA512.HashData(msg); alice.HashAlgorithm = CngAlgorithm.Sha512;} var signature = alice.SignHash(h); var alicePublic = alice.Key.Export(CngKeyBlobFormat.EccPublicBlob); var bob = new ECDsaCng(CngKey.Import(alicePublic,CngKeyBlobFormat.EccPublicBlob)); var rtn=bob.VerifyHash(h,signature); Console.WriteLine("Message:\t\t{0}",message); Console.WriteLine("Hash method:\t\t{0}",hash); Console.WriteLine("Hash:\t\t{0}",Convert.ToHexString(h)); Console.WriteLine("\nSignature:\t{0}",Convert.ToHexString(signature)); Console.WriteLine("\nVerified:\t{0}",rtn); Console.WriteLine("\nPrivate key: {0}",Convert.ToHexString(alicePrivate)); Console.WriteLine("\nPrivate key (PEM):\n{0}",alice.ExportECPrivateKeyPem()); Console.WriteLine("\nPrivate key (DER):\n{0}",Convert.ToHexString(alice.ExportPkcs8PrivateKey())); Console.WriteLine("\nPublic key: {0}",Convert.ToHexString(alicePublic)); Console.WriteLine("\nPublic key (DER):\n{0}",Convert.ToHexString(alice.ExportPkcs8PrivateKey())); Console.WriteLine("Public key (PEM):\n{0}",alice.ExportSubjectPublicKeyInfoPem()); } catch (Exception e) { Console.WriteLine("Error: {0}",e.Message); } } } }
A sample test run is::
Message: Hello 123 Hash method: SHA256 Hash: 859E38D581E214DC7C8C871C425642913363A829065CF4ACDDD120ED5391B04B Signature: E21B354399FA0B8304D79445963DF6458D4BD54FBFE5E04408056FD3D8C450E9F08CCD978353F76E05D090B6FDA92BA395FC7B390E81E5835D5146F9678F4A19 Verified: True Private key: 454353322000000082B2AE069549C12A5CAF7A83BA3E25C79EB03E92D6BBE0CBE41B2069D3DB0D16ABCC386EA00F1D9186640FCADE8BA769E22AE0B7D0DC664FFEE13417859F32E8A38F6F4DD60103DEE49A76745220536764736C4D870C5D97B5E72B0E9B9B713E Private key (PEM): -----BEGIN EC PRIVATE KEY----- MHcCAQEEIKOPb03WAQPe5Jp2dFIgU2dkc2xNhwxdl7XnKw6bm3E+oAoGCCqGSM49 AwEHoUQDQgAEgrKuBpVJwSpcr3qDuj4lx56wPpLWu+DL5BsgadPbDRarzDhuoA8d kYZkD8rei6dp4irgt9DcZk/+4TQXhZ8y6A== -----END EC PRIVATE KEY----- Private key (DER): 3081A2020100301306072A8648CE3D020106082A8648CE3D030107047930770201010420A38F6F4DD60103DEE49A76745220536764736C4D870C5D97B5E72B0E9B9B713EA00A06082A8648CE3D030107A1440342000482B2AE069549C12A5CAF7A83BA3E25C79EB03E92D6BBE0CBE41B2069D3DB0D16ABCC386EA00F1D9186640FCADE8BA769E22AE0B7D0DC664FFEE13417859F32E8A00D300B0603551D0F310403020080 Public key: 454353312000000082B2AE069549C12A5CAF7A83BA3E25C79EB03E92D6BBE0CBE41B2069D3DB0D16ABCC386EA00F1D9186640FCADE8BA769E22AE0B7D0DC664FFEE13417859F32E8 Public key (DER): 3081A2020100301306072A8648CE3D020106082A8648CE3D030107047930770201010420A38F6F4DD60103DEE49A76745220536764736C4D870C5D97B5E72B0E9B9B713EA00A06082A8648CE3D030107A1440342000482B2AE069549C12A5CAF7A83BA3E25C79EB03E92D6BBE0CBE41B2069D3DB0D16ABCC386EA00F1D9186640FCADE8BA769E22AE0B7D0DC664FFEE13417859F32E8A00D300B0603551D0F310403020080 Public key (PEM): -----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgrKuBpVJwSpcr3qDuj4lx56wPpLW u+DL5BsgadPbDRarzDhuoA8dkYZkD8rei6dp4irgt9DcZk/+4TQXhZ8y6A== -----END PUBLIC KEY-----
We can see that the signature has 64 bytes, and where the blob format encapsulates the keys.