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