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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
|
// xormac.h - written and placed in the public domain by Wei Dai
#ifndef CRYPTOPP_XORMAC_H
#define CRYPTOPP_XORMAC_H
#include "seckey.h"
#include "iterhash.h"
#include "argnames.h"
#include "algparam.h"
NAMESPACE_BEGIN(CryptoPP)
template <class T> struct DigestSizeSubtract4Workaround // VC60 workaround
{
CRYPTOPP_CONSTANT(RESULT = T::DIGESTSIZE-4)
};
template <class T>
class CRYPTOPP_NO_VTABLE XMACC_Base : public FixedKeyLength<DigestSizeSubtract4Workaround<T>::RESULT, SimpleKeyingInterface::INTERNALLY_GENERATED_IV>,
public IteratedHash<typename T::HashWordType, typename T::ByteOrderClass, T::BLOCKSIZE, MessageAuthenticationCode>
{
public:
static std::string StaticAlgorithmName() {return std::string("XMAC(") + T::StaticAlgorithmName() + ")";}
CRYPTOPP_CONSTANT(DIGESTSIZE = 4+T::DIGESTSIZE)
typedef typename T::HashWordType HashWordType;
XMACC_Base() {SetStateSize(T::DIGESTSIZE);}
void UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms);
void Resynchronize(const byte *IV)
{
GetWord(false, BIG_ENDIAN_ORDER, m_counter, IV);
this->Restart();
}
unsigned int IVSize() const
{return 4;}
void GetNextIV(byte *IV)
{
if (m_counter == 0xffffffff)
throw NotImplemented("XMACC: must have a valid counter to get next IV");
PutWord(false, BIG_ENDIAN_ORDER, IV, m_counter+1);
}
word32 CurrentCounter() const {return m_counter;}
void TruncatedFinal(byte *mac, size_t size);
bool TruncatedVerify(const byte *mac, size_t length);
unsigned int DigestSize() const {return DIGESTSIZE;} // need to override this
private:
void Init();
static void WriteWord32(byte *output, word32 value);
static void XorDigest(HashWordType *digest, const HashWordType *buffer);
void HashEndianCorrectedBlock(const HashWordType *data);
FixedSizeSecBlock<byte, DigestSizeSubtract4Workaround<T>::RESULT> m_key;
CRYPTOPP_CONSTANT(BUFFER_SIZE = (T::DIGESTSIZE / sizeof(HashWordType))) // VC60 workaround
#ifdef __BORLANDC__
FixedSizeSecBlock<HashWordType, T::DIGESTSIZE / sizeof(HashWordType)> m_buffer;
#else
FixedSizeSecBlock<HashWordType, BUFFER_SIZE> m_buffer;
#endif
word32 m_counter, m_index;
};
//! <a href="http://www.weidai.com/scan-mirror/mac.html#XMAC">XMAC</a>
/*! If you need to generate MACs with XMACC (instead of just verifying them),
you must save the counter before destroying an XMACC object
and reinitialize it the next time you create an XMACC with the same key.
Start counter at 0 when using a key for the first time. */
template <class T>
class XMACC : public ClonableImpl<XMACC<T>, MessageAuthenticationCodeImpl<XMACC_Base<T> > >
{
public:
XMACC() {}
XMACC(const byte *key, word32 counter = 0xffffffff)
{this->SetKey(key, this->KEYLENGTH, MakeParameters(Name::XMACC_Counter(), counter));}
};
template <class T> void XMACC_Base<T>::UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms)
{
this->AssertValidKeyLength(length);
m_counter = 0xffffffff;
const byte *iv = NULL;
if (params.GetValue(Name::IV(), iv))
GetWord(false, BIG_ENDIAN_ORDER, m_counter, iv);
else
params.GetValue(Name::XMACC_Counter(), m_counter);
memcpy_s(m_key, m_key.SizeInBytes(), key, this->KEYLENGTH);
Init();
}
template <class T> void XMACC_Base<T>::Init()
{
m_index = 0x80000000;
memset(this->m_digest, 0, T::DIGESTSIZE);
}
template <class T> inline void XMACC_Base<T>::WriteWord32(byte *output, word32 value)
{
output[0] = byte(value >> 24);
output[1] = byte(value >> 16);
output[2] = byte(value >> 8);
output[3] = byte(value);
}
template <class T> inline void XMACC_Base<T>::XorDigest(HashWordType *digest, const HashWordType *buffer)
{
for (unsigned i=0; i<(T::DIGESTSIZE/sizeof(HashWordType)); i++)
digest[i] ^= buffer[i];
}
template <class T> void XMACC_Base<T>::HashEndianCorrectedBlock(const HashWordType *input)
{
memcpy_s(m_buffer, m_buffer.SizeInBytes(), m_key, this->KEYLENGTH);
WriteWord32((byte *)m_buffer.begin()+this->KEYLENGTH, ++m_index);
T::CorrectEndianess(m_buffer, m_buffer, T::DIGESTSIZE);
T::Transform(m_buffer, input);
XorDigest(this->m_digest, m_buffer);
}
template <class T> void XMACC_Base<T>::TruncatedFinal(byte *mac, size_t size)
{
this->ThrowIfInvalidTruncatedSize(size);
if (size < 4)
throw InvalidArgument("XMACC: truncating the MAC to less than 4 bytes will cause it to be unverifiable");
if (m_counter == 0xffffffff)
throw InvalidArgument("XMACC: the counter must be initialized to a valid value for MAC generation");
PadLastBlock(this->BLOCKSIZE - 2*sizeof(HashWordType));
CorrectEndianess(this->m_data, this->m_data, this->BLOCKSIZE - 2*sizeof(HashWordType));
this->m_data[this->m_data.size()-2] = ByteReverse(this->GetBitCountHi()); // ByteReverse for backwards compatibility
this->m_data[this->m_data.size()-1] = ByteReverse(this->GetBitCountLo());
HashEndianCorrectedBlock(this->m_data);
memcpy_s(m_buffer, m_buffer.SizeInBytes(), m_key, this->KEYLENGTH);
WriteWord32((byte *)m_buffer.begin()+this->KEYLENGTH, 0);
memset(this->m_data, 0, this->BLOCKSIZE-4);
WriteWord32((byte *)this->m_data.begin()+this->BLOCKSIZE-4, ++m_counter);
T::CorrectEndianess(m_buffer, m_buffer, T::DIGESTSIZE);
T::CorrectEndianess(this->m_data, this->m_data, this->BLOCKSIZE);
T::Transform(m_buffer, this->m_data);
XorDigest(this->m_digest, m_buffer);
WriteWord32(mac, m_counter);
T::CorrectEndianess(this->m_digest, this->m_digest, T::DIGESTSIZE);
memcpy_s(mac+4, size-4, this->m_digest, size-4);
this->Restart(); // reinit for next use
}
template <class T> bool XMACC_Base<T>::TruncatedVerify(const byte *mac, size_t size)
{
assert(4 <= size && size <= DIGESTSIZE);
PadLastBlock(this->BLOCKSIZE - 2*sizeof(HashWordType));
CorrectEndianess(this->m_data, this->m_data, this->BLOCKSIZE - 2*sizeof(HashWordType));
this->m_data[this->m_data.size()-2] = ByteReverse(this->GetBitCountHi()); // ByteReverse for backwards compatibility
this->m_data[this->m_data.size()-1] = ByteReverse(this->GetBitCountLo());
HashEndianCorrectedBlock(this->m_data);
memcpy_s(m_buffer, m_buffer.SizeInBytes(), m_key, this->KEYLENGTH);
WriteWord32((byte *)m_buffer.begin()+this->KEYLENGTH, 0);
memset(this->m_data, 0, this->BLOCKSIZE-4);
memcpy_s((byte *)this->m_data.begin()+this->BLOCKSIZE-4, 4, mac, 4);
T::CorrectEndianess(m_buffer, m_buffer, T::DIGESTSIZE);
T::CorrectEndianess(this->m_data, this->m_data, this->BLOCKSIZE);
T::Transform(m_buffer, this->m_data);
XorDigest(this->m_digest, m_buffer);
T::CorrectEndianess(this->m_digest, this->m_digest, T::DIGESTSIZE);
bool macValid = (memcmp(mac+4, this->m_digest, size-4) == 0);
this->Restart(); // reinit for next use
return macValid;
}
NAMESPACE_END
#endif
|