Search code examples
phpcryptographycryptocurrencyed25519

Blake2b-512 with ED25519 in PHP with NanoSalt library (Nano Crypto)


I'm trying to generate a Nano private key, public key and address (that's a cryptocurrency) in PHP. I successfully generated private keys but I'm not able to generate the public key and consequently neither the checksum nor the integer address. in order to do this I would have to encrypt the private key through the blake2b-512 algorithm and the ED25119 curve and then I gotta obtain the checksum through the blake2b-40 algorithm by encrypting the public key. That's my code:

$privatekey = strtoupper(bin2hex(random_bytes(32)));
$publickey = sodium_crypto_sign_ed25519_pk_to_curve25519($private_key);
$checksum = hash(blake2b-40, $publickey);

I didn't get what I need. Why?

UPDATE 30/12/2021 13:36

I'm trying to resolve the update with the NanoSalt library but it gives me this error:

index.php

<?php
use MikeRow\Salt\NanoSalt;

$nanoSalt = new NanoSalt();
$public_key = $nanoSalt->crypto_sign_public_from_secret_key(hex2bin("781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3"));
print(strtoupper($public_key->toHex()) . PHP_EOL);
?>

and that's the error:

Fatal error: Uncaught Error: Class "MikeRow\Salt\NanoSalt" not found in C:\xampp\htdocs\Peppe\index.php:4 Stack trace: #0 {main} thrown in C:\xampp\htdocs\Peppe\index.php on line 4

Solution

  • Nano does not use the standard Ed25519, but a variant where the digest Blake2b-512 is used instead of the usual SHA-512, see standard variant vs Nano variant.

    For this reason, standard Libsodium libraries could generally not be used, such as the sodium_crypto_sign_publickey_from_secretkey() function proposed in the other answer. Apart from that, this function expects a 64 bytes key (consisting of seed and public key) while in Nano the private key is only 32 bytes (consisting of the seed).


    The following example from the Nano documentation shows a private key, a public key and a public address:

    "private": "781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3",
    "public": "3068BB1CA04525BB0E416C485FE6A67FD52540227D267CC8B6E8DA958A7FA039",
    "account": "nano_1e5aqegc1jb7qe964u4adzmcezyo6o146zb8hm6dft8tkp79za3sxwjym5rx" 
    

    One way to get the public key with PHP is to use a library that supports Nano. Such a library is e.g. Salt, which also contains the Nano variant. With this library the determination of the public key is simply:

    use MikeRow\Salt\NanoSalt;
    
    $nanoSalt = new NanoSalt();
    $public_key = $nanoSalt->crypto_sign_public_from_secret_key(hex2bin("781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3"));
    print(strtoupper($public_key->toHex()) . PHP_EOL); // 3068BB1CA04525BB0E416C485FE6A67FD52540227D267CC8B6E8DA958A7FA039
    

    Using the private key from the example results in the public key from the example.

    Another way to get the public key would be to use a library that supports Ed25519 arithmetic, so that the public key can be obtained by multiplying by the base point. Such a library is e.g. phpseclib (although frankly I have not tested this way).


    The public address is obtained by encoding the public key with a special Base32 variant and appending a checksum that is also Base32 encoded. The checksum is the Blake2b-40 hash of the public key, see here.

    A library that supports the special Base32 variant and Blake2b is NanoPHP. Since the Blake2b implementation is a bit cumbersome, this Blake2b library can be used alternatively. A possible implementation is:

    require "Uint.php";
    require "Blake2b.php";
    
    // publick key
    $public_key = "3068bb1ca04525bb0e416c485fe6a67fd52540227d267cc8b6e8da958a7fa039";
    $key = Uint::fromHex('0' . $public_key);
    $key_base32 = $key->toString();
    print($key_base32 . PHP_EOL);
    
    // checksum
    $blake40 = new Blake2b(5);
    $hash = $blake40->hash(hex2bin($public_key));
    $check = Uint::fromHex(bin2hex($hash))->reverse();
    $check_base32 = $check->toString();
    print($check_base32 . PHP_EOL);
    
    print('nano_' . $key_base32 . $check_base32 . PHP_EOL); // nano_1e5aqegc1jb7qe964u4adzmcezyo6o146zb8hm6dft8tkp79za3sxwjym5rx
    

    where Uint.php provides the Base32 encoding and comes from the NanoPHP library, while Blake2b.php is the more handy Blake2b alternative.

    Using the public key from the example gives the correct address.


    To test other private keys, this site is useful.


    Regarding security: Be aware that all libraries used are rather small and may contain vulnerabilities.