読者です 読者をやめる 読者になる 読者になる

Got Some \W+ech?

Could be Japanese. Could be English. Android, セキュリティ, 機械学習などをメインに、たまにポエムったり雑感記載したりします。

GoでAESアルゴリズムを使った実装をする (と暗号歴史を一部紹介)

実はCISSPを受験しようと思ってて、最近その勉強をしている。(結構長く勉強してるはずなんだけど、業務が忙しくて実質時間が書けられてなくて進捗だめであああああああああああああああああああああああああああああああ)。まあ、こういうこと書いてるからなのかもしれないんだけど、しかたがない(しかたなくない)

CISSP自体に暗号はドメインとして含まれていないのだが、ドメインをまたがる問題として出題されているので、結城先生の「暗号技術入門」を読書している。

せっかくなので、そのアウトプットとしてGoでAES・CBCモードのSecret Keyを実装してみることにした。 余談だが、共通鍵方式はSecret Key, Common Key, Shared Key...など色々あるようだが、CISSP(の問題)ではSecret Keyに統一されているので、本投稿もそれに合わせる。

尚、@ken5scalは暗号技術の専門家ではないので、間違いはあると思う。何か見つけた場合は是非連絡を頂きたい。

AESアルゴリズム

さて、AESアルゴリズムの実装に入る前に、その説明だけしておこう。 これは別名Rijndaelアルゴリズムともいい、DESが機械の性能向上により物理的敗北を喫したのをきっかけに、NIST(米国立標準技術研究所)によりFIPS(連邦情報処理標準規格)と認定された。これにより保管データ暗号化時の推奨アルゴリズムとして、デファクトとなる。@ken5scalの使うMBPのディスク暗号化機能「FileVault」もAESアルゴリズムを利用している。

面白いのは、このアルゴリズムがNISTによって秘密に作成・運用されているのではなく、DESに変わるSecret Keyアルゴリズムを選定するコンペで採用されたものであり、公開されていることだ。wikiにも結城先生本にも記載されてるし、NISTもがっつり公開している。*1

実は米国は、AESが選定されるまで暗号に関しては非常にクローズな立場をとっていた。1992年の時点では暗号化ソフト(含むアルゴリズム)をAuxillary(補助的?)軍需品と指定し、輸出規制の対象としていたぐらいである。具体的にはアルゴリズム・鍵長(40bit以上)で規制をかけてた*2。当然、通信の暗号化などプライバシーの強化に大活躍したが、それと同時に犯罪者によっても悪用される。従って、法執行当局のシギント的捜査が難航するからコントロールしようとするのは当然のことだろう。包丁をコンビニに置くと...みたいな話に感じそうだが、当時の背景(第2次世界対戦後と冷戦後)を考慮すると、「せやな」と思えなくもない。当局による取り組みの1例としては、1994年に「鍵預託」(key escrow)というシステムを組み込んだ「預託暗号標準(EES)」があるだろう。ホワイトハウスはこれを承認し、電話通信・コンピューター通信に個人の秘密鍵を埋め、同時に鍵のコピーを連邦当局に保存する方向に持っていこうとしてた*3。日本のマイナンバーの前身みたいなものだろうか。

でも、結局のところ、規制を米国ほどかけたくないヨーロッパや市場からの反応により、管轄が1996年に国務省(Department of State)から商務省(Department of Commerce)に移管され、それとともに軍需品の対象ではなくなった。そして、その後色々あって(知らない)、2000年のAES選定につながる。ただし、現在でも輸出規制が完全になくなったかというとそうではなく、Secrete Keyアルゴリズムは鍵長64bit超のもの且つマスマーケットになっていないものが輸出規制対象となっている。*4

なお、米国に暗号の輸入出規制があると書いたが、別に米国に限ったものではない。武器輸出を規制するワッセナー・アレンジメントに調印した国や、独自の基準をもつ中国など、様々な規制があって面白いので、別途調べてみたい。*5

AESアルゴリズムとは(リトライ)

AESルゴリズムはブロック暗号アルゴリズムで、固定長(128bit)のブロックを、128/192/256bitの鍵で暗号化するもの。この暗号化は複数回行われ、各回(ラウンド)ではSubBytes -> ShiftRows -> MixColumns -> AddRoundKeyするもの。それぞれの解説は図が絡むのでしないw 結城先生の「暗号技術入門」を買ってください。実装といっても、そんな難しくはなく、すでにGoでは標準パッケージ「crypto/aes」で容易されているので、それを使ってやればおk。

// AESによる暗号化
func EncryptByBlockSecretKey(key []byte, plainText string) ([]byte, error) {
    c, err := aes.NewCipher(key); if err != nil { // NewCipherで暗号オブジェクトを作る。
        return nil, err
    }
    cipherText := make([]byte, aes.BlockSize)

    c.Encrypt(cipherText, []byte(plainText)) // Input/Output must be 16bits large
    return cipherText, nil
}

func DecryptByBlockSecretKey(key []byte, cipherText []byte) string {
    c, err := aes.NewCipher(key); if err != nil { // NewCipherで暗号オブジェクトを作る。
        fmt.Println(err.Error())
        return ""
    }

    plainText := make([]byte, aes.BlockSize)
    c.Decrypt(plainText, cipherText)

    return string(plainText)
}

func main() {
    plainText := "1234123412341234"

    key := []byte("1234123412341234123412341234123") // あえてAES規格ではない鍵長を使う
    fmt.Println(EncryptByBlockSecretKey(key, plainText) 
        # crypto/aes: invalid key size 31                                       <- 暗号オブジェクト作成時にKeySizeErrorが発生

        key = []byte("1234123412341234") // AES-128
    fmt.Println(EncryptByBlockSecretKey(key, "12341234123412345")) // Longer than 16 byte
        # [196 235 186 96 98 151 252 89 132 220 117 226 229 247 4 48] <nil> <- 1ブロック以上は処理対象にならない

    fmt.Println(EncryptByBlockSecretKey(key, "123412341234123")) // Shorter than 16 byte
        # panic: crypto/aes: input not full block

    cipherText,_ := EncryptByBlockSecretKey(key, plainText)
    fmt.Println(cipherText)                     
        # [196 235 186 96 98 151 252 89 132 220 117 226 229 247 4 48]

        plainText = DecryptByBlockSecretKey(key, cipherText)
    fmt.Println(plainText)
        # 1234123412341234 <- 復号できた。
}

しかし、これでは固定長の平文しか処理できない。固定長以上の平文を暗号化したい場合、16byteのブロック長にきって暗号化を繰り返せばいいのだが、これをブロック暗号のモードを実装しなければいけない。これを次回は実装してみる。

追記( @bata_24さん ありがとうございます)_

  • 復号化 vs 復号 前職のパイセンにもよく言われていたが、本来"復号化”は国語的におかしいみたいだ。なぜなら「復号」は暗号文を元に戻すという意味の動詞だからそうだ。暗号は名詞以外の定義はない。よって、暗号化の対義語は復号になる。ただし、結城本は復号化だが...って思ってたら、字面の対称性を重視しているらしい*6。ま、職場でもよく復号化...と言われてるが、とりあえず本投稿は復号に寄せてる。怒られそうだけど、正直どっちでもいい

  • Rijndael vs AES Rijndaelは厳密には128, 160, 192, 224, and 256bitsの鍵長をサポートしている。また、128, 160, 192, 224, and 256bitsのブロックサイズもサポートしている。AESは鍵長を128,192 and 256bits、ブロックサイズを128bitsに絞った点で異なる。

暗号解読〈上〉 (新潮文庫)

暗号解読〈上〉 (新潮文庫)

暗号解読 下巻 (新潮文庫 シ 37-3)

暗号解読 下巻 (新潮文庫 シ 37-3)

暗号技術入門 第3版 秘密の国のアリス

暗号技術入門 第3版 秘密の国のアリス