-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
TbcPadding.cs
161 lines (137 loc) · 6.07 KB
/
TbcPadding.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
using System;
namespace Algorithms.Crypto.Paddings;
/// <summary>
/// <para>
/// Trailing-Bit-Complement padding is a padding scheme that is defined in the ISO/IEC 9797-1 standard.
/// </para>
/// <para>
/// It is used for adding data to the end of a message that needs to be encrypted or decrypted by a block cipher.
/// </para>
/// <para>
/// The padding bytes are either 0x00 or 0xFF, depending on the last bit of the original data. For example, if the last
/// bit of the original data is 0, then the padding bytes are 0xFF; if the last bit is 1, then the padding bytes are 0x00.
/// The padding bytes are added at the end of the data block until the desired length is reached.
/// </para>
/// </summary>
public class TbcPadding : IBlockCipherPadding
{
/// <summary>
/// Adds padding to the input array according to the TBC standard.
/// </summary>
/// <param name="input">The input array to be padded.</param>
/// <param name="inputOffset">The offset in the input array where the padding starts.</param>
/// <returns>The number of bytes that were added.</returns>
/// <exception cref="ArgumentException">Thrown when the input array does not have enough space for padding.</exception>
public int AddPadding(byte[] input, int inputOffset)
{
// Calculate the number of bytes to be padded.
var count = input.Length - inputOffset;
byte code;
// Check if the input array has enough space for padding.
if (count < 0)
{
throw new ArgumentException("Not enough space in input array for padding");
}
if (inputOffset > 0)
{
// Get the last bit of the previous byte.
var lastBit = input[inputOffset - 1] & 0x01;
// Set the padding code to 0xFF if the last bit is 0, or 0x00 if the last bit is 1.
code = (byte)(lastBit == 0 ? 0xff : 0x00);
}
else
{
// Get the last bit of the last byte in the input array.
var lastBit = input[^1] & 0x01;
// Set the padding code to 0xff if the last bit is 0, or 0x00 if the last bit is 1.
code = (byte)(lastBit == 0 ? 0xff : 0x00);
}
while (inputOffset < input.Length)
{
// Set each byte to the padding code.
input[inputOffset] = code;
inputOffset++;
}
// Return the number of bytes that were padded.
return count;
}
/// <summary>
/// Removes the padding from a byte array according to the Trailing-Bit-Complement padding algorithm.
/// </summary>
/// <param name="input">The byte array to remove the padding from.</param>
/// <returns>A new byte array without the padding.</returns>
/// <remarks>
/// This method assumes that the input array has padded with either 0x00 or 0xFF bytes, depending on the last bit of
/// the original data. The method works by finding the last byte that does not match the padding code and copying all
/// the bytes up to that point into a new array. If the input array is not padded or has an invalid padding, the
/// method may return incorrect results.
/// </remarks>
public byte[] RemovePadding(byte[] input)
{
if (input.Length == 0)
{
return Array.Empty<byte>();
}
// Get the last byte of the input array.
var lastByte = input[^1];
// Determine the byte code
var code = (byte)((lastByte & 0x01) == 0 ? 0x00 : 0xff);
// Start from the end of the array and move towards the front.
int i;
for (i = input.Length - 1; i >= 0; i--)
{
// If the current byte does not match the padding code, stop.
if (input[i] != code)
{
break;
}
}
// Create a new array of the appropriate length.
var unpadded = new byte[i + 1];
// Copy the unpadded data into the new array.
Array.Copy(input, unpadded, i + 1);
// Return the new array.
return unpadded;
}
/// <summary>
/// Returns the number of padding bytes in a byte array according to the Trailing-Bit-Complement padding algorithm.
/// </summary>
/// <param name="input">The byte array to check for padding.</param>
/// <returns>The number of padding bytes in the input array.</returns>
/// <remarks>
/// This method assumes that the input array has been padded with either 0x00 or 0xFF bytes, depending on the last
/// bit of the original data. The method works by iterating backwards from the end of the array and counting the
/// number of bytes that match the padding code. The method uses bitwise operations to optimize the performance and
/// avoid branching. If the input array is not padded or has an invalid padding, the method may return incorrect
/// results.
/// </remarks>
public int GetPaddingCount(byte[] input)
{
var length = input.Length;
if (length == 0)
{
throw new ArgumentException("No padding found.");
}
// Get the value of the last byte as the padding value
var paddingValue = input[--length] & 0xFF;
var paddingCount = 1; // Start count at 1 for the last byte
var countingMask = -1; // Initialize counting mask
// Check if there is no padding
if (paddingValue != 0 && paddingValue != 0xFF)
{
throw new ArgumentException("No padding found");
}
// Loop backwards through the array
for (var i = length - 1; i >= 0; i--)
{
var currentByte = input[i] & 0xFF;
// Calculate matchMask. If currentByte equals paddingValue, matchMask will be 0, otherwise -1
var matchMask = ((currentByte ^ paddingValue) - 1) >> 31;
// Update countingMask. Once a non-matching byte is found, countingMask will remain -1
countingMask &= matchMask;
// Increment count only if countingMask is 0 (i.e., currentByte matches paddingValue)
paddingCount -= countingMask;
}
return paddingCount;
}
}