Catapult(NEM2)のアカウントをブラウザで作成してみましょう!
※この記事はnem2-sdkを使用せずに作成する方法についての説明です。nem2-sdkを使用して簡単にアカウント作成する方法については以下のURLをご参考ください。
アカウント nem developer center
https://nemtech.github.io/ja/concepts/account.html
秘密鍵
const randomBytesArray = nacl.randomBytes(32);
const hashKey = convert.uint8ToHex(randomBytesArray);
ランダムに0~255までの数字の配列を32個作ります。
Uint8Arrayという型で作成されるので16進数のHEXにコンバートします。
公開鍵
const keyPair = KeyPair.createKeyPairFromPrivateKeyString(hashKey);
const publicKey = convert.uint8ToHex(keyPair.publicKey);
nem2-libray のKeyPairクラスを利用します。GitHubではNodeJS向けに公開されているので、
ちょこちょこっと修正してブラウザでも使えるようにして呼び出します。
HEXで指定するとUint8型で出力されるので、再度16進数のHEXにコンバートします。
わざわざ秘密鍵をHEXに変換してメモリ上に載せるのは危険ですね。
必要無い時はUint8から直接公開鍵を作れるようにしておいた方がよいかもしれません。
アドレス
const address = publicKeyToAddress(keyPair.publicKey, 0x90); //0x90 MIJIN_TEST
function publicKeyToAddress(publicKey, networkIdentifier){
let xembook = require("/main.js");
const decodedAddress = new Uint8Array(25);
decodedAddress[0] = networkIdentifier;
const publicKeyHash = sha3_256.arrayBuffer(publicKey);
const ripemdHash = xembook.getRipemdHash(publicKeyHash);
array.copy(decodedAddress, ripemdHash, 20, 1);
const hash = sha3_256.arrayBuffer(decodedAddress.subarray(0, 20 + 1));
array.copy(decodedAddress, array.uint8View(hash), 4, 20 + 1);
return base32.encode(decodedAddress);
}
アドレス変換時にNodeJSに依存したモジュールを多用します。
CryptoJSのように公開されたライブラリが存在しないのでブラウザ向けに自作しましょう。
browserifyというNodeJSのモジュールを利用すればブラウザ用に書き出すことができます。
今回自作したのが xembook.getRipeHash(publicKeyHash)という関数になります。
ripemd160というMD5を進化させたハッシュ関数を使うのが流行のようです。
これさえ変換できれあネットワークIDとチェックサムをつけて BASE32変換をかければアドレスの出来上がりです。
ではxembookモジュールの中身を見てみましょう。
'use strict'
var Ripemd160 = require('ripemd160');
var xembook = {}
xembook.getRipemdHash = function(publicKeyHash){
return new Ripemd160().update(new Buffer(publicKeyHash)).digest();
}
module.exports = xembook;
今はこれだけです。
publicKeyでBufferクラスを生成しRipemd160で更新した後ダイジェストを取ります。
このRipemd160とBufferがNodeJSに依存したモジュールとなっています。
ローカルにNodeJSとnpmを入れ、以下のモジュールをインストールしました。
npm install inherits
npm install hash-base
npm install buffer
npm install ripemd160
npm install base64-js
npm install ieee754
次に browserifyを使って外部から参照できるようにします。
browserify -r main.js -o bundle.js
これでbundle.jsというファイルに書き出されました。 -rオプションつけることで外部から exportsしたモジュールをrequireで呼び出せるようになります。
はい、これでCatapultのアカウントが作成できました。
ところで最近、イーサリアムの秘密鍵が重複したというニュースがありました。
おそらく完全にランダムではない強度の低い秘密鍵が狙われたものと思われます。
今回の秘密鍵の作り方もブラウザのランダム関数に依存した作りとなっています。
システムが十分に機能していない状態などで利用されたランダム関数が完全にランダムであるかどうかの保証はありません。
ですので、さらにプラスアルファで指やマウスの動きを組み合わせて強度の高い秘密鍵を作っておきましょう。
プログレスバーを表示する準備をします。
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
<div class="modal js-progress-bar">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
<div class="progress progress-popup">
<div class="progress-bar progress-bar-striped active"></div>
</div>
<div>秘密鍵の強度を高めています。</br>
画面上を指(マウス)で複雑になぞってください。</div>
</div>
</div>
</div>
</div>
以下のロジックでプログレスバーを表示し、終了させます。
countが100になると終了します。
progress(0);
var $modal = $('.js-progress-bar');
$modal.modal('show');
function progress(count){
setTimeout(function(){
$modal.find('.progress-bar').css({'width':count+'%'});
if(count >= 100) {
//プログレスバー終了
$modal.modal('hide');
//プログレスバー初期化
count = 0;
is_confirmed = false;
$modal.find('.progress-bar').css({'width':'0%'});
return; // 終了
}
progress(count);
},50);
}
マウスやスマホで指をなぞる動作をランダム関数を作る複雑さに組み合わせます。
let eventPageX = 0;
let eventPageY = 0;
let lastEventPageX = 0;
let lastEventPageY = 0;
// マウス
document.body.addEventListener('mousemove', function(e) {
eventPageY = e.pageY;
eventPageX = e.pageX;
});
// タッチ
document.body.addEventListener('touchmove', function(e) {
for (let i = 0; i < e.changedTouches.length; ++i) {
const touch = e.changedTouches[i];
eventPageY = touch.pageY;
eventPageX = touch.pageX;
}
});
//こんな感じでentropyに座標を追加していく。
var entropy = "";
if(lastEventPageX != eventPageX && lastEventPageY != eventPageY){
entropy += (""+ eventPageX).slice(-2) + (""+eventPageY).slice(-2);
console.log(entropy);
count = entropy.length / 1000 * 100;
$modal.find('.progress-bar').css({'width':count+'%'});
lastEventPageX = eventPageX;
lastEventPageY = eventPageY;
}
SHA3_256でまず作成したentropyをぐちゃぐちゃにした後、最初に作成した秘密鍵を掛け合わせてcount回グチャグチャにします。
function derivePassSha(password, seed,count) {
let uint8seed = convert.hexToUint8(convert.utf8ToHex(seed))
for (let i = 0; i < count; ++i) {
uint8seed = array.uint8View(sha3_256.arrayBuffer(uint8seed));
}
let uint8password = convert.hexToUint8(password + convert.uint8ToHex(uint8seed));
for (let i = 0; i < count; ++i) {
uint8password = array.uint8View(sha3_256.arrayBuffer(uint8password));
}
return uint8password;
};
これで一つのランダム発生器に依存しない、強度の高い秘密鍵を作成することができました。
動くものはこちらに置いておきます。
ソースコードはこちらです。
https://github.com/mediaprogramer/XEMBook-sdk/blob/master/examples/210_account.html