简体   繁体   中英

Blake2b-512 with ED25519 in PHP (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?

PHP 7 >= 7.2.0 and PHP 8 do have a built-in crypto library "sodium" that let you derive the ED25519 public key from a given secret (private) key: https://www.php.net/manual/en/function.sodium-crypto-sign-publickey-from-secretkey.php

Below is a full runing example for this task:

<?php
function generateEd25519KeyPair()
{
    return sodium_crypto_sign_keypair();
}

function deriveEd25519PublicKey($privateKey)
{
    return sodium_crypto_sign_publickey_from_secretkey ($privateKey);
}

function base64Encoding($input)
{
    return base64_encode($input);
}

echo 'Generate ED25519 private and public key and derive public key from private key' . PHP_EOL;
$keyPair = generateEd25519KeyPair();
$privateKey = sodium_crypto_sign_secretkey($keyPair);
$publicKey = sodium_crypto_sign_publickey($keyPair);
echo'privateKey (Base64): ' . base64Encoding($privateKey) . PHP_EOL;
echo'publicKey (Base64):  ' . base64Encoding($publicKey) . PHP_EOL;

echo PHP_EOL . 'derive the publicKey from the privateKey' . PHP_EOL;
$publicKeyDerived = deriveEd25519PublicKey($privateKey);
echo'publicKey (Base64):  ' . base64Encoding($publicKeyDerived) . PHP_EOL;
?>

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. This function also 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 eg 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 eg 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 ][9}. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM