Skip to content

Ed Algorithms in Dotnet similar way

Aleksandar Gyonov edited this page Feb 16, 2024 · 23 revisions

Ed Algorithms in .NET similar way

The goal is to implement the algorithms for Ed25519, Ed448, X25519 and X488 in a .NET similar way. The issue is that these algorithms, currently, are not implemented in .NET "standard" libraries. It is understandable as the "standard" .NET approach is to rely on specific/basic cryptographic libraries, provided by the underlying operating system. Such as:

  • CNG for Windows
  • OpenSSL for Linux
  • on Mac OS I am not sure what is used lately...

As a side note - OpenSSL does support Ed algorithms, but this support have not yet been ported to .NET.

There are also other libraries that provide support for Ed algorithms in .NET, such as:

  • BouncyCastle
  • libsodium based wrappers

But, they do implement Ed stuff in a API-wise way different from what is the approach in mainstream .NET. So, I have decided to implement the algorithms in a similar way to what is done in .NET.

It must be said that the core implementation of the cryptographic algorithms, in current project, comes from Bouncy Castle project. I have just added some facade classes to adjust the API surface to be more .NET way similar. This is done deliberately, based on the following:

  1. Implementation of the cryptographic algorithms is not trivial. It is better to rely on well tested and proven code.
  2. Besides, it is not a matter to just implement the core algorithm logic, but also to maintain it. This, also, is not a trivial task in terms of organization and resources.
  3. Putting several classes, as a facade, in front of the core implementation allows to achieve the goal of having the API surface similar to the one in .NET and to have the core implementation separated from the facade, but still be a well developed and maintained.

I do believe the above is a good, balanced approach.

And, eventually when support for Ed25519, Ed448, X25519 and X448, comes to standard .NET, the dependency on Bouncy Castle can be removed without affecting seriously the client logic that uses the new API.

JWK, JWS, jAdES

Also, I have extended the JWS and JAdES classes from the CryptoEx library to support the Ed algorithms. You can read another Wiki page about this - EdDSA for JWS and jAdES.

Some class diagrams

The main classes that implement the facade for algorithms may be seen as:

---
title: Classes for Ed Algorithms in .NET similar way
---
classDiagram
    direction TD
    class AsymmetricAlgorithm { 
        <<Abstract>>
        This is standard .NET class
    }
    class EDAlgorithm {
        <<Abstract>>
        This is base class for Ed algorithms [new, facade]
    }
    
    class EdDH {
        This is class for X25519 and X448 key exchange [new, facade]
    }
    class EdDsa {
        This is class for Ed25519 and Ed448 digital signatures [new, facade]
    }
    AsymmetricAlgorithm <|-- EDAlgorithm
    EDAlgorithm <|-- EdDH
    EDAlgorithm <|-- EdDsa

They closely resemble the classes in .NET for Elliptic-Curves:

---
title: Classes for ECC Algorithms in .NET
---
classDiagram
    direction TD
    class AsymmetricAlgorithm { 
        <<Abstract>>
        This is standard .NET class
    }
    class ECAlgorithm {
        <<Abstract>>
        This is base class for ECC algorithms (standard .NET)
    }
    
    class ECDiffieHellman {
        This is class for Elliptic Curve Diffie-Hellman key exchange [standard .NET]
    }
    class ECDsa {
        This is class for Elliptic Curve Digital Signature Algorithm [standard .NET]
    }
    AsymmetricAlgorithm <|-- ECAlgorithm
    ECAlgorithm <|-- ECDiffieHellman
    ECAlgorithm <|-- ECDsa

The methods in the newly facade classes override or are similar to the methods in the .NET classes for ECC.

Methods

Auxiliary methods

There is a bunch of auxiliary methods to import / export public / private keys from and to PEM and DER encoded formats. There are couple of methods to load key material from X509 Certificates and PFX files.

Please, refer to the source code of the test project for examples, how to use them. These test methods may be found in classes CryptoEx.Tests.TestEdDsa and CryptoEx.Tests.TestEdDH_ImportExport.

Key generation

As HOW-TO for key generation, please, refer to the source code of the test project. The test methods may be found in class CryptoEx.Tests.TestEdDH. Basically it may be something like:

// Define some shared context and text
byte[] context = Encoding.UTF8.GetBytes("Shared context bytes");
string palinText = "This is a test string to encrypt and decrypt";

// ================= Alice's side =================
// Create some asymetric keys
EdDH alice = EdDH.Create(EdAlgorithm.X25519);

// Here Alice shall send her public key to Bob
// Export it to JWK, PEM or DER format
// and send it to Bob via some media / network.

// ================= Bob's side =================

// Define buffers
byte[] cypher = new byte[palinText.Length];
byte[] tag = new byte[16];

// Create some asymetric keys
EdDH bob = EdDH.Create(EdAlgorithm.X25519);

// Define symmetric keys & nonce values buffer
byte[] bobsKey = new byte[44];

// Generate Bob's simetric key - using Alice's public key
int bobGet = bob.GenerateBytesKDM(alice, SHA256.Create(), context, bobsKey);

// Create AES
AesGcm aesBob = new(bobsKey[..32]);

// Encrypt
aesBob.Encrypt(bobsKey[32..], Encoding.UTF8.GetBytes(palinText), cypher, tag);

// Here Bob shall send the cypher, tag and his public key to Alice 
// Export it to JWK, PEM or DER format. Pack it together with cypher and tag
// and send it to Alice via some media / network.

// ================= Alice's side =================

// Define buffers
byte[] decryptResult = new byte[cypher.Length];

// Define symmetric keys & nonce values buffer
byte[] aliceKey = new byte[44];

// Generate Alice's simetric key - using Bob's public key
int aliceGen = alice.GenerateBytesKDM(bob, SHA256.Create(), context, aliceKey);

// Create AES
AesGcm aesAlice = new(aliceKey[..32]);

// Decrypt
aesAlice.Decrypt(aliceKey[32..], cypher, tag, decryptResult);

Major steps are the same as Elliptic-Curve Diffie-Hellman key exchange. The difference is that the EdDH class is used instead of ECDiffieHellman. You have:

  1. Alice creates her key pair and sends her public key to Bob
  2. Bob creates his key pair
  3. Bob generates the symmetric key using Alice's public key and his private key
  4. Bob encrypts the message using the symmetric key and sends the cypher, tag and his public key to Alice
  5. Alice generates the symmetric key using Bob's public key and her private key - it is the same symmetric key as Bob's
  6. Alice decrypts the message using the symmetric key

Digital signatures

As HOW-TO for digital signing and verifying, please, refer to the source code of the test project. The test methods may be found in class CryptoEx.Tests.TestEdDsa. Basically it may be something like:

// Some test message
    public const string testMessage = "This is a test";

// =============== Signing =================
// Create EdDsa
EdDsa edDsa = EdDsa.Create();

// Read private key from PEM
using (FileStream fs = File.Open(@"source\cert.pem", FileMode.Open, FileAccess.Read, FileShare.Read))
using (StreamReader sr = new(fs)) {
    // Import
    edDsa.ImportFromEncryptedPem(sr.ReadToEnd(), "pass.123");
} 

// or read private key from other sources - JWK, PFX

// Sign
byte[] signature = edDsa.Sign(System.Text.Encoding.UTF8.GetBytes(testMessage));

// =============== Verifying =================
// Create EdDsa
EdDsa edDsa = EdDsa.Create();

// Read public key from PEM
using (FileStream fs = File.Open(@"source\cert.pub", FileMode.Open, FileAccess.Read, FileShare.Read))
using (StreamReader sr = new(fs)) {
    // Import
    edDsa.ImportFromPem(sr.ReadToEnd());
}

// or read public key from other sources - JWK, X509 Certificate

// Verify
bool result = edDsa.Verify(System.Text.Encoding.UTF8.GetBytes(testMessage), signature);

Major steps are the same as Elliptic-Curve Digital Signature Algorithm. The difference is that the EdDsa class is used instead of ECDsa. You have:

  1. Create some EdDsa object
  2. Read the private key from PEM, JWK, PFX or other source
  3. Sign the message
  4. For Verifing:
    1. Create some EdDsa object
    2. Read the public key from PEM, JWK, X509 Certificate or other source
    3. Verify the message - using the signature and original message

Next - JWS, JWK and JAdES

In the current project CryptoEx.Ed I have also extended the logic from the core library CryptoEx, related to JSON signatures, to support EdDSA. So if you are interested in these topics, please, refer to the next Wiki page.