1// ccm.cpp - written and placed in the public domain by Wei Dai
2
3#include "pch.h"
4
5#ifndef CRYPTOPP_IMPORTS
6
7#include "ccm.h"
8
9NAMESPACE_BEGIN(CryptoPP)
10
11void CCM_Base::SetKeyWithoutResync(const byte *userKey, size_t keylength, const NameValuePairs &params)
12{
13	BlockCipher &blockCipher = AccessBlockCipher();
14
15	blockCipher.SetKey(userKey, keylength, params);
16
17	if (blockCipher.BlockSize() != REQUIRED_BLOCKSIZE)
18		throw InvalidArgument(AlgorithmName() + ": block size of underlying block cipher is not 16");
19
20	m_digestSize = params.GetIntValueWithDefault(Name::DigestSize(), DefaultDigestSize());
21	if (m_digestSize % 2 > 0 || m_digestSize < 4 || m_digestSize > 16)
22		throw InvalidArgument(AlgorithmName() + ": DigestSize must be 4, 6, 8, 10, 12, 14, or 16");
23
24	m_buffer.Grow(2*REQUIRED_BLOCKSIZE);
25	m_L = 8;
26}
27
28void CCM_Base::Resync(const byte *iv, size_t len)
29{
30	BlockCipher &cipher = AccessBlockCipher();
31
32	m_L = REQUIRED_BLOCKSIZE-1-(int)len;
33	assert(m_L >= 2);
34	if (m_L > 8)
35		m_L = 8;
36
37	m_buffer[0] = byte(m_L-1);	// flag
38	memcpy(m_buffer+1, iv, len);
39	memset(m_buffer+1+len, 0, REQUIRED_BLOCKSIZE-1-len);
40
41	if (m_state >= State_IVSet)
42		m_ctr.Resynchronize(m_buffer, REQUIRED_BLOCKSIZE);
43	else
44		m_ctr.SetCipherWithIV(cipher, m_buffer);
45
46	m_ctr.Seek(REQUIRED_BLOCKSIZE);
47	m_aadLength = 0;
48	m_messageLength = 0;
49}
50
51void CCM_Base::UncheckedSpecifyDataLengths(lword headerLength, lword messageLength, lword footerLength)
52{
53	if (m_state != State_IVSet)
54		throw BadState(AlgorithmName(), "SpecifyDataLengths", "or after State_IVSet");
55
56	m_aadLength = headerLength;
57	m_messageLength = messageLength;
58
59	byte *cbcBuffer = CBC_Buffer();
60	const BlockCipher &cipher = GetBlockCipher();
61
62	cbcBuffer[0] = byte(64*(headerLength>0) + 8*((m_digestSize-2)/2) + (m_L-1));	// flag
63	PutWord<word64>(true, BIG_ENDIAN_ORDER, cbcBuffer+REQUIRED_BLOCKSIZE-8, m_messageLength);
64	memcpy(cbcBuffer+1, m_buffer+1, REQUIRED_BLOCKSIZE-1-m_L);
65	cipher.ProcessBlock(cbcBuffer);
66
67	if (headerLength>0)
68	{
69		assert(m_bufferedDataLength == 0);
70
71		if (headerLength < ((1<<16) - (1<<8)))
72		{
73			PutWord<word16>(true, BIG_ENDIAN_ORDER, m_buffer, (word16)headerLength);
74			m_bufferedDataLength = 2;
75		}
76		else if (headerLength < (W64LIT(1)<<32))
77		{
78			m_buffer[0] = 0xff;
79			m_buffer[1] = 0xfe;
80			PutWord<word32>(false, BIG_ENDIAN_ORDER, m_buffer+2, (word32)headerLength);
81			m_bufferedDataLength = 6;
82		}
83		else
84		{
85			m_buffer[0] = 0xff;
86			m_buffer[1] = 0xff;
87			PutWord<word64>(false, BIG_ENDIAN_ORDER, m_buffer+2, headerLength);
88			m_bufferedDataLength = 10;
89		}
90	}
91}
92
93size_t CCM_Base::AuthenticateBlocks(const byte *data, size_t len)
94{
95	byte *cbcBuffer = CBC_Buffer();
96	const BlockCipher &cipher = GetBlockCipher();
97	return cipher.AdvancedProcessBlocks(cbcBuffer, data, cbcBuffer, len, BlockTransformation::BT_DontIncrementInOutPointers|BlockTransformation::BT_XorInput);
98}
99
100void CCM_Base::AuthenticateLastHeaderBlock()
101{
102	byte *cbcBuffer = CBC_Buffer();
103	const BlockCipher &cipher = GetBlockCipher();
104
105	if (m_aadLength != m_totalHeaderLength)
106		throw InvalidArgument(AlgorithmName() + ": header length doesn't match that given in SpecifyDataLengths");
107
108	if (m_bufferedDataLength > 0)
109	{
110		xorbuf(cbcBuffer, m_buffer, m_bufferedDataLength);
111		cipher.ProcessBlock(cbcBuffer);
112		m_bufferedDataLength = 0;
113	}
114}
115
116void CCM_Base::AuthenticateLastConfidentialBlock()
117{
118	byte *cbcBuffer = CBC_Buffer();
119	const BlockCipher &cipher = GetBlockCipher();
120
121	if (m_messageLength != m_totalMessageLength)
122		throw InvalidArgument(AlgorithmName() + ": message length doesn't match that given in SpecifyDataLengths");
123
124	if (m_bufferedDataLength > 0)
125	{
126		xorbuf(cbcBuffer, m_buffer, m_bufferedDataLength);
127		cipher.ProcessBlock(cbcBuffer);
128		m_bufferedDataLength = 0;
129	}
130}
131
132void CCM_Base::AuthenticateLastFooterBlock(byte *mac, size_t macSize)
133{
134	m_ctr.Seek(0);
135	m_ctr.ProcessData(mac, CBC_Buffer(), macSize);
136}
137
138NAMESPACE_END
139
140#endif
141