Sign a message using private key (ecdsa)

Hello everyone!

New to Elixir, trying to implement following code and facing some issues

Original code, Javascript

const nacl = require("tweetnacl");
const web3 = require("@solana/web3.js");
const bs58 = require("bs58");

const acc = web3.Keypair.fromSecretKey(bs58.decode("67W6oboaDL6CwoS2APNwkUttMkKNNtJeNNnKAXPpVhJPWfV8jHPuAP7nEUP3iXFHBvH7GpwAcrrFPgJCDQDvKhco"));
const message = new TextEncoder().encode("hello world");
const sig = nacl.sign.detached(message, acc.secretKey);
console.log("Signature", sig);

// Result:
// 125, 215, 145, 185, 147, 227, 199, 234, 158, 49, 89, 35, 26, 11, 1, 28, 157,
// 75, 128, 145, 174, 187, 144, 196, 62, 190, 164, 31, 16, 131, 114, 23, 223,
// 249, 154, 55, 219, 254, 205, 18, 92, 251, 101, 247, 15, 193, 75, 141, 31,
// 46, 18, 42, 57, 248, 135, 25, 181, 52, 69, 201, 239, 167, 223, 3

Now, Elixir:

iex(1)> secret = B58.decode58!("67W6oboaDL6CwoS2APNwkUttMkKNNtJeNNnKAXPpVhJPWfV8jHPuAP7nEUP3iXFHBvH7GpwAcrrFPgJCDQDvKhco")
<<255, 177, 27, 246, 3, 158, 142, 9, 100, 55, 130, 149, 54, 84, 65, 81, 33, 211,
  0, 110, 189, 49, 38, 118, 90, 12, 9, 135, 115, 225, 97, 198, 166, 247, 6, 214,
  222, 146, 112, 135, 85, 251, 27, 37, 149, 248, 121, 230, 215, 218, ...>>
iex(2)> :crypto.sign(:ecdsa, :sha512, "hello world", secret)
** (ErlangError) Erlang error: {:badarg, {'pkey.c', 363}, 'Couldn\'t get ECDSA private key'}
    (crypto 5.1.1) crypto.erl:1455: :crypto.sign/5

Or, using Ed25519 library:

iex(1)> Ed25519.signature("hello world", secret)
<<248, 139, 63, 60, 149, 176, 171, 216, 212, 129, 32, 157, 234, 81, 33, 242, 34,
  227, 237, 79, 137, 82, 197, 193, 104, 231, 165, 139, 26, 168, 43, 112, 120,
  192, 185, 76, 131, 60, 124, 167, 1, 129, 52, 61, 179, 184, 18, 129, 245, 82,
  ...>>

which is different from result I’m getting in the first snippet (JS).

Any idea what am I doing wrong?

:wave:

You can try using GitHub - jlouis/enacl: Erlang bindings for NaCl / libsodium

iex(1)> Mix.install [:base_58_check, :enacl]

iex(2)> secret = Base58Check.decode58!("67W6oboaDL6CwoS2APNwkUttMkKNNtJeNNnKAXPpVhJPWfV8jHPuAP7nEUP3iXFHBvH7GpwAcrrFPgJCDQDvKhco")
<<255, 177, 27, 246, 3, 158, 142, 9, 100, 55, 130, 149, 54, 84, 65, 81, 33, 211,
  0, 110, 189, 49, 38, 118, 90, 12, 9, 135, 115, 225, 97, 198, 166, 247, 6, 214,
  222, 146, 112, 135, 85, 251, 27, 37, 149, 248, 121, 230, 215, 218, ...>>

iex(3)> :enacl.sign_detached("hello world", secret)
<<125, 215, 145, 185, 147, 227, 199, 234, 158, 49, 89, 35, 26, 11, 1, 28, 157,
  75, 128, 145, 174, 187, 144, 196, 62, 190, 164, 31, 16, 131, 114, 23, 223,
  249, 154, 55, 219, 254, 205, 18, 92, 251, 101, 247, 15, 193, 75, 141, 31, 46,
  ...>>

enacl doesn’t work on M1. I tried to set the flags they recommend

export CFLAGS="-arch arm64"
export CXXFLAGS="-arch arm64"
export LDFLAGS="-arch arm64"

but still getting an error

_build/dev/lib/enacl/priv/enacl_nif.so\' (mach-o file, but is an incompatible architecture (have \'x86_64\', need \'arm64e\'))

I’m on M1 :upside_down_face:

> uname -sm
Darwin arm64
brew install libsodium
export CFLAGS="-arch arm64"
export CXXFLAGS="-arch arm64"
export LDFLAGS="-arch arm64"
export CPATH=/opt/homebrew/include
export LIBRARY_PATH=/opt/homebrew/lib

iex
iex(1)> Mix.install [:enacl]
iex(2)> :enacl.sign_keypair
%{
  public: <<48, 72, 44, 250, 47, 127, 32, 114, 59, 128, 253, 235, 109, 51, 40,
    106, 28, 193, 195, 20, 167, 61, 120, 125, 69, 170, 146, 75, 130, 65, 40,
    128>>,
  secret: <<117, 74, 219, 162, 248, 247, 114, 119, 98, 0, 33, 116, 164, 226,
    195, 6, 251, 114, 18, 29, 65, 30, 53, 58, 80, 46, 210, 19, 224, 13, 143,
    115, 48, 72, 44, 250, 47, 127, 32, 114, 59, 128, 253, 235, 109, 51, 40, 106,
    ...>>
}
1 Like

You need to manually change the rebar.conf, after mix deps.get and remove the hardcoded -arch x86_64 for the Darwin conditional.

Only after you have done that, you can do mix deps compile.

If you compiled before doing the change, you need to delete the deps/enacl folder first and refetch!

1 Like

Thanks worked for me

For anyone who’s looking for the info.

Found a better solution:
First, need only half of “secret” (second half is a public key).
Second, this code works without any dependencies:

signature = :crypto.sign(:eddsa, :none, message, [privkey, :ed25519])
:crypto.verify(:eddsa, :none, message, signature, [pubkey, :ed25519])