タグ: 暗号資産

3. アカウント[感想編]

速習Symbolブロックチェーン体験記「とろ速Symbol」の3章、アカウントについての感想編です。

こちらでは実装ではなく、「ああ、こういうことなのかな?」といった学んだと思う部分をまとめていこうと思います。まぁ、全然わかってないんだけど。

実施編はこちら。

さて。

アカウント生成・アカウントへの送信

アカウント構成はざっくり言えば下記のイメージ。

ある意味、アカウント生成とは秘密鍵(PrivateKey)を決めること。
秘密鍵(PrivateKey)から公開鍵(PublicKey)、生アドレス(RawAddress)、アドレス(Address)の導出が可能になる。

よって、バレてはまずいのは秘密鍵。
また、秘密鍵はネットワークに晒す必要もないため、誰かに教える必要はない

上記の構成で公開アカウントで囲まれた内容がある。

これについては外部に公開可能な部分であり、SymbolノードのREST APIから直接取得できる部分でもある。

僕らが普段送信を行う対象はアドレスとなる。

また、アカウント生成時点ではネットワークが不要である。
つまり、ネットワークはアカウントが作成されたことを知らない。

つまり、秘密鍵が生成されたことをブロックチェーンは知らないため、秘密鍵から生成される公開鍵も不明であるし、アドレスも不明である。

アカウント(秘密鍵)が存在することを知らせるためには、作成したアカウントからトランザクションを発生させる必要がある。

トランザクション実行には署名が必要です。(これ、次回の話?)

署名が必要であるからには、秘密鍵が存在する。

よって、トランザクション実行により、対象のアカウントに対する公開鍵以降がチェーンに刻まれる。というお話。

なお、その場合、なぜかフォーセットから、(チェーンが認識していない)aliceアカウントに送金できるという部分がある。

おそらくこんな感じで、送信されている。

2. の秘密鍵の存在は不明~~はその通りで、よって、アドレスを間違えるとGOXの可能性がある。
(実際に導出済みのアドレスとは限らないため、本当に誰も所有していないところに飛ぶ可能性もあると思う)

そのため、アプリケーションから送金などを行う場合、通常であれば「公開鍵がブロックチェーンに登録されていること」を確認したほうが安全なのだろう。
(おそらく、あらゆる場面で)

アカウント情報の確認

先ほども書いたけれど、アカウントを作成しただけではSymbolブロックチェーンはアカウントの存在を認識していない。

また導出の順の通り、アドレスについては導出の最後になる。
そのため、アドレスから公開鍵は逆引きとなるため、不明な値(ゼロフィル)となる。

よってトランザクション実行時に公開鍵を使用するため、登録される。

うん、他には特にないですね。

現場で使えるヒント

暗号化と署名。

ここ、凄い困りました。全然暗号とかわかんないし。
AES-GCM形式っていうのはわかった。
※ざっくりいえば(aliceの秘密鍵+bobの公開鍵) = (aliceの公開鍵+bobの秘密鍵)となる鍵を作って、それで暗号化するということのようだった。

わからないのでFlutterのパッケージを組み合わせて試していたのですが、全然うまくいかなかったんですよ。これ、あれですね、わかったことではなくて愚痴ですね。

もう覚えてないのであれなんですが、ハッシュの計算? かなんかが、よく使われているのとは別らしいですね。(内容はうろ覚えですが、よく提供されているのが128bitで、こちらでは256bitだったかな?)

それについて言及しているブログを目にしたため、「あ、これパッケージじゃだめなのかも」と認識しました。
(ブログではSymbolではなく、NEMについての言及でしたけれど)

結果として中身は全然わからないのですが、tweetnacl? というソースを写経する感じに至りました。ほんまこいつ。。。。

そんなわけで、暗号化についてはこんな感じで理解しました。

これでbobはaliceのメッセージを受け取ることができるようです。

公開鍵の交換はローカルでもいいですが、トランザクションさえ発生させてしまえば公開鍵がチェーンから取得できるので、「トランザクションを発行したことがあるアカウント」であれば、秘密のメッセージを送りあうことができます。

また、他人から見た場合でも安全です。

また、「本当にaliceが送信したのか?」という疑わしい点を解消するため、署名の仕組みがあるとのことでした。

今回、速習Symbolではalice → bobへの送信したメッセージに対して、aliceが署名を行いました。

その署名内容を検証することでaliceが送ったということを証明できるようです。

なお、速習Symbolでは以下の感じです。

これ、本当はトランザクションの証明についての話でもあると思うんですが、たぶんそれは次の章で触れると思います。たぶん。

アカウントの保管

秘密鍵の暗号化をしよう! という話でした。そうですね。

以上です。お疲れさまでした。

3. アカウント[実施編]

速習Symbolブロックチェーン体験記「とろ速Symbol」です。

「1. はじめに」「2.環境構築」についてはほぼ目を通すだけのため、スキップしました。

本ページでは「アカウント」をやっていきます。

感想編はこちら。

なお、今回はこちらのテストネットノードをお借りしました。

@ftakao2007

hoge.harvesting-sweet-potatoes.club(Symbol テストネット)

(敬称略)

もちろんメインネットのノードも運用されております。

間違っていたり、問題があったら教えてください。

本稿では「3. アカウント」の実施内容を記載します。

新規作成 – 秘密鍵と公開鍵の導出 – アドレスの導出

アカウントの新規作成を行います。

秘密鍵と公開鍵のキーペアを生成し、そこからアドレスの導出を行います。

また、アドレスの導出にはテストネット or メインネットの属性が必要なようです。

アカウント新規作成

まったく当てにならない僕の自作コードはこちら。

    // 使用するホスト情報を取得する。
    var availableHost = await _getNetworkHost();

    try {
      // 新アカウントを生成する。
      var newAccount = await _generateNewAccount(availableHost);

      return newAccount;

    } catch (e) {
      debugPrint(e.toString());
      debugPrint(StackTrace.current.toString());

      throw CreateNewAccountFailedException();
    }

※全体像を載せられるようなコードになっていないため、一部分だけです。

色々ありましたが、実行結果としてはこんな感じです。

※一応秘密鍵は消しました。

秘密鍵の導出はさておいて(自分で用意していない部分でもあるので)、

導出された秘密鍵を使ってSymbolSDKから、アドレスを取得しようと思います。

そうすることで秘密鍵 → 公開鍵 → 生アドレス → アドレスの導出が同一であるということの検証とします。

※なお、raw addressと記載してますが、これをraw addressと呼んでいいかはわかりません。が、とりあえずこれで進めます。

なので比較のため、SymbolSDKでも同じことを実行します。

SymbolSDKでの実行結果。

アドレスが同一の”TCY7YADZLU6G34GK7VPGI3EUK2PMS2SDS25QMIA”となっているため、秘密鍵からアドレスの生成までは同一である、ということとしました。

アカウント情報の確認

既に(もう少し先まで)実行した関係上、データがきれいではないのですが。

実行テストを行う上で、alice、bobの秘密鍵は固定で使用していくこととします。(期間が空いた際に毎度やり直すと大変なため)

aliceのアカウント。

基本的にaliceのアカウントは上記で行います。

既にトランザクションを実行している関係上、変な値ですがxymは保持されています。

explorerで表示した残高。

こちらを取得します。

当てにならないコードはこちら。

_startProcessing();

                _accountWorker.getAccountAssetsFromAddress(Address("TAEKZ65QU3T4HK3JEEI2SOAHYNPYBUZ7BPC4YAA"))
                  .then((AccountMosaicProfile accountMosaicProfile) {
                    print("取得アカウント情報。");
                    print("address: ${accountMosaicProfile.account.address}");
                    print("public key: ${accountMosaicProfile.account.publicKey}");
                    
                    print("所持モザイク情報。");

                    var account = accountMosaicProfile.account;
                    var mosaicMap = accountMosaicProfile.mosaicInfos;

                    for (var mosaic in account.mosaics){

                      var mosaicDTO = mosaicMap[mosaic.id];
                      var mosaicAmount = "";
                      if (mosaicDTO != null && mosaicDTO.divisibility > 0){
                        var amountStr = mosaic.amount.toString();
                        var intVal = amountStr.substring(0, amountStr.length - mosaicDTO.divisibility);
                        var decVal = amountStr.substring(amountStr.length - mosaicDTO.divisibility);
                        mosaicAmount = "$intVal.$decVal";
                      }

                      print("id: ${mosaic.id} amount: $mosaicAmount");
                    }
                  })
                  .whenComplete(_finishProcessing);

実行結果はこちら。

※実行結果。

所持モザイク情報が取得できていることが確認できますね。

現場で使えるヒント

暗号化と署名

AliceとBobのみがわかるように、内容を暗号化できるみたいです。

なのでやっていきます。

当てにならないコードはこちら。


  // aliceのアカウントを作成する。
  var alice = await _accountWorker
.createNewAccountFromPrivateKey("379891A667CBA1C4F9B8DFEBCEE2AE393E4D04D162B9ED3BAB6CA17178*****");

  // bobのアカウントを作成する。
  var bob = await _accountWorker
.createNewAccountFromPrivateKey("4A570201B9E491F0B1F4BDFE4C1AEED7D75582AAAA7771374FC88DC58C*****");

  var baseMessage = "super hogehoge";
  var message = PlainMessage.create(baseMessage);

  print("メッセージ[$message]を暗号化/復号化。");

  // メッセージの暗号化。
  var encryptedMessage = await alice.encryptMessage(message, bob.publicAccount);

  print("encrypted: ${encryptedMessage.payload}");

  // メッセージの復号化。
  // 念のため、payloadから暗号化メッセージクラスを別途インスタンス化し、復号化を行う。

  var newEncryptedMessage = await EncryptedMessage.create(encryptedMessage.payload, alice.publicAccount);
  var decryptedMessage = await bob.decryptMessage(newEncryptedMessage);

  print("decrypted: ${decryptedMessage.value}");

  print("署名と検証");

  // 復号化した内容にaliceが署名する。
  var aliceSignature = await alice.sign(decryptedMessage.toByte());

  print("aliceSignature: ${aliceSignature.value}");

  print("署名を行ったのがaliceなのかを検証する。");

  var isVerifiedAlice = await alice.publicAccount.verify(decryptedMessage, aliceSignature);
  var isVerifiedBob = await bob.publicAccount.verify(decryptedMessage, aliceSignature);

  print("alice: ${isVerifiedAlice ? "yes" : "no" }");
  print("bob: ${isVerifiedBob ? "yes" : "no"}");

“super hogehoge”をaliceがbobの公開アカウントに対して暗号化。

bobがaliceの公開アカウント情報に対して復号します。

公開アカウントってなに? っていうと「公開鍵/生アドレス/アドレス」のセットです。

※使用するのは公開鍵なんですけど。

復号した内容への署名を行い、それを検証することで、署名したのがaliceである証明とします。

実行結果はこちら。

こんな感じです。

encryptedが暗号化後のメッセージ、decryptedが復号したメッセージ。

aliceSignatureがaliceの署名です。

検証結果から、aliceが署名したことをうかがえます。

ここでの作りは僕がコーディングしただけのため、実際にSDKと同じなの? ということを確認する必要があります。

が、面倒でもあるため、SDKにて復号と署名の検証のみ行います。

ということで、復号は以下。

bobがaliceの公開情報を使用して復号した結果です。

同様に署名についても証明します。

aliceが署名していることを証明できました。

また自作のコードで暗号化 -> SDKにて復号(署名)も可能であることを確認しました。

アカウントの保管

アカウント項における最後。アカウントの保管についてです。

具体的には秘密鍵は大事な情報のため、ぱっと見ではわからないように暗号化して保管しようぜ! って話です。

使用しているaliceの秘密鍵379891A667CBA1C4F9B8DFEBCEE2AE393E4D04D162B9ED3BAB6CA17178E*****を使用することとします。

ということでまずはSDKを使用して実行します。

こんな感じとなりました。

※QRコード表示はカットとします。

もちろん復号も可能です。

さて、これと同じようなものを実装しました。

※これ、厳密にはSDKに含まれているわけではない気がしますが。

そんなわけで当てにならないコードです。


  var alicePrivateKey = PrivateKey("379891A667CBA1C4F9B8DFEBCEE2AE393E4D04D162B9ED3BAB6CA17178E*****");

  print("秘密鍵の暗号化");

  // パスフレーズ付きで暗号化。
  // invest_in_kishidaはPW。
  var signerQR = qr.QRCodeGenerator.createExportAccount(alicePrivateKey, 
    NetworkTypeEnum.testnet, 
    symbolTestnetGenerationHash, "invest_in_kishida");

  var schema = signerQR.getSchema();

  var outputObject = await schema.toObject(signerQR);
  var printOutputObject = jsonEncode(outputObject);

  print("暗号結果。");
  print(printOutputObject);

  {
    print("復号する。");
    var decryptedQR = await qr.QRCodeGenerator.fromJson(printOutputObject, password: "invest_in_kishida");

    decryptedQR as qr.AccountQR;

    print("復号結果。");
    print(jsonEncode(decryptedQR.privateKey));
  }

  {
    print("速習Symbolより。Edgeを使用して作成した暗号化メッセージpayloadをdecryptする。");
    print("symbol-qr-libraryと等価であることを検証。");

    var fromBrowser = '''
      {"v":3,"type":2,"network_id":152,"chain_id":"49D6E1CE276A85B70EAFE52349AACCA389302E7A9754BCF1221E79494FC665A4","data":{"ciphertext":"aed4e1b553e02e5a38946db98ebf795bMOiGKwS3VtiQZnIvzBRQ21IQgyAM662tKUMJ3MiI3k/+YYrGC/JVbrvtkN1WfjmLOaBXYIg3dJw6FqdZ0mh80YiOl******....","salt":"d8b4f954c878fb6888cbff896ac1409eb861706d2fd933f4ede030c515148396"}}
    ''';

    var decryptedQR = await qr.QRCodeGenerator.fromJson(fromBrowser, password: "invest_in_kishida");

    decryptedQR as qr.AccountQR;

    print("復号結果。");
    print(jsonEncode(decryptedQR.privateKey));

    // 表示するQRコードの取得。
    _dispAccountQR = await decryptedQR.toQrImage(null);
  }

実行結果はこちら。

こんな感じとなりました。

秘密鍵の暗号化 -> 復号が可能であり、SDKで暗号化した秘密鍵の復号も可能であることから、おそらく、秘密鍵の暗号化、復号が等価である。かもしれない。というところになりました。

一応、QRを表示するのも用意したんですよ。

お見せできないんですけども。

そんなわけで第3章、アカウントについては以上です。

後半の感想編に続きます。

© 2023 SIXIS

Theme by Anders Noren上へ ↑