1// cmac.cpp - written and placed in the public domain by Wei Dai
2
3#include "pch.h"
4
5#ifndef CRYPTOPP_IMPORTS
6
7#include "cmac.h"
8
9NAMESPACE_BEGIN(CryptoPP)
10
11static void MulU(byte *k, unsigned int length)
12{
13	byte carry = 0;
14
15	for (int i=length-1; i>=1; i-=2)
16	{
17		byte carry2 = k[i] >> 7;
18		k[i] += k[i] + carry;
19		carry = k[i-1] >> 7;
20		k[i-1] += k[i-1] + carry2;
21	}
22
23	if (carry)
24	{
25		switch (length)
26		{
27		case 8:
28			k[7] ^= 0x1b;
29			break;
30		case 16:
31			k[15] ^= 0x87;
32			break;
33		case 32:
34			k[30] ^= 4;
35			k[31] ^= 0x23;
36			break;
37		default:
38			throw InvalidArgument("CMAC: " + IntToString(length) + " is not a supported cipher block size");
39		}
40	}
41}
42
43void CMAC_Base::UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs &params)
44{
45	BlockCipher &cipher = AccessCipher();
46	unsigned int blockSize = cipher.BlockSize();
47
48	cipher.SetKey(key, length, params);
49	m_reg.CleanNew(3*blockSize);
50	m_counter = 0;
51
52	cipher.ProcessBlock(m_reg, m_reg+blockSize);
53	MulU(m_reg+blockSize, blockSize);
54	memcpy(m_reg+2*blockSize, m_reg+blockSize, blockSize);
55	MulU(m_reg+2*blockSize, blockSize);
56}
57
58void CMAC_Base::Update(const byte *input, size_t length)
59{
60	if (!length)
61		return;
62
63	BlockCipher &cipher = AccessCipher();
64	unsigned int blockSize = cipher.BlockSize();
65
66	if (m_counter > 0)
67	{
68		unsigned int len = UnsignedMin(blockSize - m_counter, length);
69		xorbuf(m_reg+m_counter, input, len);
70		length -= len;
71		input += len;
72		m_counter += len;
73
74		if (m_counter == blockSize && length > 0)
75		{
76			cipher.ProcessBlock(m_reg);
77			m_counter = 0;
78		}
79	}
80
81	if (length > blockSize)
82	{
83		assert(m_counter == 0);
84		size_t leftOver = 1 + cipher.AdvancedProcessBlocks(m_reg, input, m_reg, length-1, BlockTransformation::BT_DontIncrementInOutPointers|BlockTransformation::BT_XorInput);
85		input += (length - leftOver);
86		length = leftOver;
87	}
88
89	if (length > 0)
90	{
91		assert(m_counter + length <= blockSize);
92		xorbuf(m_reg+m_counter, input, length);
93		m_counter += (unsigned int)length;
94	}
95
96	assert(m_counter > 0);
97}
98
99void CMAC_Base::TruncatedFinal(byte *mac, size_t size)
100{
101	ThrowIfInvalidTruncatedSize(size);
102
103	BlockCipher &cipher = AccessCipher();
104	unsigned int blockSize = cipher.BlockSize();
105
106	if (m_counter < blockSize)
107	{
108		m_reg[m_counter] ^= 0x80;
109		cipher.AdvancedProcessBlocks(m_reg, m_reg+2*blockSize, m_reg, blockSize, BlockTransformation::BT_DontIncrementInOutPointers|BlockTransformation::BT_XorInput);
110	}
111	else
112		cipher.AdvancedProcessBlocks(m_reg, m_reg+blockSize, m_reg, blockSize, BlockTransformation::BT_DontIncrementInOutPointers|BlockTransformation::BT_XorInput);
113
114	memcpy(mac, m_reg, size);
115
116	m_counter = 0;
117	memset(m_reg, 0, blockSize);
118}
119
120NAMESPACE_END
121
122#endif
123