安全でないストリーム暗号ARCFOUR
はじめに
Wikipediaを見てみたらARCFOURのアルゴリズムが単純だったので実装してみた. 一応言っておくが, 暗号アルゴリズムの実装は素人がやるものではない.
参考記事: https://en.wikipedia.org/wiki/RC4
記事に書いてある通りに実装した.
ソースコード
/* This program is in the public domain */ #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <string.h> #include <stdint.h> #define MAX_KEY_LEN 256 #define S_LEN 256 uint8_t key[MAX_KEY_LEN + 1]; uint8_t S[S_LEN]; void swap(uint8_t *a, uint8_t *b) { uint8_t t = *a; *a = *b; *b = t; } char *readline(char *s, size_t size) { if (fgets(s, size, stdin) == NULL) return NULL; if (strchr(s, '\n') != NULL) s[strlen(s) - 1] = '\0'; else while (getchar() != '\n') ; return s; } /* key-scheduling algorithm */ void KSA(size_t key_len) { size_t i, j; for (i = 0; i < S_LEN; i++) S[i] = i; j = 0; for (i = 0; i < S_LEN; i++) { j = (j + S[i] + key[i % key_len]) % S_LEN; swap(&S[i], &S[j]); } } /* pseudo-random generation algorithm (PRGA) */ uint8_t PRGA(void) { /* static variables! */ static size_t i, j; i = (i + 1) % S_LEN; j = (j + S[i]) % S_LEN; swap(&S[i], &S[j]); return S[(S[i] + S[j]) % S_LEN]; } int main(int argc, char **argv) { FILE *fr, *fw; uint8_t buf; size_t key_len; if (argc != 3) { fprintf(stderr, "specify files\n"); return 1; } if (!readline((char *)key, MAX_KEY_LEN + 1)) { fprintf(stderr, "failed to read key"); return 1; } key_len = strlen((char *)key); KSA(key_len); if ((fr = fopen(argv[1], "rb")) == NULL) { fprintf(stderr, "failed to open file"); return 1; } if ((fw = fopen(argv[2], "wb")) == NULL) { fprintf(stderr, "failed to open file"); fclose(fr); return 1; } for (;;) { fread(&buf, sizeof(buf), 1, fr); if (feof(fr)) break; if (ferror(fr)) { fprintf(stderr, "failed to read\n"); break; } buf ^= PRGA(); if (fwrite(&buf, sizeof(buf), 1, fw) < 1) { fprintf(stderr, "failed to write\n"); break; } } fclose(fr); fclose(fw); return 0; }
動作テスト
テキストファイルtest.txt
を暗号化する.
暗号化すると文字化けするので16進数で表示する.
$ hexdump test.txt 0000000 83e3 e390 bc83 83e3 e381 a383 83e3 e3ab 0000010 a983 82e3 e3a4 9083 83e3 e3bc ae81 9ce6 0000020 e388 8e83 bee7 e58e 8e85 81e3 e3a7 9981 0000030 80e3 0a82 81e3 e3bf a881 83e3 e3a9 b882 0000040 81e3 e8ae b3a9 b4e7 e3b0 af81 81e3 e393 0000050 a181 82e3 ef89 81bc 86e2 6892 7474 3a70 0000060 2f2f 7774 6670 6a2e 2f70 694d 6f74 7354 0000070 6b75 6e69 0a6f 4d44 81e3 e5af 8b80 88e5 0000080 e3a5 a781 81e3 e3af aa81 81e3 408f 696e 0000090 696a 6173 6a6e 5f69 7061 e370 be81 81e3 00000a0 e3a7 8a81 a1e9 e398 8481 87e8 e3b4 9781 00000b0 81e3 e3be 9981 80e3 0a82 83e3 e395 a182 00000c0 83e3 e3b3 a282 83e3 e3bc 8883 81e3 20af 00000d0 e323 bf81 81e3 e3a8 8281 83e3 e3bc a881 00000e0 e320 a781 99e7 e8ba 8ba6 81e3 e397 be81 00000f0 81e3 ef99 81bc 000a 00000f7
これをキーVirtual YouTuber
で暗号化してtest.txt.enc
に保存.
$ ./arc4 test.txt test.txt.enc Virtual YouTuber $ hexdump test.txt.enc 0000000 ee83 d240 22d7 820f afa8 e854 2757 303f 0000010 bfed 3ebb f9a3 b055 29da b054 e975 17c2 0000020 1211 d87e 97b4 bae8 a525 3d65 7256 05e6 0000030 138a 697d c071 a7a5 e715 75be 49de 8e4c 0000040 bf07 bfff b0a4 cc27 8498 0dbd 77ea 67b1 0000050 670a 2daa a470 ab84 251f 8129 9885 41c2 0000060 afd8 57e4 2552 39ff 18db 327e 0745 399f 0000070 5c34 b6e2 c6d2 93ef c14b d111 da67 b225 0000080 fb26 fc24 d760 15fa 71e7 dce4 b55f 6d71 0000090 3355 874c e3ef 105b 423d ca88 4d9b 58ec 00000a0 f162 ef70 510a 8faa 6ef5 d474 0a03 1598 00000b0 4a35 7574 494e 2117 b50c 9898 7e58 1a4a 00000c0 93b2 4c73 4116 2b17 b2ab 4290 9d2b 107f 00000d0 1f7e d8be e6ef 0bfe 2012 f740 4595 8a8d 00000e0 ff29 84a3 a444 7c0e fbae 2515 f988 2ce9 00000f0 dcd7 7249 e9ec 002a 00000f7
test.txt.enc
を復号してtest.txt.enc.dec
に保存.
$ ./arc4 test.txt.enc test.txt.enc.dec Virtual YouTuber $ hexdump test.txt.enc.dec 0000000 83e3 e390 bc83 83e3 e381 a383 83e3 e3ab 0000010 a983 82e3 e3a4 9083 83e3 e3bc ae81 9ce6 0000020 e388 8e83 bee7 e58e 8e85 81e3 e3a7 9981 0000030 80e3 0a82 81e3 e3bf a881 83e3 e3a9 b882 0000040 81e3 e8ae b3a9 b4e7 e3b0 af81 81e3 e393 0000050 a181 82e3 ef89 81bc 86e2 6892 7474 3a70 0000060 2f2f 7774 6670 6a2e 2f70 694d 6f74 7354 0000070 6b75 6e69 0a6f 4d44 81e3 e5af 8b80 88e5 0000080 e3a5 a781 81e3 e3af aa81 81e3 408f 696e 0000090 696a 6173 6a6e 5f69 7061 e370 be81 81e3 00000a0 e3a7 8a81 a1e9 e398 8481 87e8 e3b4 9781 00000b0 81e3 e3be 9981 80e3 0a82 83e3 e395 a182 00000c0 83e3 e3b3 a282 83e3 e3bc 8883 81e3 20af 00000d0 e323 bf81 81e3 e3a8 8281 83e3 e3bc a881 00000e0 e320 a781 99e7 e8ba 8ba6 81e3 e397 be81 00000f0 81e3 ef99 81bc 000a 00000f7
もとに戻った.
$ sha256sum test.txt test.txt.enc.dec 0e288309792d64d4b14af3db9e847de9e0c32526f2f42b9254580f86648fc3e6 test.txt 0e288309792d64d4b14af3db9e847de9e0c32526f2f42b9254580f86648fc3e6 test.txt.enc.dec
おまけ
Rustの練習で書いてみた. まったく感覚がわからない.
use std::env; use std::fs::File; use std::io::{self, BufReader, BufWriter, Read, Write}; use std::process; const MAX_KEY_LEN: usize = 256; const S_LEN: usize = 256; struct Arc4 { s: [u8; S_LEN], i: usize, j: usize, } // key-scheduling algorithm fn ksa(key: &[u8]) -> Arc4 { let mut a = Arc4 { s: [0; S_LEN], i: 0, j: 0, }; for i in 0..S_LEN { a.s[i] = i as u8; } let mut j: usize = 0; let key_len = key.len(); for i in 0..S_LEN { j = (j + usize::from(a.s[i]) + usize::from(key[i % key_len])) % S_LEN; a.s.swap(i, j); } a } // pseudo-random generation algorithm (PRGA) fn prga(a: &mut Arc4) -> u8 { a.i = (a.i + 1) % S_LEN; a.j = (a.j + usize::from(a.s[a.i])) % S_LEN; a.s.swap(a.i, a.j); a.s[(usize::from(a.s[a.i]) + usize::from(a.s[a.j])) % S_LEN] } fn main() { if env::args().len() != 3 { println!("specify files"); process::exit(1); } let mut key_input = String::new(); io::stdin().read_line(&mut key_input).expect("failed to read input from stdin"); let key_input_trimmed = key_input.trim(); match key_input_trimmed.len() { 1...MAX_KEY_LEN => {}, _ => { println!("the key length must be 1-{} bytes (8 - {} bits)", MAX_KEY_LEN, MAX_KEY_LEN * 8); process::exit(1); } } let mut a = ksa(key_input_trimmed.as_bytes()); let args: Vec<String> = env::args().skip(1).collect(); let fr = File::open(&args[0]).expect("failed to open a file"); let mut reader = BufReader::new(fr); let fw = File::create(&args[1]).expect("failed to create a file"); let mut writer = BufWriter::new(fw); let mut buf = vec![]; reader.read_to_end(&mut buf).expect("failed to read a file"); for b in buf.iter_mut() { *b ^= prga(&mut a); } writer.write_all(&buf).expect("failed to write to a file"); }