前回はブラウザを使って簡単にNEMアカウントを作る方法を解説しました。
今回はもう少し強度の高い秘密鍵の生成に挑戦します。
まず、乱数ですが擬似乱数と明確に区別する必要があります。
擬似乱数とは大雑把な言い方をすると、乱数表というものの中から適当に数字を取ってくるような感じです。ですのでシード値と呼ばれるものを指定すれば、かならず同じ値が手に入ります。
そうです、Math.random() のような関数で擬似乱数で秘密鍵を作ってはいけません!
では完全にランダムな乱数というものは作れるのでしょうか?
実はこれも不可能です。システムは何かしらのインプットをたよりに乱数を作ることしかできません。
なので、そのインプット値をコピーされれば同じ乱数値を再現することは可能です。
ですので、限りなくランダムに近い値を目指すしかないのです。
しかし、そのブラウザが十分にランダムなつもりではじき出した乱数も
近い将来その仕組みが解明されて再現方法が見つかってしまうかもしれません。
そのときに重要なキーワードとなるのがエントロピーという概念です。
ルービックキューブを思い浮かべてください。
何回か縦横に回されたルービックキューブは子供が偶然触ったぐらいでは元に戻せなくなります。
そして十分にぐちゃぐちゃにされたされたルービックキューブは大の大人でも元に戻すのは相当困難になるでしょう。
この原理で、秘密鍵を作成するときもランダムに生成した乱数をさらにぐちゃぐちゃにかき回して、
ブラウザが作りだした乱数をさらに復元不可能なものに変換させます。
前回同様HTMLは以下の通り
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<!-- ライブラリ -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js"></script>
<script src="nacl-fast.js"></script>
<script src="xembook-sdk.js"></script>
<!-- app -->
<script>
//ここにロジックを追加していきます
</script>
</body>
</html>
挿入するロジックは少し追加して以下の通りです。
let r = ua2hex(nacl.randomBytes(32));
let seed = processEntropy("0124595587184571048560847563487523487394857234057", "password");
let privateKey = derivePassSha(r + seed, 1000).priv;
let k = KeyPair.create(privateKey);
let address = toAddress(k.publicKey.toString(), 104);
processEntropyを使用して、エントロピーとパスワードからシード値を生成します。
パスワードとはNanoWalletの場合、ログインパスワードを指しますが、今回はなんでもよいです。
そして、"0124595587184571048560847563487523487394857234057"というのがエントロピー値です。
この値が複雑であれば複雑であるほど、復元困難となります。
そして、導出されたシード値と乱数を足し合してさらに1000回グチャグチャにしたものを秘密鍵とします。
後は前回と同様にKeyPairを生成して公開鍵とアドレスを出力すればNEMアカウントセットの出来上がりです。
これで十分に強度の高いNEMアカウントを作成できました。
ちょっと説明が漏れてますね。今回固定値で指定したエントロピー値にはどういったものが適切でしょうか?
NanoWalletでアカウントを作成した人ならわかると思いますが、
マウス操作によって出力される座標値移動情報などは再現できる可能性が低く、よく利用される方法です。
(今回例示した数値列は明らかに桁数が少ないのでご注意ください)
またおいおい追加していきますので、ぜひご参考ください。