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 PRESENTCipher
13     {
14         // For cryptographic purposes use provided constants, more info: https://link.springer.com/content/pdf/10.1007%2F978-3-540-74735-2_31.pdf
15         // Note that current implementation assumes 64-bit `key`, but could be extended to 80-bit or 128-bit in the future.
16 
Scramble(ulong data, ulong key, int width = OriginalDataWidth, uint rounds = OriginalNumberOfRounds)17         static public ulong Scramble(ulong data, ulong key, int width = OriginalDataWidth, uint rounds = OriginalNumberOfRounds)
18         {
19             if(width < 0 || width > 64)
20             {
21                 throw new ArgumentException($"{width} is out of bounds [0, 64]", "width");
22             }
23             if(data > (ulong.MaxValue >> (64 - width)))
24             {
25                 throw new ArgumentException($"0x{data:X} doesn't fit in width ({width}) bits", "data");
26             }
27 
28             var state = data;
29             for(var i = 0; i < rounds; ++i)
30             {
31                 state ^= key;
32                 state = Substitute(state, width, coefficientsForward);
33                 state = Permutate(state, width);
34             }
35 
36             return state ^ key;
37         }
38 
Descramble(ulong data, ulong key, int width = OriginalDataWidth, uint rounds = OriginalNumberOfRounds)39         static public ulong Descramble(ulong data, ulong key, int width = OriginalDataWidth, uint rounds = OriginalNumberOfRounds)
40         {
41             if(width < 0 || width > 64)
42             {
43                 throw new ArgumentException($"{width} is out of bounds [0, 64]", "width");
44             }
45             if(data > (ulong.MaxValue >> (64 - width)))
46             {
47                 throw new ArgumentException($"0x{data:X} doesn't fit in width ({width}) bits", "data");
48             }
49 
50             var state = data;
51             for(var i = 0; i < rounds; ++i)
52             {
53                 state ^= key;
54                 state = ReversePermutate(state, width);
55                 state = Substitute(state, width, coefficientsReverse);
56             }
57 
58             return state ^ key;
59         }
60 
Substitute(ulong data, int width, ulong[] coefficients)61         static private ulong Substitute(ulong data, int width, ulong[] coefficients)
62         {
63             var mask = ulong.MaxValue >> (64 - width);
64             var substitutionMask = ulong.MaxValue >> (64 - (width & ~0x3));
65 
66             var state = data & (mask & ~substitutionMask);
67             for(int i = 0; i < width / 4; ++i)
68             {
69                 var shift = i * 4;
70                 state |= coefficients[(data >> shift) & 0xf] << shift;
71             }
72 
73             return state;
74         }
75 
Permutate(ulong data, int width)76         static private ulong Permutate(ulong data, int width)
77         {
78             var mask = ulong.MaxValue >> (64 - width);
79             var permutationMask = ulong.MaxValue >> (64 - (width & ~0x1));
80 
81             var reversedData = BitHelper.ReverseBits(data << (64 - width));
82             var state = reversedData & (mask & ~permutationMask);
83             for(byte j = 0; j < width / 2; ++j)
84             {
85                 BitHelper.SetBit(ref state, j, BitHelper.IsBitSet(reversedData, (byte)(2 * j)));
86                 BitHelper.SetBit(ref state, (byte)(width / 2 + j), BitHelper.IsBitSet(reversedData, (byte)(2 * j + 1)));
87             }
88 
89             return state;
90         }
91 
ReversePermutate(ulong data, int width)92         static private ulong ReversePermutate(ulong data, int width)
93         {
94             var mask = ulong.MaxValue >> (64 - width);
95             var permutationMask = ulong.MaxValue >> (64 - (width & ~0x1));
96 
97             var state = data & (mask & ~permutationMask);
98             for(byte j = 0; j < width / 2; ++j)
99             {
100                 BitHelper.SetBit(ref state, (byte)(2 * j), BitHelper.IsBitSet(data, j));
101                 BitHelper.SetBit(ref state, (byte)(2 * j + 1), BitHelper.IsBitSet(data, (byte)(width / 2 + j)));
102             }
103 
104             return BitHelper.ReverseBits(state << (64 - width));
105         }
106 
107         private static readonly ulong[] coefficientsForward = new ulong[]
108         {
109             0xc, 0x5, 0x6, 0xb, 0x9, 0x0, 0xa, 0xd,
110             0x3, 0xe, 0xf, 0x8, 0x4, 0x7, 0x1, 0x2
111         };
112 
113         private static readonly ulong[] coefficientsReverse = new ulong[]
114         {
115             0x5, 0xe, 0xf, 0x8, 0xc, 0x1, 0x2, 0xd,
116             0xb, 0x4, 0x6, 0x3, 0x0, 0x7, 0x9, 0xa
117         };
118 
119         private const int OriginalDataWidth = 64;
120         private const uint OriginalNumberOfRounds = 31;
121     }
122 }
123