平文の暗号化 – 暗号処理の流れ

### 暗号化の流れ
(1)ブロック長に満たない部分をパディング方式で補完する
(2)アルゴリズムに対応したブロック長に分割する
※この時の分割ルールを決めるのが暗号利用モード
※分割モードによっては初期値が必要で、それがIV(初期ベクトル)
(3)暗号鍵を用いてアルゴリズムで暗号化
(4)分割されたデータを結合

複合化の流れ
(1)アルゴリズムに対応したブロック長に分割する
(2)暗号鍵を用いてアルゴリズムで複合化する
(3)暗号利用モードを元に分割されたデータを結合する
※分割モードによっては初期値が必要で、これがIV(初期化ベクトル)
(4)補完されたパディングをパディング方式で除去する

ブロック暗号では、ブロック長、秘密鍵、暗号アルゴリズム、暗号利用モード、パディング方式、IV(初期化ベクトル)のキーワードが重要
使用できる暗号化アルゴリズムは組み込まれているライブラリに依存
暗号利用モードにはECB, CBD, OFB, CFB
パディング方式はZeroBytePadding, パディング不要な状態であればZeroBytePaddingは何もしないのでPKCS#5 Paddingなどは独自に実装することができる。NoPaddingはできない

$ sudo apt install php-dev libmcrypt-dev php-pear
$ sudo pecl channel-update pecl.php.net
$ sudo pecl install mcrypt-1.0.1

function cipher_encrypt($input, $key){
  // 指定した暗号のブロックサイズを得る
  $size = mcrypt_get_block_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
  // PKCS5Padブロック長に満たないサイズを埋める
  $input = pkcs5_pad($input, $size);
  // 使用するアルゴリズムおよびモードのモジュールをオープンする
  $td = mcrypt_module_open(MCRYPT_BLOWFISH, '', MCRYPT_MODE_ECB, '');
  // オープンされたアルゴリズムのIVの大きさを返す
  $ivsize = mcrypt_enc_get_iv_size($td);
  // MCRYPT_RANDの初期化を行う
  srand();

  // 乱数ソースから初期化ベクトル(IV)を生成する
  // ECB以外では複合にIVが必要
  // ECBではIVは使用されないがIVがないとエラーが出る
  $iv = mcrypt_create_iv($ivsize, MCRYPT_RAND);
  // 暗号化に必要な全てのバッファを初期化
  mycrypt_generic_init($td, $key, $iv);
  // データを暗号化
  $data = mcrypt_generic($td, $input);
  // 暗号化モジュールを修了する
  mycrypt_generic_deinit($td);
  // mcryptモジュールを閉じる
  mcrypt_module_close($td);
  return $data;
}

// 複合化を行う
function cipher_decrypt($input, $key){
  // 指定した暗号のブロックサイズを得る
  $size = mcrypt_get_block_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
  // 使用するアルゴリズムおよびモジュールをオープンする
  $td = mcrypt_module_open(MCRYPT_BLOWFISH, '', MCRYPT_MODE_ECB, '');
  // オープンされたアルゴリズムのIVの大きさを返す
  $ivsize = mcrypt_enc_get_iv_size($td);
  // MCRYPT_RANDの初期化を行う
  srand();

  // 乱数ソースから初期化ベクトル(IV)を生成する
  // ECB以外では複合にIVが必要
  // ECBではIVは使用されないがIVがないとエラーが出る
  $iv = mcrypt_create_iv($ivsize, MCRYPT_RAND);
  // 暗号化に必要な全てのバッファを初期化する
  mcrypt_generic_init($td, $key, $iv);
  // データを複合する
  $data = mdecrypt_generic($td, $input);
  // 暗号化モジュールを修了
  mcrypt_generic_deinit($td);
  // mcryptモジュールを閉じる
  mcrypt_module_close($td);
  // PKCS5Padding埋められたバイト値を除く
  $data = pkcs5_unpad($data, $size);
  return $data;
}

// PKCS5Padding
// ブロック長に満たないサイズを埋める
function pkcs5_pad($text, $blocksize){
  $pad = $blocksize - (strlen($text) % $blocksize);
  return $text . str_repeat(chr($pad), $pad);
}

// PKCS5Padding
// 埋められたバイト値を除く
function pkcs5_unpad($text){
  $pad = ord($text{strlen($text)-1});
  if($pad > strlen($text)) return false;
  if(strspn($text, chr($pad), strlen($text) - $pad)!= $pad) return false;
  return substr($text, 0, -1 * $pad);
}

// 暗号化例
$data = "この文字を暗号化する";
$key = "ABCDEF";

echo "元データ:" . $data;
echo "<br>";
$encrypt = cipher_encrypt($data, $key);
echo "暗号化されたデータ:". $encrypt;
echo "<br>";
$decrypt = cipher_decrypt($encrypt,$key);
echo "複合化されたデータ:". $decrypt;

pecl/mcrypt requires PHP (version >= 7.2.0, version <= 7.3.0, excluded versions: 7.3.0), installed version is 7.4.3-4ubuntu2.18 No valid packages found install failed 仕組みはわかったけど、mcryptではなく、openssl_ecnrypt出ないと駄目やな

ブロック暗号の主なモード(ECB, CBC, CFB, OFB, CTR)

ECBモード: Electronic CodeBook mode(電子符号表モード)
CBCモード: Cipher Block Chaining mode(暗号ブロック連鎖モード)
CFBモード: Cipher-FeedBack mode(暗号フィードバックモード)
OFBモード: Output-FeedBack mode(出力フィードバックモード)
CTRモード: CounTeR mode (カウンタモード)

### ECBモード
平文ブロックを暗号化したものが、そのまま暗号文ブロックになる
最後の平文ブロックがブロック長に満たない場合にはパディングと呼ばれるデータを埋める
=> たくさんあるモードの中で最もシンプルだが最も機密性の低いモード
平文の中に同じ値を持つ平文ブロックが複数存在した場合、全て同じ値の暗号文ブロックに変換されてしまう。暗号文を見るだけで平文の繰り返しがわかってしまう

### CBCモード
CBCモードは1つ前の暗号文ブロックと平文ブロックのXORをとり、そのXORの値を暗号化を行う。CBCモードも最後の平文ブロックがブロック長に満たない場合にはパディングを行う
SSL/TLSではCBCモードが使われている
最初の平文ブロックを暗号化する際に、「1つ前の暗号文ブロック」は存在しないので、「1つ前の暗号ブロック」の代わりのビット列を1ブロック分用意する必要がある。このビット列をIV(initial vector)

### CFBモード
CFBモードは1つ前の暗号文ブロックを暗号アルゴリズムの入力に戻す
フィードバックとは、入力へ戻すということを意味する
CFBモードで暗号化して得られる値を鍵ストリームという
ECBやCBCモードでは暗号化アルゴリズムによって直接暗号化されるが、CFBは直接暗号化されるわけではない

### OFBモード
暗号アルゴリズムの出力を暗号アルゴリズムの入力へフィードバック
“1つ前の暗号ブロックを暗号アルゴリズムで暗号化した値”と”平文ブロック”のXOR=暗号文ブロックとなる
OFBでもCBC, CFBと同様にIVを使用する
平文ブロックと暗号アルゴリズムの出力とXORして暗号文ブロックを作っている CFBモードに似ている
OFBモードでは、平文ブロックとは無関係に暗号アルゴリズムを前もってぐるぐる回し、XORするためのビット列をしておく

### CTRモード
1ずつ増加していくカウンタを暗号化して鍵ストレーむを作り出すストリーム暗号
“カウンタを暗号化した値”と”平文ブロック”のXOR=暗号文ブロックとなる
カウンタの初期値は暗号化のたびに異なる値(ノンス)を元にして作る

RSA暗号

function rsa_key_generate (
  $p,
  $q
){
  if ($p == $q) {
    return false;
  }

  $n = (string)gmp_mul($p, $q); //mul-> 乗算
  $n_ gmp_mul(gmp_sub($p, '1'), gmp_sub($q, '1')); // 減算(p-1)(q-1)
  $rand = gmp_random_range(0, gmp_sub($n_, '1')); // random_range -> ランダムな数 0 ~ (n')

  while(true) {
    $coprime_numbers = (string)gmp_gcd($rand, $_n); // gcd -> 最大公約数を返す

    // 最大公約数が1なら互いに素な数
    if($coprime_numbers === '1') {
      $e = (string)$rand;
      break;
    }
    // 非互に素な数減算し再計算
    $rand = gmp_sub($rand, '1');
  }

  $d = (string)gmp_invert($e, $n_); // n'を法としたeの逆数

  // 秘密鍵:d 公開鍵:n, e
  return [$d, $n, $e];
}

function rsa_encryption (
  $a, //暗号化対象の平文
  $n, //公開鍵1
  $e //公開鍵2
){
  $b = [];
  foreach ($a as $value) {
    // aの一つを2乗
    $a_e = gmp_pow($value, $e);
    // e乗した値をnで割った余りを格納
    $b[] = (string)gmp_div_r($a_e, $n);
  }
  return $b;
}

function composite (
  $b, // 暗号文
  $d, // 秘密鍵
  $n // 公開鍵
){
    $a = [];
    foreach ($b as $value) {
      // bの一つをd乗する
      $b_d = gmp_pow($value, $d); // 
      // d乗した値をnで割った余りを格納
      $a[] = (string)gmp_div_r($b_d, $n);
    }
    return $a;
  }
  return $b;
}

// 平文を数値配列に変換
function convert_string_to_integer(
  $str
){
  // 初期配列
  $ord_array = [];

  for ($i = 0; $i < mb_strlen($str); $i ++) {
    $value = mb_substr($str, $i, 1);
    $ord_array[] = mb_ord($value);
  }
  return $ord_array;
}

// 数値配列を文字列に変換
function convert_integer_to_string (
  $int_array
) {
  // 初期文字列
  $chr = '';
  // 配列の数値を全て文字列に変換
  foreach ($int_array as $value){
    // 数値を文字列に
    $chr .= mb_char($value);
  }
  return $chr;
}

OpenSSLと楕円曲線暗号

$ openssl ecparam -list_curves

ECDSAで署名の生成・検証をする
$ echo abcde > plain.txt
$ openssl ecparam -genkey -name secp112r1 -out key-pair.pem
$ openssl ec -in key-pair.pem -outform PEM -pubout -out public.pem
$ openssl ec -in key-pair.pem -outform PEM -out private.pem
$ openssl dgst -sha1 -sign private.pem plain.txt > signature.dat
$ openssl dgst -sha1 -verify public.pem -signature signature.dat plain.txt
$ openssl asn1parse -inform DER -in signature.dat
0:d=0 hl=2 l= 34 cons: SEQUENCE
2:d=1 hl=2 l= 15 prim: INTEGER :D8BBE81DBD2E3A576952570B88D6
19:d=1 hl=2 l= 15 prim: INTEGER :B655B2C4FB75815E84B67BC4356E

署名

署名は署名者アリスと受け取ったデータの正当性を確認したいボブの二人の間で使用する

1. 鍵生成: 署名鍵sと検証鍵Sのペアを作成する
2. 署名: アリスは署名したいデータmに足して署名鍵sで検証に使うためのデータΩを作成する これを署名という
3. 検証: データと署名の組み合わせをもらったボブはアリスから受け取った検証鍵Sを用いて検証する

ハッシュ関数 SHA-2とSHA-3

ハッシュはあらゆるデータに対して、相違なる識別子を割り振る仕組み
認証や署名などに必須の要素技術

指紋認証などは、その特徴量は容易に複製されないことを前提としている

ハッシュ関数とは、任意のデータから、予め決められた範囲内の値を計算する関数
同じデータを入力すると常に同じ値が得られる
L 出力サイズが一定: SHA256は出力ハッシュ値はいつも256ビット。サイズの大きなデータはデータが欠損している
L 一方向性、衝突困難性: データが一致しているかを調べるにはハッシュ値の値が同一かを確認すれば良い

MD5, SHA-1は衝突する。SHA256, SHA3は今の所安全とされている
予めよく使われる単語を用意したり、パスワードとしてよく使われるパスワードはすぐにバレてしまう。携帯電話も同様。文字数が長くなると計算時間がかかる 8文字の場合は高性能GPUだと8時間で解明されてしまう

### SHA-2(256)とSHA-3
SHA-256が広く使われている
入力データを512ビットごとのブロックに分割する
あまりにデータの収量を示す1ビットの1を追加
0をあまりのサイズが448ビットになるように追加する
最後に64ビット整数の形式で追加して全体が512ビットの倍数になるように調整

paddingと書く

8個の32ビット整数からなる256ビットの内部状態Sを準備する
内部状態SとブロックBを入力して新しい内部状態Sを出力する圧縮関数fを準備する
内部状態Sを初期化IVで初期化し、各ブロックを入力して順次内部状態を更新する
SHA-256の方が高速に処理される

### SHA-3
マークルダンガード構造ではなくスポンジ構造を採用している。吸収フェーズと搾取フェーズがある

RSA暗号

RSA暗号は冪乗の余りが持つ性質を利用した暗号

二つの素数pとqを選び、その二つをかけてn = pxqとする
整数2を選ぶ e.g. e=65537
e x d = (p-1)x(q-1)で割ったあまりとなるような整数dを探す
dが秘密鍵で nとeのペア(n, e)が公開鍵

nの素因数分解ができような大きなnを選ぶ必要がある
落としどつき一方向関数という
RSA暗号の基本方式は安全ではないとされ、RSA-OAEPが推奨されている

### OpenSSLによるRSA暗号の鍵の作り方
$ openssl version
OpenSSL 1.1.1f 31 Mar 2020

秘密鍵ファイルの作成
$ openssl genrsa 2048 > sec-test-key.txt
Generating RSA private key, 2048 bit long modulus (2 primes)
…………+++++
………………………………………..+++++
e is 65537 (0x010001)

公開鍵の作成
$ openssl rsa -pubout < sec-test-key.txt > pub-test-key.txt
writing RSA key

確認方法
$ openssl rsa -text -pubin -noout < pub-test-key.txt RSA Public-Key: (2048 bit) Modulus: 00:ac:d4:b1:05:d5:86:6d:83:4a:34:65:62:b9:b9: d3:08:eb:23:ec:8f:11:4b:5d:f1:90:59:cd:b0:0f: 83:15:a5:90:09:f2:d6:13:9f:89:b4:98:cb:7c:1d: // 省略 df:5b:ce:c4:03:55:14:b8:d2:30:7d:f3:99:03:96: df:eb:cb:69:79:e3:b8:12:e4:80:b3:b3:7a:71:e9: 09:b9 Exponent: 65537 (0x10001) $ openssl rsa -text -noout < sec-test-key.txt RSA Private-Key: (2048 bit, 2 primes) ※privateExponentが秘密鍵を示す Pythonによる動作確認 def convert_to_int(s): return int("".join(s.split()).replace(":",""),16) n=convert_to_int(""" 00:ac:d4:b1:05:d5:86:6d:83:4a:34:65:62:b9:b9: d3:08:eb:23:ec:8f:11:4b:5d:f1:90:59:cd:b0:0f: 83:15:a5:90:09:f2:d6:13:9f:89:b4:98:cb:7c:1d: 81:13:d6:f8:f2:b7:b0:ac:03:3d:d5:00:f6:10:0f:         // 省略 14:0a:71:96:e1:0d:48:ee:8b:b1:74:ed:3b:c2:15: 3e:47:d9:1b:8c:5a:d3:a3:89:7a:20:37:6d:42:23: 3b:d2:3f:7a:5b:e4:75:c2:b7:89:d7:6b:48:8e:bd: df:5b:ce:c4:03:55:14:b8:d2:30:7d:f3:99:03:96: df:eb:cb:69:79:e3:b8:12:e4:80:b3:b3:7a:71:e9: 09:b9 """) d=convert_to_int(""" 00:8d:04:d6:94:2e:0f:8b:97:ce:9a:46:07:72:07: 7f:7c:0d:70:b8:7a:5b:e0:24:fd:0f:8d:56:d9:4b: 2e:e8:20:b9:10:85:05:cb:e9:26:d1:26:c3:11:b8: 79:33:98:fa:74:01:11:b4:a8:c8:70:de:61:e7:e4: // 省略 b1:a2:ae:51:87:38:48:4b:70:ce:90:9b:3c:10:d0: 37:8b:84:73:56:c7:25:e2:f7:7e:ca:9a:eb:09:47: 42:52:6a:3b:d4:c3:04:87:27:8e:54:93:af:4b:04: 4e:02:23:1b:24:0a:49:63:ce:b8:5d:fd:7b:5d:c6: 13:26:3a:00:75:9c:51:bd:1d:cb:f2:40:c3:a2:6c: ad:01 """) e=65537 m=123456789 c=pow(m,e,n) c x=pow(c,d,n) x

有限体と拡大体

有限体と拡大体はAESや楕円曲線暗号など様々な暗号技術を実装しようとしたときに必要となる知識
加減乗除の四則演算ができる集合を体という
実数の集合や分数の集合は体なので実数体や有理数体という  有限個の集合からなる体は有限体という

### 拡大体
共通鍵暗号AES、ディスク暗号化XTS-AES、認証付き暗号で紹介するAES-GCMなどではF2の拡大体の演算が使われる

素数p未満の集合{0, 1, …, p-1}に四則演算の規則を入れたものを有限体Fpという
有限体F2をk個まとめて新たな四則演算を入れたものをF2の拡大体F2kという
有限体や拡大体は様々な暗号技術で利用されている

鍵共有

鍵共有とは秘匿されていない通信経路を用いて安全に秘密情報を共有するための仕組み
L 1976年に鍵共有方法、公開鍵暗号、署名の概念を発表

### 冪乗
(g^a)^b = g^ab = g^ba = (g^b)^a

xをnで割った余りがrの時、r = x mod nと書く 。プログラミング言語では x % n と表記する

冪乗の性質を使ってアリスとボブは鍵共有をする(DH鍵交換という)
1. アリスとボブでgとnを決めて固定する
2. アリスは秘密の値aを決めて A = g^a mod n を求めてボブに渡す
3. ボブも秘密の値bを決めて B = g^b mod n を求めてアリスに渡す
4. アリスはボブからもらったBから s = B^a mod n を求める
5. ボブもアリスからもらったAから s’ = A^b mod n を求める
冪乗の性質から s = s’ = g^ab(mod n)となる これがアリスとボブで共有した値で、共通鍵暗号の秘密鍵など二人だけしか知らない秘密の情報として利用する

DH鍵共有の安全性… DHP(DH Problem)はスーパーコンピュータでも解けないだろうとされている
ある向きの計算は簡単だが、その逆向きの計算が難しい関数を一方向性関数という
DH鍵共有はDHPやDLPといった数学的な問題の困難さを安全性の根拠にしている

ディスクの暗号化

TMP(Trusted Platform Module)はパソコンやスマートフォンに組み込まれているセキュリティ専用チップ
TMPはTCGが定めていて、TMP2.0は標準規格になっている
XTS-AESはブロック暗号AESを使った暗号方法の一つで、WindowsのBitLocker, macOSのFileVault2, Linuzのdm-cryptなどディスク暗号化ソフトウェアで利用される

安全に暗号通信するには平文の情報を漏らさないだけでなく暗号文の改竄攻撃も想定しなければならない