-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
Iso7816D4Padding.cs
154 lines (132 loc) · 6.26 KB
/
Iso7816D4Padding.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
using System;
namespace Algorithms.Crypto.Paddings;
/// <summary>
/// <para>
/// ISO 7816-4 padding is a padding scheme that is defined in the ISO/IEC 7816-4 documentation.
/// </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>
/// ISO 7816-4 padding works as follows:
/// <para>
/// The first byte of the padding is 0x80, which is the hexadecimal representation of the binary value 10000000. This
/// byte indicates the start of the padding.
/// </para>
/// <para>
/// All other bytes of the padding are 0x00, which is the hexadecimal representation of the binary value 00000000. These
/// bytes fill up the remaining space in the last block.
/// </para>
/// <para>
/// The padding can be of any size, from 1 byte to the block size. For example, if the block size is 8 bytes and the
/// message has 5 bytes, then 3 bytes of padding are needed. The padding would be <c>0x80 0x00 0x00</c>.
/// </para>
/// <para>
/// ISO 7816-4 padding is also known as bit padding,because it simply places a single 1 bit after the plaintext, followed
/// by 0 valued bits up to the block size. It works for both byte-oriented and bit-oriented protocols, as it does not
/// depend on any specific character encoding or representation.
/// </para>
/// </summary>
public class Iso7816D4Padding : IBlockCipherPadding
{
/// <summary>
/// Adds padding to the input data according to the ISO 7816-4 standard.
/// </summary>
/// <param name="inputData">The input data array that needs padding.</param>
/// <param name="inputOffset">The offset in the input data array where the padding should start.</param>
/// <returns>The number of bytes added as padding.</returns>
/// <exception cref="ArgumentException">
/// Thrown when there is not enough space in the input array for padding or when the input offset is invalid.
/// </exception>
public int AddPadding(byte[] inputData, int inputOffset)
{
// Calculate the number of padding bytes based on the input data length and offset.
var code = (byte)(inputData.Length - inputOffset);
// Check if the padding bytes are valid and fit in the input array.
if (code == 0 || inputOffset + code > inputData.Length)
{
throw new ArgumentException("Not enough space in input array for padding");
}
// Set the first padding byte to 80. This marks the start of padding in the ISO 7816-4 standard.
inputData[inputOffset] = 80;
inputOffset++;
// Set the remaining padding bytes to 0.
while (inputOffset < inputData.Length)
{
inputData[inputOffset] = 0;
inputOffset++;
}
// Return the number of padding bytes.
return code;
}
/// <summary>
/// Removes the padding from the input data array and returns the original data.
/// </summary>
/// <param name="inputData">
/// The input data with ISO 7816-4 padding. Must not be null and must have a valid length and padding.
/// </param>
/// <returns>The input data without the padding as a new byte array.</returns>
/// <exception cref="ArgumentException">
/// Thrown when the input data has invalid padding.
/// </exception>
public byte[] RemovePadding(byte[] inputData)
{
// Find the index of the first padding byte by scanning from the end of the input.
var paddingIndex = inputData.Length - 1;
// Skip all the padding bytes that are 0.
while (paddingIndex >= 0 && inputData[paddingIndex] == 0)
{
paddingIndex--;
}
// Check if the first padding byte is 0x80.
if (paddingIndex < 0 || inputData[paddingIndex] != 0x80)
{
throw new ArgumentException("Invalid padding");
}
// Create a new array to store the unpadded data.
var unpaddedData = new byte[paddingIndex];
// Copy the unpadded data from the input data to the new array.
Array.Copy(inputData, 0, unpaddedData, 0, paddingIndex);
// Return the unpadded data array.
return unpaddedData;
}
/// <summary>
/// Gets the number of padding bytes in the input data according to the ISO 7816-4 standard.
/// </summary>
/// <param name="input">The input data array that has padding.</param>
/// <returns>The number of padding bytes in the input data.</returns>
/// <exception cref="ArgumentException"> Thrown when the input data has invalid padding.</exception>
public int GetPaddingCount(byte[] input)
{
// Initialize the index of the first padding byte to -1.
var paddingStartIndex = -1;
// Initialize a mask to indicate if the current byte is still part of the padding.
var stillPaddingMask = -1;
// Initialize the current index to the end of the input data.
var currentIndex = input.Length;
// Loop backwards through the input data.
while (--currentIndex >= 0)
{
// Get the current byte as an unsigned integer.
var currentByte = input[currentIndex] & 0xFF;
// Compute a mask to indicate if the current byte is 0x00.
var isZeroMask = (currentByte - 1) >> 31;
// Compute a mask to indicate if the current byte is 0x80.
var isPaddingStartMask = ((currentByte ^ 0x80) - 1) >> 31;
// Update the index of the first padding byte using bitwise operations.
// If the current byte is 0x80 and still part of the padding, set the index to the current index.
// Otherwise, keep the previous index.
paddingStartIndex ^= (currentIndex ^ paddingStartIndex) & (stillPaddingMask & isPaddingStartMask);
// Update the mask to indicate if the current byte is still part of the padding using bitwise operations.
// If the current byte is 0x00, keep the previous mask.
// Otherwise, set the mask to 0.
stillPaddingMask &= isZeroMask;
}
// Check if the index of the first padding byte is valid.
if (paddingStartIndex < 0)
{
throw new ArgumentException("Pad block corrupted");
}
// Return the number of padding bytes.
return input.Length - paddingStartIndex;
}
}