- 平文ブロックと暗号文ブロックは一対一の関係
- ECBモード(単純なブロック暗号の利用法)では、ある鍵で同一の平文ブロックを暗号化すると、同一の暗号文ブロックになる。→同じブロックかつ同じ鍵は同じ暗号文ブロック
- 同じ暗号文ブロックがあれば「平文が繰り返されていること」はわかる
- Cut&Paste Attack可能(ブロック単位で改竄や複製できる)
- ECBでは暗号文を解読しなくても"いい感じに"改ざんや複製などが容易い
int encrypt (int plain, int key) {
return plain*key*2;
}
void ecb(int (&cipher)[3]) {
int plain[] = {1,2,3};
int key = 1;
for(int i = 0; i < 3; i++) {
cipher[i] = encrypt(plain[i], key);
}
}
- "1つ前の暗号文ブロックと平文ブロックのXOR"を平文と見立てて、暗号化する
- つまり”1つ前の暗号文ブロック”と”平文ブロック”のXORで得た値を暗号化したもの=暗号文ブロック
- SSL/TLSで使われている(TLS1.3非推奨・IVを起点する攻撃が存在するため)
- 最初の平文ブロックを暗号化するときには「1つ前の暗号文ブロック」は存在しないので、「1つ前の暗号文ブロック」の代わりのビット列を1ブロック分用意=(Initialization Vector)する必要がある
- IVを乱数にしないと同じデータを送っていることがバレる
- 最初から順番に暗号化する必要がある
- 暗号化処理の並列化が不可能
- 暗号文ブロックが1つ破損したら復号できないブロックは2つ(暗号技術入門P92) (競プロ的には"破損した暗号ブロック数"+"ブロックの連結成分の数"復号できない平文ブロックが発生する)
- IVに対して攻撃は可能だが、暗号文ブロックに対する攻撃は、他の全てのブロックに影響を与えるため困難(暗号技術入門P93)
- 再生攻撃可能
- 暗号文と平文のXORをベクトルにする、PCBCというのもある
int encrypt (int plain, int key) {
return plain*key*2;
}
void cbc(int (&cipher)[3]) {
int plain[] = {1,2,3};
int key = 1;
int iv = 2; //今回は定数にします
int vec, tmp;
for(int i = 0; i < 3; i++) {
if(i == 0) {
tmp = encrypt(plain[i]^iv, key);
} else {
tmp = encrypt(plain[i]^vec, key);
}
vec = tmp;
cipher[i] = vec;
}
}
- 安全性の観点で欠陥があるため使われていない
- IVを0としてCBCモードで暗号化を行う
- 改ざん検知ができなくなる
- 鍵は固定長かつ既知の長さのメッセージにしか用いることはできない
- 変長メッセージにおいてもCBC-MACを安全に利用するためには3つの主な改良法が存在する。
- 鍵の入力長の分割
- メッセージ長の情報を先頭に追加する
- 最後のブロックを暗号化する
- CBCとほぼ一緒だが、CFBは平文を"直接"暗号化しない(前のブロックを暗号化し、それと平文をXORするので、間接的に暗号化は使われいている)
- 使い捨てパッドに似ており、IVは乱数のシード値に相当する
- 復号処理はない(平文はXORをとっているだけで、一つ前のブロックさえあれば復号処理は要らないから)
- 上図の方式は自己同期方式ではないが, そのように変形することができる
ブロック暗号の入力にシフトレジスタを組み合わせることで、CFBモードは自己同期型で利用することが可能となる。(wikipediaより)
- 自己同期型とは
- 暗号文をいじって復号できる平文を作りにくい、一箇所暗号文を変更すると他の箇所も変更されてしまうような構造。今回のサーベイではECB以外の全ての利用モードが該当する
- データがすり替えられやすい?
- 下図の通り、全部用意しておけばすり替え可能
- 再生攻撃可能
- ブロック2,3,4をすり替えるとブロック2はエラーだが、3,4はエラーが起こらない(このエラーが改竄かどうか認証しないとわからない)
int encrypt (int plain, int key) {
return plain*key*2;
}
void cfb(int (&cipher)[3]) {
int plain[] = {1,2,3};
int key = 1;
int iv = 2; //今回は定数にします
int vec, tmp;
for(int i = 0; i < 3; i++) {
if(i == 0) {
tmp = encrypt(iv, key);
} else {
tmp = encrypt(vec, key);
}
vec = tmp^plain[i];
cipher[i] = vec;
}
}
- 別名ICM(integer counter mode), SIC(segmented integer counter mode)
- IV(nonce)とカウンタ(1,2,3,...)からカウンタブロックを作り、それを暗号化し、平文とのXORをとる
- カウンタの初期値は[23 43 23 23 02 89 29 AB](nonce)+[00 00 00 00 00 00 00 01](カウンタ)で構成される(ここでの+は結合)
- nonceはランダム値、カウンタは1ずつ増やす
- 暗号化と復号の構造は全く同じ
- 並列実行可能(前から順番通りに暗号化・復号処理をしなくてもいい)
int encrypt (int plain, int key) {
return plain*key*2;
}
void ctr(int (&cipher)[3]) {
int plain[] = {1,2,3};
int key = 1;
int tmp, NonceCounter;
int nonce = 2;
int counter = 1;// 今回はnonce1桁、counter1桁とします
for(int i = 0; i < 3; i++) {
NonceCounter = nonce*10 + counter;
tmp = encrypt(NonceCounter, key);
cipher[i] = tmp^plain[i];
counter++;
}
}