Opensslのsha 3を使用してイーサ坊アカウントアドレスを生成

4703 ワード

以太坊口座住所
ブロックチェーンは暗号学に基づく技術であることが知られており、エーテル坊の口座などの規則はEC(楕円曲線)という体系を用いて行われている.エーテル坊が用いているECは有名なsecp 256 k 1曲線であり、本質的にエーテル坊口座はECDSAの秘密鍵であり、ある秘密鍵を把握すれば秘密鍵を用いて取引に署名することができるため、この口座を完全に把握しました.一方、ECの秘密鍵は、secp 256 k 1曲線の最大値が0 xFFFFFFFFFFFFFFFFFFFEBAAEDCE 6 AF 48 A 03 BBFD 25 E 8 CD 0364141の範囲内の乱数である.アカウントのアドレスは、その秘密鍵を用いて生成された公開鍵のhashである.ECの公開鍵は楕円曲線のある秘密鍵に対応する点である.公開鍵と秘密鍵の関係は簡単に言えばPublickey=Privatekey*Gであり、Gはある楕円曲線の基点であり、ここでの*は伝統的な意味での乗ではなく、具体的な内容は楕円曲線楕円域に関する知識を参照してください.幸いなことにOpenSSLライブラリは完全な楕円曲線相関計算を提供するので,楕円曲線の具体的な実装の詳細に注目する必要はない.
簡単に言えば、秘密鍵からアドレスまで3つのステップが必要です.
  • 公開鍵
  • を生成する.
  • Hash
  • を生成する.
  • 剪断Hash
  • イーサー坊はKeccak 256アルゴリズムを用いてHashを生成し,生成したHashは32バイトであるが,イーサー坊のアドレスは後20バイトのみをアドレスとして取った.
    SHA 3とKeccak
    イーサー坊のソースコードから分かるように、イーサー坊アカウントアドレスのHashアルゴリズムはKeccak 256を使用しているが、Keccakアルゴリズムは2015年にSHA 3標準アルゴリズムとなった.しかし、太坊が使用するKeccak 256はSHA 3 256に等しくなく、SHA 3アルゴリズムとなるとNISTはKeccakを変更した.エーテル坊では実際には標準的なSHA 3アルゴリズムが実現されているが,使用されていない.
    // NewKeccak256 creates a new Keccak-256 hash.
    func NewKeccak256() hash.Hash { return &state{rate: 136, outputLen: 32, dsbyte: 0x01} }
    // New256 creates a new SHA3-256 hash.
    // Its generic security strength is 256 bits against preimage attacks,
    // and 128 bits against collision attacks.
    func New256() hash.Hash { return &state{rate: 136, outputLen: 32, dsbyte: 0x06} }

    実はこの2つのアルゴリズムは1つのdsbyteしか欠けていないことがわかりますが、このdsbyteはここにあります.
    	// Pad with this instance's domain-separator bits. We know that there's
    	// at least one byte of space in d.buf because, if it were full,
    	// permute would have been called to empty it. dsbyte also contains the
    	// first one bit for the padding. See the comment in the state struct.
    	d.buf = append(d.buf, dsbyte)

    使われて、注釈を見て発見して、このdsbyteはpadです.
    OpenSSLの最新バージョン1.1.1には非標準のKeccak 256はなく、SHA 3 256のみ
    typedef struct {
        uint64_t A[5][5];
        size_t block_size;          /* cached ctx->digest->block_size */
        size_t md_size;             /* output length, variable in XOF */
        size_t num;                 /* used bytes in below buffer */
        unsigned char buf[KECCAK1600_WIDTH / 8 - 32];
        unsigned char pad;
    } KECCAK1600_CTX;
    
    static int init(EVP_MD_CTX *evp_ctx, unsigned char pad)
    {
        KECCAK1600_CTX *ctx = evp_ctx->md_data;

    OpenSSLのSHA 3実装を見ると、実はそのpadがKECCAK 1600_に存在していることがわかります.CTXではKECCAK 1600_CTXはsha 3初期化時にEVP_が存在するMD_CTXでは,OpenSSLを用いてKeccak 256を実現するには,SHA 3 256のpadをいくつかの手段で変更する必要がある.
    OpenSSLによるアドレス生成
    まずEC関連コンテンツの初期化と公開鍵と秘密鍵の生成
    	BIGNUM* privatekey = BN_new();
    	BIGNUM* n = BN_new();
    	BN_CTX *bn_ctx = BN_CTX_new();
    	const EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp256k1);
    	EC_GROUP_get_order(group, n, bn_ctx);
        BN_rand_range(privatekey, n);
    	EC_POINT *publickey = EC_POINT_new(group);
    	EC_POINT_mul(group, publickey, privatekey, nullptr, nullptr, bn_ctx);
    	unsigned char **buf = (unsigned char **)malloc(8);
    	size_t size = EC_POINT_point2buf(group, publickey, POINT_CONVERSION_UNCOMPRESSED, buf, bn_ctx);

    そしてEVP関連を初期化
    	const EVP_MD* evp_md = EVP_sha3_256();
    	EVP_MD_CTX *evp_md_ctx = EVP_MD_CTX_new();
        EVP_DigestInit(evp_md_ctx, evp_md);

    次にpadを変更します.OpenSSLのヘッダファイルには修正する構造体の定義がありませんので、ソースバーからコピーする必要があります.
    struct EVP_MD_CTX_t {
    	const EVP_MD *digest;
    	ENGINE *engine;             /* functional reference if 'digest' is
    								            * ENGINE-provided */
    	unsigned long flags;
    	void *md_data;
    	/* Public key context for sign/verify */
    	EVP_PKEY_CTX *pctx;
    	/* Update function: usually copied from EVP_MD */
    	int(*update) (EVP_MD_CTX *ctx, const void *data, size_t count);
    } /* EVP_MD_CTX */;
    
    struct KECCAK1600_CTX {
    	uint64_t A[5][5];
    	size_t block_size;          /* cached ctx->digest->block_size */
    	size_t md_size;             /* output length, variable in XOF */
    	size_t num;                 /* used bytes in below buffer */
    	unsigned char buf[1600 / 8 - 32];
    	unsigned char pad;
    };

    padを変更して公開鍵をEVPに渡す
    	//change the sha3 pad 0x06 to keccak pad 0x01
    	KECCAK1600_CTX* keccak256 = reinterpret_cast((reinterpret_cast(evp_md_ctx))->md_data);
    	keccak256->pad = 0x01;
    	EVP_DigestUpdate(evp_md_ctx, (*buf) + 1, size - 1);

    注意が必要なのは、POINT_を使うことです.CONVERSION_UNCOMPRESSED方式で生成されるbufの1バイト目は曲線タイプであり、後はXYの座標であるため、曲線タイプを除外する必要がある.
    最後に結果が得られる
    	unsigned int * len = new unsigned[10];
    	unsigned char* result = (unsigned char*)malloc(32);
    	EVP_DigestFinal(evp_md_ctx, result, len);

    生成された内容は,イーサー坊で生成されたアドレスと一致する.最後に20バイトを取ると以太坊の口座アドレスになります.