Since humans invented the written language, they have tried to share information secretly. This is basically, the objective of Cryptography, the study of the techniques to protect sensitive communications by means of data encryption and its posterior decryption. Encryption is the transformation of data into some unreadable form, so, even those who can see the encrypted data cannot understand the hidden information. Decryption is the reverse of encryption; it is the transformation of encrypted data back into some intelligible form.
There are some essential concepts concerning Cryptography.
- Cipher: The procedure that will render a message unintelligible to the recipient. It's also used to recreate the original message, depending on the encryption mechanism used.
- Plaintext: The message or information that is being encrypted.
- Ciphertext: The message or information that is created after the cipher has been used.
Although there are different methods to encrypt and decrypt messages, we'll focus on a linear algebra based cipher, the Hill cipher, which uses a matrix as a cipher to encode a message, and it's extremely difficult to break when a large matrix is used. The receiver of the message decodes it using the inverse of the matrix. This first matrix is called the encoding matrix and its inverse is called the decoding matrix.
Suppose we have an invertible matrix A (the encoding matrix) and a text we want to encrypt.
Transform the text to a sequence of numbers by giving each character a unique numerical value, then split the numbers to form a matrix by grouping the numbers into columns according to the order of the matrix A (the amount of elements in each column must be equal to the order of the matrix). Let's call this matrix B (the plain matrix). Multiply the matrix A by the matrix B:
C = A•B
The matrix C is the cipher matrix. To decrypt the message, just multiply Inv(A)•C, where Inv(A) is the inverse matrix of A.
Note that:
Inv(A)•C = Inv(A)•A•B = I•B = B
The original plaintext can be found again by taking the resulting matrix and splitting it back up into its separate vectors, making them a sequence, and then converting the numbers back into their letter forms.
In many articles, authors use only the 26 letters of English alphabet, sometimes 29 when including space, question mark and period. In these cases a simple mapping of the letters and additional symbols to the first integer numbers, and the use of modular arithmetic, allow this method to obtain an encrypted message composed only by the same integer numbers, which can be mapped again to their corresponding letters and symbols.
In this post we are not going to do any consideration about which letters or symbols you can use, this way any text, regardless of languages or symbols, can be encrypted using this method. To transform characters to numbers, we'll use the Unicode number for character.
The cipher matrix C, can be shown as a sequence of numbers splitting into its separate vectors, in the same way you must do to get the original plaintext, but you cannot convert numbers to letters using the previous method, because the resulting numbers may range in a wider numerical set. If you prefer, it can be transform to a text, mapping digits, the minus sign and the list separator to different chars. In this example we use simple mapping functions to do that.
Let's see an example:
We shall use the following message
The password is: NCS-2014First, we must assign each letter a numeric equivalent. As state above, we'll use the Unicode number for each character. For the message to encrypt, we get the following sequence of numbers:
84 104 101 32 112 97 115 115 119 111 114 100 32 105 115 58 32 78 67 83 45 50 48 49 52
We choose the following 4x4 invertible matrix A:
┌ ┐ │ 1 -1 -1 1 │ │ 2 -3 -5 4 │ A = │ -2 -1 -2 2 │ │ 3 -3 -1 2 │ └ ┘
We convert the sequence of numbers related to plaintext into a matrix, splitting it into column vectors of 4 elements (the order of the encoding matrix). We fill out the last column with zeros as necessary to complete the 4 elements.
┌ ┐ │ 84 112 119 32 32 45 52 │ │ 104 97 111 105 78 50 0 │ B = │ 101 115 114 115 67 48 0 │ │ 32 115 100 58 83 49 0 │ └ ┘
We now encode the message by multiplying the encoding matrix A by the above matrix B. The result is the cipher matrix C:
┌ ┐ │ -89 15 -6 -130 -30 -4 52 │ │ -521 -182 -265 -594 -173 -104 104 │ C = A•B = │ -410 -321 -377 -283 -110 -138 -104 │ │ -97 160 110 -218 -39 35 156 │ └ ┘
The columns of this matrix give the encoded message. The message could be transmitted in the following linear form:
-89 -521 -410 -97 15 -182 -321 160 -6 -265 -377 110 -130 -594 -283 -218 -30 -173 -110 -39 -4 -104 -138 35 52 104 -104 156
To decode the message, write the sequence of numbers you received as a matrix, by splitting the numbers into column vectors of 4 elements. The resulting matrix from this process will be equal to the cipher matrix C. You must know the inverse of the encoding matrix:
┌ ┐ │ 6 -1 0 -1 │ │ 22 -4 1 -4 │ Inv(A) = │ 14 -3 1 -2 │ │ 31 -6 2 -5 │ └ ┘
Multiply that matrix (decoding matrix) by the cipher matrix C. Form back the resulting matrix (it'll be equal to matrix B) into a continuous sequence of numbers and map the numbers to their corresponding characters, to get the original message.
We have developed the whole encryption and decryption algorithms, using the coding and decoding matrices selected in the above example, where you could encrypt the text you want. We have also included a checkbox to specify if you want to see the ciphertext as a sequence of numbers or as a sequence of chars (map numbers to chars).
<script type="text/javascript"> var encodingMatrix = [[1,-1,-1,1],[2,-3,-5,4],[-2,-1,-2,2],[3, -3,-1,2]]; var decodingMatrix = [[6,-1,0,-1],[22,-4,1,-4],[14,-3,1,-2],[31,-6,2,-5]]; function getMatrixFromArray(arr, rows){ var matrix = new Array(); for (var i=0; i<rows; i++) matrix[i] = new Array(); for (var i=0; i<arr.length; i++) matrix[i % rows][Math.floor(i/rows)] = arr[i]; if (arr.length % rows != 0) for (var i=arr.length % rows; i<rows; i++) matrix[i][Math.floor((arr.length - 1)/rows)] = 0; return matrix; } function getMatrixFromText(text, rows){ var arr = new Array(); for (var i=0; i<text.length; i++) arr[i] = text.charCodeAt(i); return getMatrixFromArray(arr, rows); } function getTextFromMatrix(matrix){ var text = new String(); for (var j=0; j<matrix[0].length; j++) for (var i=0; i<matrix.length; i++) text += matrix[i][j]>0 ? String.fromCharCode(matrix[i][j]) : ""; return text; } function getMatrixFromNumbers(text, rows){ var i = 0; var numbers = text.split(" "); while (i<numbers.length){ if (numbers[i].replace(/s+/g, "") == "") numbers.splice(i, 1); else i++; } var arr = new Array(); for (var i=0; i<numbers.length; i++) arr[i] = parseInt(numbers[i]); return getMatrixFromArray(arr, rows); } function getNumbersFromMatrix(matrix){ var text = ""; for (var j=0; j<matrix[0].length; j++) for (var i=0; i<matrix.length; i++) text += matrix[i][j].toString() + " "; return text; } function multiplyMatrices(m1, m2){ var matrix = new Array(); for (var i=0; i<m1.length; i++) matrix[i] = new Array(); for (var i=0; i<m1.length; i++) for (var j=0; j<m2[0].length; j++){ matrix[i][j] = 0; for (var k=0; k<m1[0].length; k++) matrix[i][j] += m1[i][k]*m2[k][j]; } return matrix; } function numberToChar(text){ var result = new String(); for (var i=0; i<text.length; i++) result += String.fromCharCode(text.charCodeAt(i) + (text.charCodeAt(i)==32 ? 33 : 21)); return result; } function charToNumber(text){ var result = new String(); for (var i=0; i<text.length; i++) result += String.fromCharCode(text.charCodeAt(i)-(text.charCodeAt(i)==65 ? 33 : 21)); return result; } function encryptText(){ var plainText = document.getElementById("plainText").value; var plainMatrix = getMatrixFromText(plainText, 4); var cipherMatrix = multiplyMatrices(encodingMatrix,plainMatrix); var cipherText = getNumbersFromMatrix(cipherMatrix); if (document.getElementById("mapNumbers").checked) cipherText = numberToChar(cipherText); document.getElementById("cypherText").value = cipherText; } function decryptText(){ var cipherText = document.getElementById("cypherText").value; if (document.getElementById("mapNumbers").checked) var cipherText = charToNumber(cipherText); var cipherMatrix = getMatrixFromNumbers(cipherText, 4); var plainMatrix = multiplyMatrices(decodingMatrix,cipherMatrix) document.getElementById("decryptedText").value = getTextFromMatrix(plainMatrix); } window.onload = function(){ document.getElementById("encrypt").onclick = encryptText; document.getElementById("decrypt").onclick = decryptText; }; </script>
The following is an implementation of a class in C # to use this cryptographic method. Note that both matrices, the encoding and decoding matrix, are required. Although the decoding matrix can be obtained from the coding matrix by finding its inverse, it's not our objective to make an implemention of the inverse of a matrix, that's why both matrices are needed to instantiate the class. In some of the methods of the class it could be used Linq and get a simpler solution, but was not used to maintain the greatest similarity to the implemented JavaScript code.
using System.Collections.Generic; using System.Text; public class HillCipher { public HillCipher(int[,] encodingMatrix, int[,] decodingMatrix, bool mapNumbers = true) { this.EncodingMatrix = encodingMatrix; this.DecodingMatrix = decodingMatrix; this.MapNumbers = mapNumbers; } private int[,] EncodingMatrix { get; set; } private int[,] DecodingMatrix { get; set; } private bool MapNumbers { get; set; } public string EncryptText(string plainText) { var plainMatrix = this.GetMatrixFromText(plainText); var cipherMatrix = MultiplyMatrices(this.EncodingMatrix, plainMatrix); var cipherText = GetNumbersFromMatrix(cipherMatrix); if (this.MapNumbers) cipherText = NumberToChar(cipherText); return cipherText; } public string DecryptText(string cipherText) { if (this.MapNumbers) cipherText = CharToNumber(cipherText); var cipherMatrix = this.GetMatrixFromNumbers(cipherText); var plainMatrix = MultiplyMatrices(this.DecodingMatrix, cipherMatrix); return GetTextFromMatrix(plainMatrix); } private static string GetTextFromMatrix(int[,] matrix) { var text = new StringBuilder(); for (var j = 0; j < matrix.GetLength(1); j++) for (var i = 0; i < matrix.GetLength(0); i++) text.Append(matrix[i, j] > 0 ? ((char)matrix[i, j]).ToString() : string.Empty); return text.ToString(); } private static string GetNumbersFromMatrix(int[,] matrix) { var text = new StringBuilder(); for (var j = 0; j < matrix.GetLength(1); j++) for (var i = 0; i < matrix.GetLength(0); i++) text.Append(matrix[i, j].ToString("0") + " "); return text.ToString(); } private static int[,] MultiplyMatrices(int[,] m1, int[,] m2) { var rows = m1.GetLength(0); var columns = m2.GetLength(1); var matrix = new int[rows, columns]; for (var i = 0; i < rows; i++) for (var j = 0; j < columns; j++) for (var k = 0; k < m1.GetLength(1); k++) matrix[i, j] += m1[i, k] * m2[k, j]; return matrix; } private static string NumberToChar(string text) { var result = new StringBuilder(); foreach (var t in text) result.Append((char)(t + ((int)t == 32 ? 33 : 21))); return result.ToString(); } private static string CharToNumber(string text) { var result = new StringBuilder(); foreach (var t in text) result.Append((char)(t - ((int)t == 65 ? 33 : 21))); return result.ToString(); } private int[,] GetMatrixFromArray(IListarr) { var rows = this.EncodingMatrix.GetLength(0); var columns = arr.Count % rows == 0 ? arr.Count / rows : (arr.Count / rows) + 1; var matrix = new int[rows, columns]; for (var i = 0; i < arr.Count; i++) matrix[i % rows, i / rows] = arr[i]; if (arr.Count % rows != 0) for (var i = arr.Count % rows; i < rows; i++) matrix[i, (arr.Count - 1) / rows] = 0; return matrix; } private int[,] GetMatrixFromText(string text) { var arr = new int[text.Length]; for (var i = 0; i < text.Length; i++) arr[i] = text[i]; return this.GetMatrixFromArray(arr); } private int[,] GetMatrixFromNumbers(string text) { var numbers = text.Split(' '); var arr = new List (); foreach (var t in numbers) if (!string.IsNullOrWhiteSpace(t)) arr.Add(int.Parse(t)); return this.GetMatrixFromArray(arr); } }
An example of how to use this class.
public void TestHillCipher() { var encodingMatrix = new[,] { { 1, -1, -1, 1 }, { 2, -3, -5, 4 }, { -2, -1, -2, 2 }, { 3, -3, -1, 2 } }; var decodingMatrix = new[,] { { 6, -1, 0, -1 }, { 22, -4, 1, -4 }, { 14, -3, 1, -2 }, { 31, -6, 2, -5 } }; var hillCipher = new HillCipher(encodingMatrix, decodingMatrix, true); const string PlainText = "Lorem ipsum dolor sit amet, quis phasellus diam, nunc duis. Odio magna amet wisi duis sit iaculis, ac sodales dolores pellentesque orci vestibulum, nulla et quis id iaculis lorem, vestibulum massa sociis in, mauris ac vestibulum sociis hendrerit eros a. Mauris varius molestie in sem mi, id mattis purus, dolor vitae euismod nisl mauris nulla varius, ut libero sem, quam velit vestibulum quam."; var cipherText = hillCipher.EncryptText(PlainText); Console.WriteLine(cipherText); var decryptedText = hillCipher.DecryptText(cipherText); Console.WriteLine(decryptedText); }
Your comment