Score:2

Generating and validating a signature with ED25519 expanded private key

jp flag

I am building a encrypted messaging app over tor network and currently I'm struggling on using tor generated ed25519 private key to sign and verify any message.

Below piece of code works with a 32 bytes key however after skipping 32 header bytes of hs_ed25519_secret_key it fails to verify the signature on below cases:

1 - secret: left half of the remaining 64 bytes, public: right half
2 - secret: left half of the remaining 64 bytes, public: last 32 bytes of hs_ed25519_public_key after removing the header
3 - secret: all 64 bytes, public: last 32 bytes of hs_ed25519_public_key

I found a python library that seems to do this PyNaCl however i not familiar with py too much.

Is there something i am doing wrong or bouncycastle does not support expanded 64 bytes private keys

import org.bouncycastle.crypto.Signer;
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
import org.bouncycastle.crypto.signers.Ed25519Signer;
import java.nio.charset.StandardCharsets;
public class ED25519 {
    public static void main(String[] args) throws Exception {
        byte[] message = "a msg to be signed".getBytes(StandardCharsets.UTF_8);
        Signer signer = new Ed25519Signer();
        signer.init(true,  new Ed25519PrivateKeyParameters(KeysUtil.myPrivKey, 0));
        signer.update(message, 0, message.length);
        Signer verifier = new Ed25519Signer();
        verifier.init(false, new Ed25519PublicKeyParameters(KeysUtil.myPubKey, 0));
        verifier.update(message, 0, message.length);
        boolean validSig = verifier.verifySignature(signer.generateSignature());
    }
}

```
Score:1
es flag

Your code is fine if you ask BouncyCastle to generate the key pair:

var gen = new Ed25519KeyPairGenerator();
gen.init(new Ed25519KeyGenerationParameters(new SecureRandom()));
var keyPair = gen.generateKeyPair();
var sk = new byte[32];
((Ed25519PrivateKeyParameters) keyPair.getPrivate()).encode(sk, 0);
var pk = ((Ed25519PublicKeyParameters) keyPair.getPublic()).getEncoded();

The reason it's not working for your Tor key pair is because BouncyCastle isn't calculating the public key by scalar multiplication of the base point with your little-endian secret key.

Instead, it's following RFC 8032 and doing SHA-512 on your initial key material, clearing and setting certain bits, and then using that as the secret key to determine the public key.

It's doing this:

MessageDigest md = MessageDigest.getInstance("SHA-512");
byte [] digest = md.digest(initialKeyMaterial);
var sk = subarray(digest, 0, 32);
sk[0] &= 248;
sk[31] &= 127;
sk[31] |= 64;
var pk = basePoint.scalarMultiply(sk);

If the secret key you are specifying has already been through this SHA-512 transformation, then your code is failing because it's now doing SHA-512 twice. Since you can't reverse the SHA-512 operation, then if you must use BouncyCastle, you'll need to generate your secret key in BouncyCastle and then use that with tor, instead of the other way around.

However, since you probably need to start with the Tor key pair, you might consider modifying the BouncyCastle code to not perform the SHA-512 transformation prior to using it as a signing key.

Miliano avatar
jp flag
Hmm, thanks for the advice, I'll try to rework the signing process since i cannot use BC's generated keypairs when later I'll need to use a quick vanity address with mkp224o that is generating an 64 bytes expanded key like tor.
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.