1 // 2 // Copyright (c) 2010-2021 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using System; 8 using Antmicro.Renode.Utilities; 9 10 namespace Antmicro.Renode.Utilities 11 { 12 public class PRINCECipher 13 { 14 // For cryptographic purposes use provided constants, more info: https://eprint.iacr.org/2012/529.pdf 15 Scramble(ulong data, ulong k1, ulong k0, int width = OriginalDataWidth, uint rounds = NumberOfRounds)16 static public ulong Scramble(ulong data, ulong k1, ulong k0, int width = OriginalDataWidth, uint rounds = NumberOfRounds) 17 { 18 if(rounds > NumberOfRounds) 19 { 20 throw new ArgumentException($"{rounds} is greater than proper number of rounds ({NumberOfRounds})", "rounds"); 21 } 22 if(width < 0 || width > 64) 23 { 24 throw new ArgumentException($"{width} is out of bounds [0, 64]", "width"); 25 } 26 if(data > (ulong.MaxValue >> (64 - width))) 27 { 28 throw new ArgumentException($"0x{data:X} doesn't fit in width ({width}) bits", "data"); 29 } 30 31 var k0Prime = (((k0 & 0x1) << 63) | (k0 >> 1)) ^ (k0 >> 63); 32 var state = data ^ k0 ^ k1 ^ roundConstant[0]; 33 34 for(var i = 1; i < rounds / 2; ++i) 35 { 36 state = Substitute(state, 64, coefficientsForward); 37 state = Multiply(state); 38 state = ShiftRows(state, false); 39 state ^= roundConstant[i]; 40 state ^= (i & 0x1) == 0x1 ? k0 : k1; 41 } 42 43 state = Substitute(state, 64, coefficientsForward); 44 state = Multiply(state); 45 state = Substitute(state, 64, coefficientsReverse); 46 47 for(var i = NumberOfRounds - rounds / 2 + 1; i < NumberOfRounds; ++i) 48 { 49 state ^= (i & 0x1) == 0x1 ? k1 : k0; 50 state ^= roundConstant[i]; 51 state = ShiftRows(state, true); 52 state = Multiply(state); 53 state = Substitute(state, 64, coefficientsReverse); 54 } 55 56 return state ^ k0Prime ^ k1 ^ roundConstant[NumberOfRounds]; 57 } 58 Substitute(ulong data, int width, ulong[] coefficients)59 static private ulong Substitute(ulong data, int width, ulong[] coefficients) 60 { 61 var mask = ulong.MaxValue >> (64 - width); 62 var substitutionMask = ulong.MaxValue >> (64 - (width & ~0x3)); 63 64 var state = data & (mask & ~substitutionMask); 65 for(int i = 0; i < width / 4; ++i) 66 { 67 var shift = i * 4; 68 state |= coefficients[(data >> shift) & 0xf] << shift; 69 } 70 71 return state; 72 } 73 Multiply(ulong data)74 static private ulong Multiply(ulong data) 75 { 76 var state = 0UL; 77 for(var i = 0; i < 4; ++i) 78 { 79 // segment := data[i * 16 .. (i + 1) * 16 - 1] * block[i] 80 var segment = 0ul; 81 for(int j = 0; j < 16; ++j) 82 { 83 if(BitHelper.IsBitSet(data, (byte)(j + (i * 16)))) 84 { 85 segment ^= m[i][j]; 86 } 87 } 88 89 state |= segment << (i * 16); 90 } 91 return state; 92 } 93 ShiftRows(ulong data, bool inverse)94 static private ulong ShiftRows(ulong data, bool inverse) 95 { 96 // 0x0123456789ABCDEF -> 0x05AF49E38D27C16B 97 var mask = 0xF000F000F000F000; 98 var state = 0ul; 99 for(var i = 0; i < 4; ++i) 100 { 101 var row = data & (mask >> (i * 4)); 102 var shift = (inverse ? i : (4 - i)) * 16; 103 state |= (row >> shift) | (row << (64 - shift)); 104 } 105 return state; 106 } 107 108 private static readonly ulong[] roundConstant = new ulong[] 109 { 110 0x0000000000000000, 0x13198a2e03707344, 111 0xa4093822299f31d0, 0x082efa98ec4e6c89, 112 0x452821e638d01377, 0xbe5466cf34e90c6c, 113 0x7ef84f78fd955cb1, 0x85840851f1ac43aa, 114 0xc882d32f25323c54, 0x64a51195e0e3610d, 115 0xd3b5a399ca0c2399, 0xc0ac29b7c97c50dd 116 }; 117 118 private static readonly ulong[] coefficientsForward = new ulong[] 119 { 120 0xb, 0xf, 0x3, 0x2, 0xa, 0xc, 0x9, 0x1, 121 0x6, 0x7, 0x8, 0x0, 0xe, 0x5, 0xd, 0x4 122 }; 123 124 private static readonly ulong[] coefficientsReverse = new ulong[] 125 { 126 0xb, 0x7, 0x3, 0x2, 0xf, 0xd, 0x8, 0x9, 127 0xa, 0x6, 0x4, 0x0, 0x5, 0xe, 0xc, 0x1 128 }; 129 130 // mX represents M^(X) matrix; mX[n] is n-th column of M^(0) matrix 131 // each digit is one of the columns of M_0 to M_3 bit matrix 132 private static readonly ulong[] m0 = new ulong[] 133 { 134 0x0111, 0x2220, 0x4404, 0x8088, 0x1011, 0x0222, 0x4440, 0x8808, 135 0x1101, 0x2022, 0x0444, 0x8880, 0x1110, 0x2202, 0x4044, 0x0888 136 }; 137 138 private static readonly ulong[] m1 = new ulong[] 139 { 140 0x1110, 0x2202, 0x4044, 0x0888, 0x0111, 0x2220, 0x4404, 0x8088, 141 0x1011, 0x0222, 0x4440, 0x8808, 0x1101, 0x2022, 0x0444, 0x8880 142 }; 143 144 // m[n] is a diagonal block of M' matrix 145 private static readonly ulong[][] m = new ulong[][] { m0, m1, m1, m0 }; 146 147 private const int OriginalDataWidth = 64; 148 private const uint NumberOfRounds = 11; 149 } 150 } 151