boto3のログを黙らせる

したいこと

標準出力のテストをしているのですが、boto3のログが邪魔で非常に厄介です。

対処法

python
import logging
import boto3

boto3.set_stream_logger('boto3.resources', logging.CRITICAL)

意図的にログレベルをCRITICALにあげて、DEBUGログが出ないようにします。

参考

[https://github.com/boto/boto3/blob/develop/boto3/init.py#L37:title]

SLP(Simple Ledger Protocol)トークンをNode.jsから手動で焼いてみる

f:id:serinuntius:20190312115117p:plain

はじめに

念の為書いておきますが、こちらの方法でトークンを作るのに失敗したりしても、自己責任でお願いします。 テストネットで十分検証した上で、メインネットでやりましょう。

準備

mkdir slp_test
cd slp_test
npm init
npm install slp-sdk --save

NodeのREPLでmnemonicを生成します。

$ node
const BITBOXSDK = require('bitbox-sdk')
const BITBOX = new BITBOXSDK()
BITBOX.Mnemonic.generate(128)

'absurd unfair anchor loyal drill twin letter banana bright approve tone cube'

この12個の単語は秘密鍵みたいなものなので、絶対に他人に漏らしてはいけません。 GitHubにもコミットすべきではないでしょう。

アドレスを生成して、送金しておく

const BITBOXSDK = require('bitbox-sdk')
const SLPSDK = require('slp-sdk')
const SLP = new SLPSDK()
const BITBOX = new BITBOXSDK()

const mnemonic = 'absurd unfair anchor loyal drill twin letter banana bright approve tone cube'

let seedBuffer = BITBOX.Mnemonic.toSeed(mnemonic)


// 今回はtestnet用のアドレスを生成します。
// メインネットなら BITBOX.HDNode.fromSeed(seedBuffer)
let hdNode = BITBOX.HDNode.fromSeed(seedBuffer, 'testnet')

const wif = BITBOX.HDNode.toWIF(hdNode)


const address = BITBOX.HDNode.toCashAddress(hdNode)
console.log(address)
node index.js
bchtest:qzghg4u00s4n2yf3emlcqaxp64n0de2mas7fg4v4qw

bitcoin.com さんに感謝しつつ、テストネット用のBCHを

Testnet BCH Faucet - Bitcoin.com Developer Platform

からいただきます。

実際に作ってみる

const BITBOXSDK = require('bitbox-sdk')
const SLPSDK = require('slp-sdk')
const SLP = new SLPSDK()
const BITBOX = new BITBOXSDK()

const mnemonic = 'absurd unfair anchor loyal drill twin letter banana bright approve tone cube'

let seedBuffer = BITBOX.Mnemonic.toSeed(mnemonic)

let hdNode = BITBOX.HDNode.fromSeed(seedBuffer, 'testnet')

const wif = BITBOX.HDNode.toWIF(hdNode)


const address = BITBOX.HDNode.toCashAddress(hdNode)
console.log(address)

const slp_address = SLP.Address.toSLPAddress(address)
console.log(slp_address)

SLP.TokenType1.create({
    fundingAddress: address, // 手数料を払うアドレス
    fundingWif: wif, // 手数料を払うWif(Wallet import format) 秘密鍵みたいなものかな?
    tokenReceiverAddress: slp_address, // 作ったtokenを受け取るアドレス
    batonReceiverAddress: slp_address, // (たぶん)あとでもっとトークンを増やしたい(mint)ってときに追加で焼く権利を持っているアドレス
    bchChangeReceiverAddress: slp_address, // お釣りを受け取るアドレス
    decimals: 4,  // 小数点以下が何桁か
    name: 'Shit Coin', // 名前
    symbol: 'shit', // 単位
    documentUri: 'http://shit.org', // トークンのドキュメントのURL
    documentHash: null, // トークンのドキュメントのhash
    initialTokenQty: 100000000000, // 最初に発行するトークンの量
}).then(tokenID => console.log).catch(err => console.log)
node index.js
bchtest:qzghg4u00s4n2yf3emlcqaxp64n0de2mas7fg4v4qw
slptest:qzghg4u00s4n2yf3emlcqaxp64n0de2mas9a0wkzjn
03c8dfe1f8f00270ac01bc3194bb9dbe84efc3d391f13aec22ea419bcb33d29a

最後に出力される 03c8dfe1f8f00270ac01bc3194bb9dbe84efc3d391f13aec22ea419bcb33d29a がtoken idと呼ばれ、このトークンのgenesisトランザクションとなります。 このtoken idがトークンをユニークであると証明するためのidであり、追加で発行する(mint)する際にも必要になるようです。*1

bitcoin.comさんに感謝しつつ、テストネットのエクスクプローラーでtoken idを入力してみましょう。

Bitcoin Cash TESTNET (tBCH) Block Explorer

f:id:serinuntius:20190312113300p:plain

こちらのようにOP_RETURNに刻まれていれば成功です。

f:id:serinuntius:20190312113554p:plain

おわりに

結構簡単にトークンの発行ができたと思います。

次回はメインネットで発行して、誰かに送信するのをやってみたいと思います。

参考

SLP: Getting Started - Bitcoin.com Developer Platform

*1:実際はただのtx id何ですけどねw

Ubuntu18.04にOpenCV 4.0をインストールする

環境

依存パッケージをインストールする

sudo apt install -y libxrender-dev libsm6 libxext6

本体をインストールする

pip install opencv-python opencv-contrib-python

確認

$ python3
Python 3.6.7 (default, Oct 22 2018, 11:32:17)
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
>>> cv2.__version__
'4.0.0'

参考

こちらだけでは依存パッケージがなくてエラー出た

Python 3.6 に OpenCV 3.4 など環境を構築する - Qiita

解決策

python - ImportError: libSM.so.6: cannot open shared object file: No such file or directory - Stack Overflow

ひとこと

いつの間にか、4.0になってる・・・!

cryptographyを使って暗号化したファイルをOpenSSLで復号する

タイトルの通りです。

Pythoncryptographyというパッケージがあるのですが、それを使って暗号化したファイルをOpenSSLのコマンドを叩いて復号するのに躓いたのでメモです。

cryptographyで暗号化する

import os
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import padding

backend = default_backend()

# どんな長さの暗号化対象が来てもブロックサイズが一定になるように、パディングする
padder = padding.PKCS7(128).padder()


def encrypt(target_data: bytes) -> (bytes, str, str):
    # 初期ベクトル(同じkeyを利用しても結果が毎回ランダムになるように)
    iv = os.urandom(16)

    # 256byteの鍵の生成
    key = os.urandom(32)

    # AESという共通鍵暗号アルゴリズムを利用し、CBCという暗号モードで初期ベクトルを設定する
    cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=backend)
    encryptor = cipher.encryptor()

    # パディングする
    padded_data = padder.update(target_data) + padder.finalize()

    # 暗号化する
    encrypted_bytes = encryptor.update(padded_data) + encryptor.finalize()

    # openssl で復号するなら下記のコマンド
    # keyとivを16進数にしているのがポイントです
    print(f'openssl enc -aes-256-cbc -d -in encrypted_mufufu.png -nosalt -K {key.hex()} -iv {iv.hex()} -out out.png')

    return encrypted_bytes, key, iv


def decrypt(encrypted_data: bytes, key: bytes, iv: bytes) -> bytes:
    # AESという共通鍵暗号アルゴリズムを利用し、CBCという暗号モードで初期ベクトルを設定する
    cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=backend)
    decryptor = cipher.decryptor()
    decrypted_bytes = decryptor.update(encrypted_data) + decryptor.finalize()
    return decrypted_bytes


if __name__ == '__main__':
    with open('mufufu.png', 'rb') as mufufu:
        data = mufufu.read()

    _encrypted_data, _key, _iv = encrypt(data)

    with open('encrypted_mufufu.png', 'wb') as mufufu:
        mufufu.write(_encrypted_data)

    decrypted_data = decrypt(_encrypted_data, _key, _iv)

    with open('decrypted_mufufu.png', 'wb') as mufufu:
        mufufu.write(decrypted_data)

実行する

python main.py 
openssl enc -aes-256-cbc -d -in encrypted_mufufu.png -nosalt -K 66c7622c137739fa8d1c10111695188d966d10dfec70e26f5905ea11da57bbb3 -iv 5c81d38913ef067fb24f9db566518190 -out out.png

このような結果になると思います。

この実行結果をコピーして実行すると、復号されるはずです。

typed-astの依存がぶっ壊れてpipenv install出来なくなるのに対処した

タイトルの通りです。

pipenv install したら、昨日まで動いていたであろうPipfileでinstallがコケるようになりました。

対策

astroidというパッケージで、mypyとの依存関係が壊れてしまっているようです。

なので、mypyを0.670から0.660に下げると解決します。

Pipfileをお使いの方は

mypy = "==0.660"

にして、 pipenv install すれば直ると思います。

参考

海外の方めちゃくちゃ反応早い・・・

ありがたい限りです。 github.com

CoinomiWalletが何故秘密鍵の流出をしてしまったか雑に検証する

f:id:serinuntius:20190228163123p:plain

事の発端

このツイートにより、GoogleのスペルチェックAPIによってニーモニックが流出してしまっているとのこと。

なぜそんなことが起こったか気になったので調べてみる

下記のCoinomiWalletの公式サイトからMac版をダウンロードする

www.coinomi.com

v1.0.5*1を本記事では検証しました。

f:id:serinuntius:20190228153426p:plain

Windows, Mac, Linuxと対応していたので、Electronを使っているのではないかと当たりをつけてました。

f:id:serinuntius:20190228153900p:plain

右クリック→パッケージの内容を表示 とすると、アプリのディレクトリが見れます。

f:id:serinuntius:20190228154130p:plain

ElectronではなくJavaだった!

coinomi-wallet-mac.jar をデコンパイルする

Javaだったのでデコンパイルする必要があります。

下記のデコンパイラを使用します。

jd.benow.ca

f:id:serinuntius:20190228154546p:plain

f:id:serinuntius:20190228154637p:plain

f:id:serinuntius:20190228154901p:plain chromium(Google Chromeのベース部分)で動いてそうなのが伺えます。

AppWindow.class を確認してみると SpellCheckerService というのが利用されてますが、 setEnabled(false) とスペルチェックをオフにしています。 OAuthWindow.class も同様にoffにされています。

f:id:serinuntius:20190228155357p:plain

あれ、offにされてるのに送信されるの?バグなの??

ここで冷静になる

Twitterで報告されるような脆弱性対応終わってないわけないでしょ。修正済みなのでは?」

と思って、公式Twitter見てみたら・・・

公式声明が出てました。

そらそうですよね。

公式声明まとめ

  • restore時のみスペルチェックが発動してもれる
  • GoogleAPIAPIキーが含まれておらず、正常には処理できていなかった
  • モバイルは大丈夫
  • もう修正済みで自動アプデされる

chromiumとjxBrowser

jxBrowserというjavachromiumラッパー(?)がちょうど1週間前にアップデートされています。

f:id:serinuntius:20190228160818p:plain

jxbrowser.support.teamdev.com

chromiumがデフォルトでGoogleAPIを利用して通信してしまうので、それをオフにできるような修正が入っていました。 セキュリティ要件等で厳しい時は、それをオフにするといいとのこと。

まとめ

  • CoinomiWalletのデスクトップ版のみこのバグがあったが既に修正済み(自動でアプデ配信されているようです)
  • chromium便利だけど、こういう時びっくり挙動があったりでびっくりする(かも)

*1:checksum: 39efb726c8993af435710be6fda4c9ab808f39b7601951168d3536979be649c0

恥ずかしながら私はPromiseのthenとcatchの順番に意味があるとは知りませんでした

どういうこと?

catch => thenで書いたときとthen => catchで書いたときの挙動が異なります。

次のコードを用意しました。

ボタンを左から順番に押してみるとどういうことかわかると思います。

See the Pen then catchの順番によって意図しないコードになる by serinuntius (@serinuntius) on CodePen.

そうです。最後のボタンを押したときだけ2回alertが出ましたね。

最後のボタンは catch => thenの順序で描いて、条件的にcatchに入るパターン です。

使い分けると便利?かもしれません。おそらく大半のケースで then => catch という順序で書いた方が意図する実行結果になるということがわかりました。

今回はたまたまFirebaseのドキュメントを見ながら書いていて、catchが書いてあったからそこにthenを足したことで気づきましたw