質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

暗号化

ネットワークを通じてデジタルデータをやり取りする際に、第三者に解読されることのないよう、アルゴリズムを用いてデータを変換すること。

ハッシュ

ハッシュは、高速にデータ検索を行うアルゴリズムのことです。

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

Q&A

解決済

1回答

1667閲覧

[Go]ハッシュ値が一致せず、困っています

ts21

総合スコア32

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

暗号化

ネットワークを通じてデジタルデータをやり取りする際に、第三者に解読されることのないよう、アルゴリズムを用いてデータを変換すること。

ハッシュ

ハッシュは、高速にデータ検索を行うアルゴリズムのことです。

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

0グッド

0クリップ

投稿2021/09/11 06:31

やりたいこと

GolangでHashChainを作成し、それを検証するコードを書いています。
Goで4つの要素(ブロック)からなるハッシュチェーンを実装しています。
まずArrayに4つの要素を準備し、一つ目をハッシュ化します。2つ目以降の要素は前のハッシュとその要素を足してハッシュ化していきます。

このハッシュチェーン自体は実装できたのですが、この検証の段階で予期せぬ挙動が現れており、困っています。

検証とは、作成されたハッシュが入力要素と前のハッシュの値から成っているかどうかを確かめる過程です。
これを、i=3から遡って検証していく実装を目指しています。

コード

実際に書いたコードは次のようなものです

package main import ( "crypto/sha256" "fmt" "reflect" ) func main() { createHashChain() } func HashChain() { Array := [...]string{"0001", "0002", "0003", "0004"} h := sha256.New() //構造体を定義 type Block struct { Index int Data string Hash []byte } //構造体配列を定義 type BlockArray []*Block var hashChain BlockArray //Hashchainの作成 for i := 0; i < len(Array); i++ { if i == 0 { h.Write([]byte(Array[i])) hashChain = append(hashChain, &Block{i, Array[i], h.Sum(nil)}) fmt.Printf("Array %x = %x \n", i, []byte(Array[i])) fmt.Printf("Block %x Hash = %x \n", i, hashChain[i].Hash) fmt.Printf("------------------------------------------------------------------------------------- \n") } else { message := append([]byte(Array[i]), hashChain[i-1].Hash...) fmt.Printf("Array %x = %x \n", i, []byte(Array[i])) fmt.Printf("Block %x Hash = %x \n", i-1, hashChain[i-1].Hash) h.Write([]byte(message)) hashChain = append(hashChain, &Block{i, Array[i], h.Sum(nil)}) fmt.Printf("Block %x Hash = %x \n", i, hashChain[i].Hash) fmt.Printf("-------------------------------------------------------------------------------- \n") } } fmt.Printf("================================================================================\n")                                  //HashChainの検証 for i := len(hashChain) - 1; i >= 0; i-- { fmt.Printf("Array %x = %x \n", i, []byte(Array[i])) if i == 0 { h.Write([]byte(Array[i])) fmt.Printf("Block %x Hash = %x \n", i, hashChain[i].Hash) } else { fmt.Printf("Block %x Hash = %x \n", i-1, hashChain[i-1].Hash) message := append([]byte(Array[i]), hashChain[i-1].Hash...) h.Write([]byte(message)) fmt.Printf("Block %x Hash = %x \n", i, h.Sum(nil)) } if reflect.DeepEqual(hashChain[i].Hash, h.Sum(nil)) { fmt.Printf("Block %x is valid \n", i) } else { fmt.Printf("Block %x is invalid \n", i) } fmt.Printf("-------------------------------------------------------------------------------- \n") } }

実行結果

出力から、以下のことがわかりました

  • ハッシュチェーンは実装できている
  • 検証の際に、要素と前のハッシュのペアは間違っていない
  • しかし、このペアから出力されるハッシュの値は、作成されたhashchainのそれと一致しない
Array 0 = 30303031 Block 0 Hash = 888b19a43b151683c87895f6211d9f8640f97bdc8ef32f03dbe057c8f5e56d32 ------------------------------------------------------------------------------------- Array 1 = 30303032 Block 0 Hash = 888b19a43b151683c87895f6211d9f8640f97bdc8ef32f03dbe057c8f5e56d32 Block 1 Hash = 6dac394913764d4e0c4beed3aae22375fa8692079798d7dd6502e132935311d3 -------------------------------------------------------------------------------- Array 2 = 30303033 Block 1 Hash = 6dac394913764d4e0c4beed3aae22375fa8692079798d7dd6502e132935311d3 Block 2 Hash = fe93f79a1f6ac296c29bd9c14bdc9663ad919e70ef3111b64b37949eddd63e4b -------------------------------------------------------------------------------- Array 3 = 30303034 Block 2 Hash = fe93f79a1f6ac296c29bd9c14bdc9663ad919e70ef3111b64b37949eddd63e4b Block 3 Hash = 77693c0422e8e2f5fb5cec27e6ccac0ebc6eff93655ae9fd7e983332ba59ca26 -------------------------------------------------------------------------------- ================================================================================ Array 3 = 30303034 Block 2 Hash = fe93f79a1f6ac296c29bd9c14bdc9663ad919e70ef3111b64b37949eddd63e4b Block 3 Hash = 2bb9d7434a81d0174832beb2d6f1199e6684d4059c3f3ee80985a656cf292530 Block 3 is invalid -------------------------------------------------------------------------------- Array 2 = 30303033 Block 1 Hash = 6dac394913764d4e0c4beed3aae22375fa8692079798d7dd6502e132935311d3 Block 2 Hash = 5f3fb52d22ad38f461277d051da01a493e6d58ffae22e3785463baa78fd575c9 Block 2 is invalid -------------------------------------------------------------------------------- Array 1 = 30303032 Block 0 Hash = 888b19a43b151683c87895f6211d9f8640f97bdc8ef32f03dbe057c8f5e56d32 Block 1 Hash = 39bccdb509a3c3024cee6eaa59c97b304cc0b09008cfeff124b2eeddf58df1cd Block 1 is invalid -------------------------------------------------------------------------------- Array 0 = 30303031 Block 0 Hash = 888b19a43b151683c87895f6211d9f8640f97bdc8ef32f03dbe057c8f5e56d32 Block 0 is invalid --------------------------------------------------------------------------------

sha256は暗号学的ハッシュ関数なので、入力値が同じならば出力されるハッシュ値は常に同じになるはずですが、この通りの結果になっています。

直接的な解決方法でなくても何かの基礎的知識が抜けているのであれば、そのあたりをご指摘もらえると幸甚です。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

ハッシュ計算時のh := sha256.New()をそのまま検証計算時に使い続けてはいけません。
検証用にsha256.New()をもう一度する必要があります。

go

1package main 2 3import ( 4 "crypto/sha256" 5 "fmt" 6 "reflect" 7) 8 9func main() { 10 createHashChain() 11} 12 13func createHashChain() { 14 Array := [...]string{"0001", "0002", "0003", "0004"} 15 16 //構造体を定義 17 type Block struct { 18 Index int 19 Data string 20 Hash []byte 21 } 22 23 //構造体配列を定義 24 type BlockArray []*Block 25 var hashChain BlockArray 26 //Hashchainの作成 27 h := sha256.New() 28 for i := 0; i < len(Array); i++ { 29 if i == 0 { 30 h.Write([]byte(Array[i])) 31 hashChain = append(hashChain, &Block{i, Array[i], h.Sum(nil)}) 32 fmt.Printf("Array %x = %x \n", i, []byte(Array[i])) 33 fmt.Printf("Block %x Hash = %x \n", i, hashChain[i].Hash) 34 fmt.Printf("------------------------------------------------------------------------------------- \n") 35 36 } else { 37 38 message := append([]byte(Array[i]), hashChain[i-1].Hash...) 39 fmt.Printf("Array %x = %x \n", i, []byte(Array[i])) 40 fmt.Printf("Block %x Hash = %x \n", i-1, hashChain[i-1].Hash) 41 42 h.Write([]byte(message)) 43 hashChain = append(hashChain, &Block{i, Array[i], h.Sum(nil)}) 44 fmt.Printf("Block %x Hash = %x \n", i, hashChain[i].Hash) 45 fmt.Printf("-------------------------------------------------------------------------------- \n") 46 47 } 48 } 49 50 fmt.Printf("================================================================================\n") 51 //HashChainの検証 52 //for i := len(hashChain) - 1; i >= 0; i-- { 53 h = sha256.New() // <- 検証用に作り直し 54 for i := 0; i < len(Array); i++ {  // ループの組み方もハッシュ計算時と合わせる必要があります。 55 56 fmt.Printf("Array %x = %x \n", i, []byte(Array[i])) 57 58 if i == 0 { 59 h.Write([]byte(Array[i])) 60 fmt.Printf("Block %x Hash = %x \n", i, hashChain[i].Hash) 61 } else { 62 fmt.Printf("Block %x Hash = %x \n", i-1, hashChain[i-1].Hash) 63 message := append([]byte(Array[i]), hashChain[i-1].Hash...) 64 h.Write([]byte(message)) 65 fmt.Printf("Block %x Hash = %x \n", i, h.Sum(nil)) 66 } 67 res := h.Sum(nil) 68 fmt.Printf("%x : %x", hashChain[i].Hash, res) 69 70 if reflect.DeepEqual(hashChain[i].Hash, res) { 71 fmt.Printf("Block %x is valid \n", i) 72 } else { 73 fmt.Printf("Block %x is invalid \n", i) 74 } 75 fmt.Printf("-------------------------------------------------------------------------------- \n") 76 } 77} 78

ハッシュについて

Gitのコミットハッシュはご存じですか?
ある「ハッシュ値」はリポジトリに「過去のすべての積み上げられた変更の履歴」の「一意性」を天文学的な確率で保証するものです。

つまりコミットハッシュは大まかに以下の手順で作られます

  • ファイル名でソートされた初期投入コードのテキストを
  • その後は変更差分を
  • 順番を守って
  • ハッシュ計算を積み上げたもの

つまりある「ハッシュ値」は最終的なソースコードセットの状態が「一意」であることが期待できるということです。

そして、ある別のソースと履歴をハッシュ値に計算した結果が同じ「ハッシュ値」になることを「コンフリクト」といいます。

ハッシュアルゴリズムはこの「コンフリクト」を意図的に起こすのを困難にすることを目標に改良されてきました。実際に古いハッシュアルゴリズムではコンフリクトする複数のデータ列が発見されたりしています。最新のハッシュアルゴリズムにもコンフリクトを起こすデータ列がきっとあるだろうと探すような研究を行っているところもあります。

要するに、「一定のオーダーで一定の長さのデータ列Aに対し計算されたハッシュ値」は
別のところで「一定のオーダーで一定の長さのデータ列Bに対し計算されたハッシュ値」と一致するとき、「データ列Aとデータ列Bは同じであることがほぼほぼ保証される」ということです。

つまり離れた場所でデータ列そのものを送らなくてもハッシュだけ送って元データが同じかどうか比較できるのが「ハッシュ」です。

元のコードではハッシュ積算用のインスタンス一つにあるデータ列を投入しこの時点のハッシュ値Aと、さらにそのデータ列のオーダーを入れ替えたものを投入した結果のハッシュ値Bを比較しようとしています。これはGitハッシュでいうと変更を積み上げた前と後のソースコードセットを比較しているようなものなのでハッシュが異なるのは自然というか当たり前なのです。

投稿2021/09/11 14:16

編集2021/09/11 19:11
nobonobo

総合スコア3367

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ts21

2021/09/11 15:01

ご回答いただきありがとうございます。おかげさまで期待していた挙動を得ることができました!
ts21

2021/09/11 15:01

質問ですが、なぜsha256を作り直す必要があったのでしょうか? また、逆順で検証する実装ではなぜいけなかったのか考察してみたのですが自己解決に至りませんでした。この点に関しても大変恐縮ですが概説していただけないでしょうか?
nobonobo

2021/09/11 19:30

追記しました。また、バイト列はreflect.DeepEqualじゃなく、bytes.Equalを使うとよいですよ!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問