public static byte[] ApplyPadding(PaddingMode mode, byte[] bytes, int blockSizeInBytes) { int paddingBytesNeeded = GetPaddingBytesNeeded(mode, bytes.Length, blockSizeInBytes); if (paddingBytesNeeded == 0) { // sanity check return(ByteUtilities.Clone(bytes)); } byte[] output = new byte[blockSizeInBytes]; Buffer.BlockCopy(bytes, 0, output, 0, bytes.Length); switch (mode) { case PaddingMode.ANSIX923: ApplyAnsiX923Padding(output, paddingBytesNeeded); break; case PaddingMode.ISO10126: ApplyIso10126Padding(output, paddingBytesNeeded); break; case PaddingMode.PKCS7: ApplyPkcs7Padding(output, paddingBytesNeeded); break; case PaddingMode.Zeros: // nop break; default: throw new NotImplementedException("Padding mode not implemented"); } return(output); }
private static void ShowRijndaelBookTestVectors() { WriteHeader("Rijndael Book Test"); Console.WriteLine("Here are the test vectors from Appendix D of \"The Design of Rijndael\" book."); Console.WriteLine("(Note how there are more variants than AES allows)"); Console.WriteLine(); for (int keyLength = 128; keyLength <= 256; keyLength += 32) { for (int blockLength = 128; blockLength <= 256; blockLength += 32) { Console.WriteLine("block length {0} key length {1}", blockLength, keyLength); byte[] blockBytes = new byte[blockLength / Constants.BitsPerByte]; byte[] keyBytes = new byte[keyLength / Constants.BitsPerByte]; var encrypted = Rijndael.Encrypt(blockBytes, keyBytes); ByteUtilities.WriteBytes(encrypted); var decrypted = Rijndael.Decrypt(encrypted, keyBytes); for (int i = 0; i < decrypted.Length; i++) { if (decrypted[i] != 0) { throw new CryptographicException("The decrypted Rijndael book values were not all zero."); } } var encryptedAgain = Rijndael.Encrypt(encrypted, keyBytes); ByteUtilities.WriteBytes(encryptedAgain); var decryptedAgain = Rijndael.Decrypt(encryptedAgain, keyBytes); ByteUtilities.AssertBytesEqual(decryptedAgain, encrypted); Console.WriteLine(); } } Console.WriteLine(); }
private static void PerformRandomizedTest(CipherMode mode, PaddingMode padding, int keySize) { var aesCapi = new AesCryptoServiceProvider(); aesCapi.Mode = mode; aesCapi.Padding = padding; aesCapi.Key = ByteUtilities.GetCryptographicallyRandomBytes(keySize / Constants.BitsPerByte); aesCapi.IV = ByteUtilities.GetCryptographicallyRandomBytes(128 / Constants.BitsPerByte); var capiEncryptionTransform = aesCapi.CreateEncryptor(); var ours = new Rijndael(aesCapi.Key); var ourEncryptionTransform = ours.CreateEncryptor(aesCapi.Mode, aesCapi.IV, aesCapi.FeedbackSize, aesCapi.Padding); byte[] encryptedResult = PerformRandomizedTest(capiEncryptionTransform, ourEncryptionTransform, null); var capiDecryptionTransform = aesCapi.CreateDecryptor(); var ourDecryptionTransform = ours.CreateDecryptor(aesCapi.Mode, aesCapi.IV, aesCapi.FeedbackSize, aesCapi.Padding); byte[] decryptedResult = PerformRandomizedTest(capiDecryptionTransform, ourDecryptionTransform, encryptedResult); }
private static void ShowExamplesOfRijndaelMath() { WriteHeader("Rijndael Math"); string m = "x^8 + " + ByteUtilities.ToPolynomial(0x1B); Console.WriteLine("Rijndael involves math in a finite field modulo m(x) = {0} where bytes are treated as polynomials", m); Console.WriteLine(); Console.WriteLine("Some examples:"); const byte left = 0x1B; const byte right = 0xAA; Console.WriteLine("({0}) * ({1}) = {2:X2} * {3:X2}", left.ToPolynomial(), right.ToPolynomial(), left, right); byte logLeft = FiniteFieldMath.Log(left); byte logRight = FiniteFieldMath.Log(right); byte logAddition = (byte)(logLeft + logRight); Console.WriteLine("Log({0:X2}) + Log({1:X2}) = {2:X2} ^ {3:X2} = {4:X2}", left, right, logLeft, logRight, logAddition); Console.WriteLine("AntiLog({0:X2}) = Log^-1 ({0:X2}) = {1:X2}", logAddition, FiniteFieldMath.AntiLog(logAddition)); Console.WriteLine("So {0:X2} * {1:X2} = {2:X2}", left, right, FiniteFieldMath.Multiply(left, right)); Console.WriteLine(); byte accumulator = 0x01; for (int i = 0; i < 0x10; i++) { const byte multiplier = 0x03; byte newResult = FiniteFieldMath.Multiply(accumulator, multiplier); Console.WriteLine( "({0}) * ({1}) = {2} => {3:X2} * {4:X2} = {5:X2}", multiplier.ToPolynomial(), accumulator.ToPolynomial(), newResult.ToPolynomial(), multiplier, accumulator, newResult); accumulator = newResult; } Random weakRng = new Random(); Console.WriteLine(); Console.WriteLine("Rijndael's substitution box uses the \"g\" function that gives inverses in the field: g(a) = a^-1 mod m"); Console.WriteLine(); Console.WriteLine("Examples of a * g(a):"); for (int i = 0; i < 5; i++) { byte currentByte = (byte)weakRng.Next(2, 256); byte inverseByte = FiniteFieldMath.G(currentByte); byte resultByte = FiniteFieldMath.Multiply(currentByte, inverseByte); Console.WriteLine( "({0}) * ({1}) = {2} => {3:X2} * {4:X2} = {5:X2}", currentByte.ToPolynomial(), inverseByte.ToPolynomial(), resultByte.ToPolynomial(), currentByte, inverseByte, resultByte); } Console.WriteLine(); Console.WriteLine("The actual s-box values is f(g(a)) where \"f\" is an affine transform"); Console.WriteLine(); Console.WriteLine("Some examples:"); for (int i = 0; i < 5; i++) { byte currentByte = (byte)weakRng.Next(2, 256); byte resultByte = FiniteFieldMath.F(FiniteFieldMath.G(currentByte)); Console.WriteLine("f(g({0:X2})) = {1:X2}", currentByte, resultByte); } }
public Rijndael() : this(ByteUtilities.GetCryptographicallyRandomBytes(256 / Constants.BitsPerByte)) { }
/// <summary> /// Updates the key schedule with a new key and plaintext block size count. /// </summary> /// <param name="key">The key to derive round keys from.</param> /// <param name="plaintextBlockSizeInBytes">The intended plaintext block size.</param> private void Rekey(byte[] key, int plaintextBlockSizeInBytes) { _Key = key; int keyColumns = key.Length / Constants.StateRows; if (keyColumns < Constants.MinKeySizeColumns) { throw new ArgumentException("Key must be at least 128 bits", "key"); } if ((key.Length % Constants.StateRows) != 0) { throw new ArgumentException("Key must be a multiple of 32 bits", "key"); } ByteMatrix keyMatrix = new ByteMatrix(Constants.StateRows, key); int plaintextBlockSizeColumns = plaintextBlockSizeInBytes / Constants.StateRows; int numberOfRounds = Constants.GetRounds(keyMatrix.Columns, plaintextBlockSizeColumns); // There are Nr rounds, so Nb * (Nr + 1) round keys where Nr is the number of rounds (plus the initial round) // and Nb is the size of the block in columns. ByteMatrix allRoundKeys = new ByteMatrix(Constants.StateRows, plaintextBlockSizeColumns * (numberOfRounds + 1)); // The initial round key (#0) is the key itself for (int col = 0; col < keyMatrix.Columns; col++) { for (int row = 0; row < Constants.StateRows; row++) { allRoundKeys[row, col] = keyMatrix[row, col]; } } ByteMatrix[] roundKeys = new ByteMatrix[numberOfRounds + 1]; // 30 round constants are enough for a 256 bit block and a 256 bit key byte[] roundConstants = Constants.GetRoundConstants(30); // The basic idea for the round keys is that you start with the initial round key // and then when you go to generate the next round key, you take the last column // of the previous round key and move it up by one byte (the previous top byte goes // to the bottom). Then you put each of the bytes of that column through the S-box. // Then you XOR the new top byte with the round key. Finally, you xor the whole column // with the column Nb columns earlier. // The other columns are made by xor-ing the previous column with the column Nb columns // earlier. // Say you have a key like this // | S | | | | // | O | 1 | B | K | // | M | 2 | I | E | // | E | 8 | T | Y | // Taking the last column gives us: // // | | // | K | // | E | // | Y | // Bumping it up and then moving the top to the bottom and then putting it // through the s-box gives us: // | K | = | 53 | => | S-Box | => | B3 | // | E | = | 45 | => | S-Box | => | 6E | // | Y | = | 59 | => | S-Box | => | CB | // | | = | 20 | => | S-Box | => | B7 | // Adding in the first round constant gives us: // | B3 | ⊕ | 01 | => | B2 | // | 6E | ⊕ | 00 | => | 6E | // | CB | ⊕ | 00 | => | CB | // | B7 | ⊕ | 00 | => | B7 | // Now, we xor that with the column from 4 columns ago (e.g. the first column of the // initial round key) // | S | = | 53 | ⊕ | B2 | = | E1 | // | O | = | 4F | ⊕ | 6E | = | 21 | // | M | = | 4D | ⊕ | CB | = | 86 | // | E | = | 45 | ⊕ | B7 | = | F2 | // This means the first column of the next round key is: // | E1 | // | 21 | // | 86 | // | F2 | // Now, to calculate the next column, we just xor the previous column with the // one from 4 columns ago: // | | = | 20 | ⊕ | E1 | = | C1 | // | 1 | = | 31 | ⊕ | 21 | = | 10 | // | 2 | = | 32 | ⊕ | 86 | = | B4 | // | 8 | = | 38 | ⊕ | F2 | = | CA | // So the second column is // | C1 | // | 10 | // | B4 | // | CA | // The third and fourth column are computed similarly. The next round key starts // the process over again with a new round key (02) and the process continues until // all round keys are made. // (Note: For keys bigger than 192 bits, you put every 4th column through the s-box first.) for (int col = keyMatrix.Columns; col < allRoundKeys.Columns; col++) { if ((col % keyMatrix.Columns) == 0) { // Most of the work is when we're starting a new round key byte roundConstant = roundConstants[col / keyMatrix.Columns]; // The upper left byte is xor'd with the round constant to prevent symmetry allRoundKeys[0, col] = (byte) (allRoundKeys[0, col - keyMatrix.Columns] ^ SubstitutionBox.Value(allRoundKeys[1, col - 1]) ^ roundConstant); for (int row = 1; row < Constants.StateRows; row++) { allRoundKeys[row, col] = (byte) (allRoundKeys[row, col - keyMatrix.Columns] ^ SubstitutionBox.Value(allRoundKeys[(row + 1) % Constants.StateRows, col - 1])); } } else { // Special case if we have bigger than a 192 bit key if (((col % keyMatrix.Columns) == Constants.StateRows) && (keyMatrix.Columns > 6)) { for (int row = 0; row < Constants.StateRows; row++) { allRoundKeys[row, col] = (byte) (allRoundKeys[row, col - keyMatrix.Columns] ^ SubstitutionBox.Value(allRoundKeys[row, col - 1])); } } else { for (int row = 0; row < Constants.StateRows; row++) { allRoundKeys[row, col] = (byte)(allRoundKeys[row, col - keyMatrix.Columns] ^ allRoundKeys[row, col - 1]); } } } } // The actual round keys are just subsets of allRoundKeys (aka "W") for (int currentRoundKey = 0; currentRoundKey < roundKeys.Length; currentRoundKey++) { roundKeys[currentRoundKey] = allRoundKeys.SubMatrix(plaintextBlockSizeColumns * currentRoundKey, plaintextBlockSizeColumns); } _RoundKeys = roundKeys; if (Debugging.IsEnabled) { Debugging.Trace("Re-keyed Rijndael with {0}-bit key for {1} bit blocks. Key is:", key.Length * Constants.BitsPerByte, plaintextBlockSizeInBytes * Constants.BitsPerByte); ByteUtilities.WriteBytes(key); Debugging.Trace(""); Debugging.Trace("There are {0} round keys.", _RoundKeys.Length); for (int i = 0; i < _RoundKeys.Length; i++) { Debugging.Trace("Round key {0}:", i); Debugging.Trace(_RoundKeys[i].ToString()); } } }