Closed kingsleyh closed 4 years ago
I found some more info in the manual: https://monocypher.org/manual/optional/hmac
I'll try to figure it out from that
My understanding of the node.js code is that it's doing HMAC-SHA512(key="ED25519_CURVE", seed)
, where seed
comes from somewhere out of scope for this.
The equivalent of that in Monocypher with C would be with mac
being the result of the operation:
const char szKey[] = "ed25519 seed";
const uint8_t bKey[12]; /* technically required because of strict aliasing */
uint8_t mac[64];
for (size_t i = 0; i < sizeof(bKey); ++i)
bKey[i] = szKey[i];
crypto_hmac_sha512(mac,
bKey, sizeof(bKey),
seed, seed_len);
Note that crypto_hmac_sha512(3monocypher) is just a convenience function that performs crypto_hmac_sha512_init()
, crypto_hmac_sha512_update()
and crypto_hmac_sha512_final()
all in one go so that you don't need to micromanage a context if you just need the HMAC of one buffer.
I hope that helps.
That said, I'm curious what you're trying to do there. The string “ed25519 seed” definitely sounds like you're doing something somewhat out of the ordinary and I'm curious as to what it is you're doing. Just in case you're doing something subtly bad.
ok thanks so I think you are saying:
key = "ed25519 seed"
message = seed
void crypto_hmac_sha512(hash_out, key, key_len, message, message_len);
It would make more sense to me if it was:
key = seed
message = "ed25519 seed"
In the code from your original post, the parameters map as such:
ed25519 seed
as ASCIIseed
, interpreted as a hex stringI just translated your code 1:1. The JavaScript code you've given does HMAC with 'ed25519 seed'
as the key argument. So if you've been having issues because you're getting wrong results: That's probably the actual cause.
Hi @kingsleyh
Note that we're not exactly JavaScript specialists. I myself know very little, and the code you're trying to understand… I personally don't know what it does, so I cannot translate it. I can make guesses, but they won't be reliable.
You need to understand the JavaScript code yourself, so you can tell us what it does. We'll be happy to help you translate it then.
Right now, all I can say about this code is that something's fishy. Why is this code is talking about Ed25519 and HMAC? Even if you hash the message before signing the hash (instead of signing the message directly), you don't need HMAC, SHA-512 is enough. And what's the "seed" for? HMAC generally uses a key, and EdDSA has no need for a random seed —it's deterministic.
(Note: I have no idea how much you know about cryptography, but if you haven't followed a formal introduction yet, I strongly suggest you do. It's tedious, but unfortunately necessary. I currently recommend Crypto101 (PDF) or this Stanford course (videos).)
Hi
Ok here is some more background:
I'm trying to implement this: https://github.com/satoshilabs/slips/blob/master/slip-0010.md#test-vector-1-for-ed25519
and specifically the first part is Master Key Generation: https://github.com/satoshilabs/slips/blob/master/slip-0010.md#master-key-generation
The main aim is that given the reference test vectors that the output from the code should match the expected results specified in the test vectors 1 and 2.
There are 2 existing implementations I'm looking at for reference:
both the dart and the javascript one when using the hmac sha512 produce the same result. But when using OpenSSL and Monocypher I get a different result. (At least OpenSSL and Monocypher agree with each other though)
so given this key and message:
key = "ed25519 seed"
message = "000102030405060708090a0b0c0d0e0f"
The digest should be:
2b4be7f19ee27bbf30c667b642d5f4aa69fd169872f8fc3059c08ebae2eb19e790046a93de5380a72b5e45010748567d5ea02bbf6522f979e05c0d8d8ca9fffb
for example some js: (https://npm.runkit.com/create-hmac)
var createHmac = require('create-hmac')
var hmac = createHmac('sha512', 'ed25519 seed')
const I = hmac.update(Buffer.from('000102030405060708090a0b0c0d0e0f', 'hex')).digest()
const result = I.slice(0,64).toString('hex')
however with monocypher and openSSL I'm getting:
2f415ca1560fa1350d9ab22b3de4fec73bed2a6346601e9b17436f961b48fbccb1267fc885f16703526a8039a4605ca07fa53b91dbc1a9f1b67795cf5bae6ac8
I would have hoped that Dart and Javascript hmac sha512 would have been compatible with OpenSSL and Monocypher but either they are not - or I'm doing something wrong!
An incompatibility is extremely unlikely. You must be using the JavaScript API and Monocypher/OpenSSL differently. I'd first verify you didn't swapped hexadecimal and ASCII encodings.
Moreover, Have you tried the official test vectors from the RFC?
Can you show your C code, so we can compare?
The following snippet of code generates and prints the expected value of 2b4be7f19ee27bbf30c667b642d5f4aa69fd169872f8fc3059c08ebae2eb19e790046a93de5380a72b5e45010748567d5ea02bbf6522f979e05c0d8d8ca9fffb:
#include "monocypher.h"
#include "monocypher-ed25519.h"
#include <stdio.h>
int
main(void)
{
const char szKey[] = "ed25519 seed";
uint8_t bKey[12];
for (size_t i = 0; i < 12; ++i)
bKey[i] = szKey[i];
uint8_t msg[16] = { /* <-- message in binary */
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
};
uint8_t mac[64];
crypto_hmac_sha512(mac, bKey, 12, msg, 16);
for (size_t i = 0; i < 64; ++i)
printf("%02x", mac[i]);
puts("");
}
I didn't manage to reverse engineer the code that didn't work though. Hard to tell at this point.
Thanks very much for confirming - I must be doing something wrong. I'm not a C programmer and I'm also not using the C version of Monocypher - just Crystal lang bindings so my code may not make any sense to you:
def go
# fun hmac_sha512 = crypto_hmac_sha512(hmac : Uint8T[64], key : Uint8T*, key_size : SizeT, message : Uint8T*, message_size : SizeT)
digest = uninitialized UInt8[64]
key = "ed25519 seed"
message = "000102030405060708090a0b0c0d0e0f"
LibMonocypher.hmac_sha512(digest, key, key.bytesize, message, message.bytesize)
digest.to_slice.hexstring
end
and the OpenSSL version:
puts OpenSSL::HMAC.hexdigest(:sha512, "ed25519 seed", "000102030405060708090a0b0c0d0e0f")
I've got it working now thanks for the help:
def go
# fun hmac_sha512 = crypto_hmac_sha512(hmac : Uint8T[64], key : Uint8T*, key_size : SizeT, message : Uint8T*, message_size : SizeT)
digest = uninitialized UInt8[64]
key = "ed25519 seed"
message = "000102030405060708090a0b0c0d0e0f".hexbytes
LibMonocypher.hmac_sha512(digest, key, key.bytesize, message, message.bytesize)
digest.to_slice.hexstring
end
I was passing bytes instead of hexbytes for the message - obvious really!! the more I looked the less I saw! lol
Hi
I'm trying to both understand and convert this code to monocypher:
I see in the ed25519.h there is this:
Are these the correct functions that correspond to my javascript example? And is there an example anywhere of how to use them. Do I need to use the init,update,final functions to replicate the javascript?
Any help greatly appreciated