Ed25519 C equivalent implementation

Hi! I’m struggling to select a compact equivalent C implementation of std.crypto.sign.Ed25519 to be compatible in the sense of checking message sig.
Compiling a zig’s implementation into a static lib (event with ReleaseSmall opt) is not an option - it does not fit my constraints to my regret :frowning:
Are there any compatible C impls. around?

Have you checked Monocypher?
It is small-ish single file crypto implementation.

I’m not sure I understand what this means. If it is the same curve what problem you get with other implementation?

1 Like

Thank you! right now I’m testing it )) I tried libsodium and wolfssl, but they didn’t work… May be I do something wrong as soon as I’m not an expert in cryptography.

I have signed a “combined” message, such as

        signer.update(&optionsSlice);
        signer.update(&licenseTextHash);
        signer.update(&metaHash);
        const signature = signer.finalize();

and then I failed to verifiy sig, given pub key, signature and input slices…

UPD. Of course, zig’s implementation works as expected, by libsodium and wolfssl did not

UPD2. When I was checking monochypher’s bindings, I discovered a “footgun” (mistake) in my code, and fixing it had reveled, that libsodium implementation was also fully compatible. As for the wolfssl I lost my patience, so it remains unclear, whether it compatible or not, my gues - yes.

As I undestand, monocypher uses blake2 hashing when verifying, while zig’s implementation sha512… Am I wrong, that this will be an issue?

Ed25519 :
The crypto_ed25519_sign() and crypto_ed25519_check() functions provide Ed25519 public key signatures and verification with SHA-512 as the underlying hash function. They are interoperable with other Ed25519 implementations.

hmmm… it looks like I have missed, that there are two implementations… and SHA512 is an optional one. It looks better!

I believe you have been confused with EdDSA. They are not the same, although it’s easy to misdirect them :grin:

1 Like

The following example proves that implementations are interoperable.

const std = @import("std");
const crypto = std.crypto;
const Ed25519 = crypto.sign.Ed25519;
const SecretKey = Ed25519.SecretKey;
const KeyPair = Ed25519.KeyPair;

const mc = @cImport(
    @cInclude("monocypher-ed25519.h"),
);

pub fn main() !void {
    var seed: [KeyPair.seed_length]u8 = undefined;
    crypto.random.bytes(&seed);

    const key_pair = try KeyPair.create(seed);
    var signer = try key_pair.signer(null);

    signer.update("hello");
    signer.update(" ");
    signer.update("world");

    const signature = signer.finalize();
    const signature_bytes = signature.toBytes();

    const message: []const u8 = "hello world";
    const rc = mc.crypto_ed25519_check(
        &signature_bytes,
        &key_pair.public_key.bytes,
        message.ptr,
        message.len,
    );
    std.debug.print("verify: {s}\n", .{if (rc == 0) "valid" else "invalid"});
}
2 Likes

Great! really appreciate your time for this troubleshooting, @yataro !
Thank you!

1 Like