PageRenderTime 27ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/Sharpotify.Lib/Crypto/Shannon.cs

#
C# | 636 lines | 340 code | 138 blank | 158 comment | 61 complexity | 3314f22512b95492cfc078cd1e7b1865 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. namespace Sharpotify.Crypto
  6. {
  7. public class Shannon
  8. {
  9. /*
  10. * Fold is how many register cycles need to be performed after combining the
  11. * last byte of key and non-linear feedback, before every byte depends on every
  12. * byte of the key. This depends on the feedback and nonlinear functions, and
  13. * on where they are combined into the register. Making it same as the register
  14. * length is a safe and conservative choice.
  15. */
  16. private static readonly int N = 16;
  17. private static readonly int FOLD = N; /* How many iterations of folding to do. */
  18. private static readonly int INITKONST = 0x6996c53a; /* Value of konst to use during key loading. */
  19. private static readonly int KEYP = 13; /* Where to insert key/MAC/counter words. */
  20. private int[] R; /* Working storage for the shift register. */
  21. private int[] CRC; /* Working storage for CRC accumulation. */
  22. private int[] initR; /* Saved register contents. */
  23. private int konst; /* Key dependant semi-constant. */
  24. private int sbuf; /* Encryption buffer. */
  25. private int mbuf; /* Partial word MAC buffer. */
  26. private int nbuf; /* Number of part-word stream bits buffered. */
  27. private int IntRotateLeft(int x, int n)
  28. {
  29. UInt32 ux = (UInt32)(x & 0xFFFFFFFF);
  30. return (int)((ux << n) | (ux >> (32 - n)));
  31. }
  32. /**
  33. * Create a new instance of the Shannon stream-cipher.
  34. */
  35. public Shannon(){
  36. /* Registers with length N. */
  37. this.R = new int[N];
  38. this.CRC = new int[N];
  39. this.initR = new int[N];
  40. }
  41. /* Nonlinear transform (sbox) of a word. There are two slightly different combinations. */
  42. private int sbox(int i){
  43. i ^= IntRotateLeft(i, 5) | IntRotateLeft(i, 7);
  44. i ^= IntRotateLeft(i, 19) | IntRotateLeft(i, 22);
  45. return i;
  46. }
  47. private int sbox2(int i){
  48. i ^= IntRotateLeft(i, 7) | IntRotateLeft(i, 22);
  49. i ^= IntRotateLeft(i, 5) | IntRotateLeft(i, 19);
  50. return i;
  51. }
  52. /* Cycle the contents of the register and calculate output word in sbuf. */
  53. private void cycle(){
  54. /* Temporary variable. */
  55. int t;
  56. /* Nonlinear feedback function. */
  57. t = this.R[12] ^ this.R[13] ^ this.konst;
  58. t = this.sbox(t) ^ IntRotateLeft(this.R[0], 1);
  59. /* Shift register. */
  60. for(int i = 1; i < N; i++){
  61. this.R[i - 1] = this.R[i];
  62. }
  63. this.R[N - 1] = t;
  64. t = sbox2(this.R[2] ^ this.R[15]);
  65. this.R[0] ^= t;
  66. this.sbuf = t ^ this.R[8] ^ this.R[12];
  67. }
  68. /*
  69. * The Shannon MAC function is modelled after the concepts of Phelix and SHA.
  70. * Basically, words to be accumulated in the MAC are incorporated in two
  71. * different ways:
  72. * 1. They are incorporated into the stream cipher register at a place
  73. * where they will immediately have a nonlinear effect on the state.
  74. * 2. They are incorporated into bit-parallel CRC-16 registers; the
  75. * contents of these registers will be used in MAC finalization.
  76. */
  77. /*
  78. * Accumulate a CRC of input words, later to be fed into MAC.
  79. * This is actually 32 parallel CRC-16s, using the IBM CRC-16
  80. * polynomian x^16 + x^15 + x^2 + 1
  81. */
  82. private void crcFunc(int i){
  83. /* Temporary variable. */
  84. int t;
  85. /* Accumulate CRC of input. */
  86. t = this.CRC[0] ^ this.CRC[2] ^ this.CRC[15] ^ i;
  87. for(int j = 1; j < N; j++){
  88. this.CRC[j - 1] = this.CRC[j];
  89. }
  90. this.CRC[N - 1] = t;
  91. }
  92. /* Normal MAC word processing: do both stream register and CRC. */
  93. private void macFunc(int i){
  94. this.crcFunc(i);
  95. this.R[KEYP] ^= i;
  96. }
  97. /* Initialize to known state. */
  98. private void initState(){
  99. /* Register initialized to Fibonacci numbers. */
  100. ////Fast loading
  101. //if (N == 16)
  102. //{
  103. // this.R = new int[16] { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987 };
  104. //}
  105. //else
  106. //{
  107. this.R[0] = 1;
  108. this.R[1] = 1;
  109. for (int i = 2; i < N; i++)
  110. {
  111. this.R[i] = this.R[i - 1] + this.R[i - 2];
  112. }
  113. //}
  114. /* Initialization constant. */
  115. this.konst = INITKONST;
  116. }
  117. /* Save the current register state. */
  118. private void saveState(){
  119. for(int i = 0; i < N; i++){
  120. this.initR[i] = this.R[i];
  121. }
  122. }
  123. /* Inisialize to previously saved register state. */
  124. private void reloadState(){
  125. for(int i = 0; i < N; i++){
  126. this.R[i] = this.initR[i];
  127. }
  128. }
  129. /* Initialize 'konst'. */
  130. private void genKonst(){
  131. this.konst = this.R[0];
  132. }
  133. /* Load key material into the register. */
  134. private void addKey(int k){
  135. this.R[KEYP] ^= k;
  136. }
  137. /* Extra nonlinear diffusion of register for key and MAC. */
  138. private void diffuse(){
  139. for(int i = 0; i < FOLD; i++){
  140. this.cycle();
  141. }
  142. }
  143. /*
  144. * Common actions for loading key material.
  145. * Allow non-word-multiple key and nonce material.
  146. * Note: Also initializes the CRC register as a side effect.
  147. */
  148. private void loadKey(byte[] key){
  149. byte[] extra = new byte[4];
  150. int i, j;
  151. int t;
  152. /* Start folding key. */
  153. for(i = 0; i < (key.Length & ~0x03); i += 4){
  154. /* Shift 4 bytes into one word. */
  155. t = ((key[i + 3] & 0xFF) << 24) |
  156. ((key[i + 2] & 0xFF) << 16) |
  157. ((key[i + 1] & 0xFF) << 8) |
  158. ((key[i ] & 0xFF));
  159. /* Insert key word at index 13. */
  160. this.addKey(t);
  161. /* Cycle register. */
  162. this.cycle();
  163. }
  164. /* If there were any extra bytes, zero pad to a word. */
  165. if(i < key.Length){
  166. /* i remains unchanged at start of loop. */
  167. for(j = 0; i < key.Length; i++){
  168. extra[j++] = key[i];
  169. }
  170. /* j remains unchanged at start of loop. */
  171. for(; j < 4; j++){
  172. extra[j] = 0;
  173. }
  174. /* Shift 4 extra bytes into one word. */
  175. t = ((extra[3] & 0xFF) << 24) |
  176. ((extra[2] & 0xFF) << 16) |
  177. ((extra[1] & 0xFF) << 8) |
  178. ((extra[0] & 0xFF));
  179. /* Insert key word at index 13. */
  180. this.addKey(t);
  181. /* Cycle register. */
  182. this.cycle();
  183. }
  184. /* Also fold in the length of the key. */
  185. this.addKey(key.Length);
  186. /* Cycle register. */
  187. this.cycle();
  188. /* Save a copy of the register. */
  189. for(i = 0; i < N; i++){
  190. this.CRC[i] = this.R[i];
  191. }
  192. /* Now diffuse. */
  193. this.diffuse();
  194. /* Now XOR the copy back -- makes key loading irreversible. */
  195. for(i = 0; i < N; i++){
  196. this.R[i] ^= this.CRC[i];
  197. }
  198. }
  199. /* Set key */
  200. public void key(byte[] key)
  201. {
  202. /* Initializet known state. */
  203. this.initState();
  204. /* Load key material. */
  205. this.loadKey(key);
  206. /* In case we proceed to stream generation. */
  207. this.genKonst();
  208. /* Save register state. */
  209. this.saveState();
  210. /* Set 'nbuf' value to zero. */
  211. this.nbuf = 0;
  212. }
  213. /* Set IV */
  214. public void nonce(byte[] nonce){
  215. /* Reload register state. */
  216. this.reloadState();
  217. /* Set initialization constant. */
  218. this.konst = INITKONST;
  219. /* Load "IV" material. */
  220. this.loadKey(nonce);
  221. /* Set 'konst'. */
  222. this.genKonst();
  223. /* Set 'nbuf' value to zero. */
  224. this.nbuf = 0;
  225. }
  226. /*
  227. * XOR pseudo-random bytes into buffer.
  228. * Note: doesn't play well with MAC functions.
  229. */
  230. public void stream(byte[] buffer){
  231. int i = 0, j, n = buffer.Length;
  232. /* Handle any previously buffered bytes. */
  233. while(this.nbuf != 0 && n != 0){
  234. buffer[i++] ^= (byte)(this.sbuf & 0xFF);
  235. this.sbuf >>= 8;
  236. this.nbuf -= 8;
  237. n--;
  238. }
  239. /* Handle whole words. */
  240. j = n & ~0x03;
  241. while(i < j){
  242. /* Cycle register. */
  243. this.cycle();
  244. /* XOR word. */
  245. buffer[i + 3] ^= (byte)((this.sbuf >> 24) & 0xFF);
  246. buffer[i + 2] ^= (byte)((this.sbuf >> 16) & 0xFF);
  247. buffer[i + 1] ^= (byte)((this.sbuf >> 8) & 0xFF);
  248. buffer[i ] ^= (byte)((this.sbuf ) & 0xFF);
  249. i += 4;
  250. }
  251. /* Handle any trailing bytes. */
  252. n &= 0x03;
  253. if(n != 0){
  254. /* Cycle register. */
  255. this.cycle();
  256. this.nbuf = 32;
  257. while(this.nbuf != 0 && n != 0){
  258. buffer[i++] ^= (byte)(this.sbuf & 0xFF);
  259. this.sbuf >>= 8;
  260. this.nbuf -= 8;
  261. n--;
  262. }
  263. }
  264. }
  265. /*
  266. * Accumulate words into MAC without encryption.
  267. * Note that plaintext is accumulated for MAC.
  268. */
  269. public void macOnly(byte[] buffer){
  270. int i = 0, j, n = buffer.Length;
  271. int t;
  272. /* Handle any previously buffered bytes. */
  273. if(this.nbuf != 0){
  274. while(this.nbuf != 0 && n != 0){
  275. this.mbuf ^= buffer[i++] << (32 - this.nbuf);
  276. this.nbuf -= 8;
  277. n--;
  278. }
  279. /* Not a whole word yet. */
  280. if(this.nbuf != 0){
  281. return;
  282. }
  283. /* LFSR already cycled. */
  284. this.macFunc(this.mbuf);
  285. }
  286. /* Handle whole words. */
  287. j = n & ~0x03;
  288. while(i < j){
  289. /* Cycle register. */
  290. this.cycle();
  291. /* Shift 4 bytes into one word. */
  292. t = ((buffer[i + 3] & 0xFF) << 24) |
  293. ((buffer[i + 2] & 0xFF) << 16) |
  294. ((buffer[i + 1] & 0xFF) << 8) |
  295. ((buffer[i ] & 0xFF));
  296. this.macFunc(t);
  297. i += 4;
  298. }
  299. /* Handle any trailing bytes. */
  300. n &= 0x03;
  301. if(n != 0){
  302. /* Cycle register. */
  303. this.cycle();
  304. this.mbuf = 0;
  305. this.nbuf = 32;
  306. while(this.nbuf != 0 && n != 0){
  307. this.mbuf ^= buffer[i++] << (32 - this.nbuf);
  308. this.nbuf -= 8;
  309. n--;
  310. }
  311. }
  312. }
  313. /*
  314. * Combined MAC and encryption.
  315. * Note that plaintext is accumulated for MAC.
  316. */
  317. public void encrypt(byte[] buffer){
  318. this.encrypt(buffer, buffer.Length);
  319. }
  320. /*
  321. * Combined MAC and encryption.
  322. * Note that plaintext is accumulated for MAC.
  323. */
  324. public void encrypt(byte[] buffer, int n){
  325. int i = 0, j;
  326. int t;
  327. /* Handle any previously buffered bytes. */
  328. if(this.nbuf != 0){
  329. while(this.nbuf != 0 && n != 0){
  330. this.mbuf ^= (buffer[i] & 0xFF) << (32 - this.nbuf);
  331. buffer[i] ^= (byte)((this.sbuf >> (32 - this.nbuf)) & 0xFF);
  332. i++;
  333. this.nbuf -= 8;
  334. n--;
  335. }
  336. /* Not a whole word yet. */
  337. if(this.nbuf != 0){
  338. return;
  339. }
  340. /* LFSR already cycled. */
  341. this.macFunc(this.mbuf);
  342. }
  343. /* Handle whole words. */
  344. j = n & ~0x03;
  345. while(i < j){
  346. /* Cycle register. */
  347. this.cycle();
  348. /* Shift 4 bytes into one word. */
  349. t = ((buffer[i + 3] & 0xFF) << 24) |
  350. ((buffer[i + 2] & 0xFF) << 16) |
  351. ((buffer[i + 1] & 0xFF) << 8) |
  352. ((buffer[i ] & 0xFF));
  353. this.macFunc(t);
  354. t ^= this.sbuf;
  355. /* Put word into byte buffer. */
  356. buffer[i + 3] = (byte)((t >> 24) & 0xFF);
  357. buffer[i + 2] = (byte)((t >> 16) & 0xFF);
  358. buffer[i + 1] = (byte)((t >> 8) & 0xFF);
  359. buffer[i ] = (byte)((t ) & 0xFF);
  360. i += 4;
  361. }
  362. /* Handle any trailing bytes. */
  363. n &= 0x03;
  364. if(n != 0){
  365. /* Cycle register. */
  366. this.cycle();
  367. this.mbuf = 0;
  368. this.nbuf = 32;
  369. while(this.nbuf != 0 && n != 0){
  370. this.mbuf ^= (buffer[i] & 0xFF) << (32 - this.nbuf);
  371. buffer[i] ^= (byte)((this.sbuf >> (32 - this.nbuf)) & 0xFF);
  372. i++;
  373. this.nbuf -= 8;
  374. n--;
  375. }
  376. }
  377. }
  378. /*
  379. * Combined MAC and decryption.
  380. * Note that plaintext is accumulated for MAC.
  381. */
  382. public void decrypt(byte[] buffer){
  383. this.decrypt(buffer, buffer.Length);
  384. }
  385. /*
  386. * Combined MAC and decryption.
  387. * Note that plaintext is accumulated for MAC.
  388. */
  389. public void decrypt(byte[] buffer, int n){
  390. int i = 0, j;
  391. int t;
  392. /* Handle any previously buffered bytes. */
  393. if(this.nbuf != 0){
  394. while(this.nbuf != 0 && n != 0){
  395. buffer[i] ^= (byte)((this.sbuf >> (32 - this.nbuf)) & 0xFF);
  396. this.mbuf ^= (buffer[i] & 0xFF) << (32 - this.nbuf);
  397. i++;
  398. this.nbuf -= 8;
  399. n--;
  400. }
  401. /* Not a whole word yet. */
  402. if(this.nbuf != 0){
  403. return;
  404. }
  405. /* LFSR already cycled. */
  406. this.macFunc(this.mbuf);
  407. }
  408. /* Handle whole words. */
  409. j = n & ~0x03;
  410. while(i < j){
  411. /* Cycle register. */
  412. this.cycle();
  413. /* Shift 4 bytes into one word. */
  414. t = ((buffer[i + 3] & 0xFF) << 24) |
  415. ((buffer[i + 2] & 0xFF) << 16) |
  416. ((buffer[i + 1] & 0xFF) << 8) |
  417. ((buffer[i ] & 0xFF));
  418. t ^= this.sbuf;
  419. this.macFunc(t);
  420. /* Put word into byte buffer. */
  421. buffer[i + 3] = (byte)((t >> 24) & 0xFF);
  422. buffer[i + 2] = (byte)((t >> 16) & 0xFF);
  423. buffer[i + 1] = (byte)((t >> 8) & 0xFF);
  424. buffer[i ] = (byte)((t ) & 0xFF);
  425. i += 4;
  426. }
  427. /* Handle any trailing bytes. */
  428. n &= 0x03;
  429. if(n != 0){
  430. /* Cycle register. */
  431. this.cycle();
  432. this.mbuf = 0;
  433. this.nbuf = 32;
  434. while(this.nbuf != 0 && n != 0){
  435. buffer[i] ^= (byte)((this.sbuf >> (32 - this.nbuf)) & 0xFF);
  436. this.mbuf ^= (buffer[i] & 0xFF) << (32 - this.nbuf);
  437. i++;
  438. this.nbuf -= 8;
  439. n--;
  440. }
  441. }
  442. }
  443. /*
  444. * Having accumulated a MAC, finish processing and return it.
  445. * Note that any unprocessed bytes are treated as if they were
  446. * encrypted zero bytes, so plaintext (zero) is accumulated.
  447. */
  448. public void finish(byte[] buffer){
  449. this.finish(buffer, buffer.Length);
  450. }
  451. /*
  452. * Having accumulated a MAC, finish processing and return it.
  453. * Note that any unprocessed bytes are treated as if they were
  454. * encrypted zero bytes, so plaintext (zero) is accumulated.
  455. */
  456. public void finish(byte[] buffer, int n){
  457. int i = 0, j;
  458. /* Handle any previously buffered bytes. */
  459. if(this.nbuf != 0){
  460. /* LFSR already cycled. */
  461. this.macFunc(this.mbuf);
  462. }
  463. /*
  464. * Perturb the MAC to mark end of input.
  465. * Note that only the stream register is updated, not the CRC.
  466. * This is an action that can't be duplicated by passing in plaintext,
  467. * hence defeating any kind of extension attack.
  468. */
  469. this.cycle();
  470. this.addKey(INITKONST ^ (this.nbuf << 3));
  471. this.nbuf = 0;
  472. /* Now add the CRC to the stream register and diffuse it. */
  473. for(j = 0; j < N; j++){
  474. this.R[j] ^= this.CRC[j];
  475. }
  476. this.diffuse();
  477. /* Produce output from the stream buffer. */
  478. while(n > 0){
  479. this.cycle();
  480. if(n >= 4){
  481. /* Put word into byte buffer. */
  482. buffer[i + 3] = (byte)((this.sbuf >> 24) & 0xFF);
  483. buffer[i + 2] = (byte)((this.sbuf >> 16) & 0xFF);
  484. buffer[i + 1] = (byte)((this.sbuf >> 8) & 0xFF);
  485. buffer[i ] = (byte)((this.sbuf ) & 0xFF);
  486. n -= 4;
  487. i += 4;
  488. }
  489. else{
  490. for(j = 0; j < n; j++){
  491. buffer[i + j] = (byte)((this.sbuf >> (i * 8)) & 0xFF);
  492. }
  493. break;
  494. }
  495. }
  496. }
  497. }
  498. }