From 1cf965cfccfb72eff78c1846557bb8c8aaedfb99 Mon Sep 17 00:00:00 2001 From: Pascal Brand Date: Mon, 3 Nov 2014 10:25:40 +0100 Subject: [PATCH] Add doc for CCM Authentication full set of functions Change-Id: I2830ea3c04fd0410cc12137be41e6c511c4a47fe --- crypt.tex | 241 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 172 insertions(+), 69 deletions(-) diff --git a/crypt.tex b/crypt.tex index ce740a1..88d0874 100644 --- a/crypt.tex +++ b/crypt.tex @@ -1511,9 +1511,91 @@ Similarly, this will OCB decrypt, and compare the internally computed tag agains appropriately. \subsection{CCM Mode} -CCM is a NIST proposal for encrypt + authenticate that is centered around using AES (or any 16--byte cipher) as a primitive. Unlike EAX and OCB mode, -it is only meant for \textit{packet} mode where the length of the input is known in advance. Since it is a packet mode function, CCM only has one -function that performs the protocol. +CCM is a NIST proposal for encrypt + authenticate that is centered around using AES (or any 16--byte cipher) as a primitive. + +\subsubsection{Initialization} +To initialize the CCM context with a secret key call the following function. + +\index{ccm\_init()} +\begin{verbatim} +int ccm_init( ccm_state *ccm, + int cipher, + const unsigned char *key, + int keylen, + int ptlen, + int taglen, + int aadlen); +\end{verbatim} +This initializes the CCM state \textit{ccm} for the given cipher indexed by \textit{cipher}, with a secret key \textit{key} of length \textit{keylen} octets. The cipher +chosen must have a 16--byte block size (e.g., AES). +Unlike EAX and OCB mode, CCM is only meant for \textit{packet} mode where the length of the input is known in advance. This is why the length of the stream +to authenticate is given as \textit{ptlen}. +With CCM, a header is meta--data you want to send with the message but not have encrypted. The header len is given in the init +as \textit{aadlen}. + +\subsubsection{Nonce Vector} +After the state has been initialized (or reset) the next step is to add the session (or packet) initial vector. It should be unique per packet encrypted. + +\index{ccm\_add\_nonce()} +\begin{verbatim} +int ccm_add_nonce( ccm_state *ccm, + const unsigned char *nonce, + unsigned long noncelen); +\end{verbatim} + +This adds the nonce or salt is \textit{nonce} of length \textit{noncelen} octets to the CCM state \textit{ccm}. Note that this function must be called +once and only once. + +\subsubsection{Additional Authentication Data} +The header is meta--data you want to send with the message but not have encrypted, it is stored in \textit{adata} of length \textit{adatalen} octets. + +\index{ccm\_add\_aad()} +\begin{verbatim} +int ccm_add_aad( ccm_state *ccm, + const unsigned char *adata, + unsigned long adatalen); +\end{verbatim} +This adds the additional authentication data \textit{adata} of length \textit{adatalen} to the CCM state \textit{ccm}. + +\subsubsection{Plaintext Processing} +After the AAD has been processed, the plaintext (or ciphertext depending on the direction) can be processed. + +\index{ccm\_process()} +\begin{verbatim} +int ccm_process(ccm_state *ccm, + unsigned char *pt, + unsigned long ptlen, + unsigned char *ct, + int direction); +\end{verbatim} +This processes message data where \textit{pt} is the plaintext and \textit{ct} is the ciphertext. The length of both are equal and stored in \textit{ptlen}. Depending on +the mode \textit{pt} is the input and \textit{ct} is the output (or vice versa). When \textit{direction} equals \textbf{CCM\_ENCRYPT} the plaintext is read, +encrypted and stored in the ciphertext buffer. When \textit{direction} equals \textbf{CCM\_DECRYPT} the opposite occurs. + +\subsubsection{State Termination} +To terminate a CCM state and retrieve the message authentication tag call the following function. + +\index{ccm\_done()} +\begin{verbatim} +int ccm_done( ccm_state *ccm, + unsigned char *tag, + unsigned long *taglen); +\end{verbatim} +This terminates the CCM state \textit{ccm} and stores the tag in \textit{tag} of length \textit{taglen} octets. + +\subsubsection{State Reset} +The call to ccm\_init() will perform considerable pre--computation and if you're going to be dealing with a lot of packets +it is very costly to have to call it repeatedly. To aid in this endeavour, the reset function has been provided. + +\index{ccm\_reset()} +\begin{verbatim} +int ccm_reset(ccm_state *ccm); +\end{verbatim} + +This will reset the CCM state \textit{ccm} to the state that ccm\_init() left it. The user would then call ccm\_add\_nonce(), ccm\_add\_aad(), etc. + +\subsubsection{One--Shot Packet} +To process a single packet under any given key the following helper function can be used. \index{ccm\_memory()} \begin{verbatim} @@ -1529,86 +1611,107 @@ int ccm_memory( int direction); \end{verbatim} -This performs the \textit{CCM} operation on the data. The \textit{cipher} variable indicates which cipher in the descriptor table to use. It must have a -16--byte block size for CCM. +This will initialize the CCM state with the given key, nonce and AAD value then proceed to encrypt or decrypt the message text and store the final +message tag. The definition of the variables is the same as it is for all the manual functions. -The key can be specified in one of two fashions. First, it can be passed as an array of octets in \textit{key} of length \textit{keylen}. Alternatively, -it can be passed in as a previously scheduled key in \textit{uskey}. The latter fashion saves time when the same key is used for multiple packets. If -\textit{uskey} is not \textbf{NULL}, then \textit{key} may be \textbf{NULL} (and vice-versa). +If you are processing many packets under the same key you shouldn't use this function as it invokes the pre--computation with each call. -The nonce or salt is \textit{nonce} of length \textit{noncelen} octets. The header is meta--data you want to send with the message but not have -encrypted, it is stored in \textit{header} of length \textit{headerlen} octets. The header can be zero octets long (if $headerlen = 0$ then -you can pass \textit{header} as \textbf{NULL}). - -The plaintext is stored in \textit{pt}, and the ciphertext in \textit{ct}. The length of both are expected to be equal and is passed in as \textit{ptlen}. It is -allowable that $pt = ct$. The \textit{direction} variable indicates whether encryption (direction $=$ \textbf{CCM\_ENCRYPT}) or -decryption (direction $=$ \textbf{CCM\_DECRYPT}) is to be performed. - -As implemented, this version of CCM cannot handle header or plaintext data longer than $2^{32} - 1$ octets long. - -You can test the implementation of CCM with the following function. - -\index{ccm\_test()} -\begin{verbatim} -int ccm_test(void); -\end{verbatim} - -This will return \textbf{CRYPT\_OK} if the CCM routine passes known test vectors. It requires AES or Rijndael to be registered previously, otherwise it will -return \textbf{CRYPT\_NOP}. - -\subsubsection{CCM Example} -The following is a sample of how to call CCM. +\subsubsection{Example Usage} +The following is an example usage of how to use CCM over multiple packets with a shared secret key. \begin{small} \begin{verbatim} #include + +int send_packet(const unsigned char *pt, unsigned long ptlen, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *aad, unsigned long aadlen, + ccm_state *ccm) +{ + int err; + unsigned long taglen; + unsigned char tag[16]; + + /* reset the state */ + if ((err = ccm_reset(ccm)) != CRYPT_OK) { + return err; + } + + /* Add the nonce */ + if ((err = ccm_add_nonce(ccm, nonce, noncelen)) != CRYPT_OK) { + return err; + } + + /* Add the AAD (note: aad can be NULL if aadlen == 0) */ + if ((err = ccm_add_aad(ccm, aad, aadlen)) != CRYPT_OK) { + return err; + } + + /* process the plaintext */ + if ((err = + ccm_process(ccm, pt, ptlen, pt, CCM_ENCRYPT)) != CRYPT_OK) { + return err; + } + + /* Finish up and get the MAC tag */ + taglen = sizeof(tag); + if ((err = ccm_done(ccm, tag, &taglen)) != CRYPT_OK) { + return err; + } + + /* ... send a header describing the lengths ... */ + + /* depending on the protocol and how nonce is + * generated you may have to send it too... */ + send(socket, nonce, noncelen, 0); + + /* send the aad */ + send(socket, aad, aadlen, 0); + + /* send the ciphertext */ + send(socket, pt, ptlen, 0); + + /* send the tag */ + send(socket, tag, taglen, 0); + + return CRYPT_OK; +} + int main(void) { - unsigned char key[16], nonce[12], pt[32], ct[32], - tag[16], tagcp[16]; - unsigned long taglen; - int err; + ccm_state ccm; + unsigned char key[16], NONCE[12], pt[PACKET_SIZE]; + int err, x; + unsigned long ptlen; - /* register cipher */ + /* somehow fill key/NONCE with random values */ + + /* register AES */ register_cipher(&aes_desc); - /* somehow fill key, nonce, pt */ - - /* encrypt it */ - taglen = sizeof(tag); + /* init the CCM state */ if ((err = - ccm_memory(find_cipher("aes"), - key, 16, /* 128-bit key */ - NULL, /* not prescheduled */ - nonce, 12, /* 96-bit nonce */ - NULL, 0, /* no header */ - pt, 32, /* [in] 32-byte plaintext */ - ct, /* [out] ciphertext */ - tag, &taglen, - CCM_ENCRYPT)) != CRYPT_OK) { - printf("ccm_memory error %s\n", error_to_string(err)); - return -1; - } - /* ct[0..31] and tag[0..15] now hold the output */ - - /* decrypt it */ - taglen = sizeof(tagcp); - if ((err = - ccm_memory(find_cipher("aes"), - key, 16, /* 128-bit key */ - NULL, /* not prescheduled */ - nonce, 12, /* 96-bit nonce */ - NULL, 0, /* no header */ - pt, 32, /* [out] 32-byte plaintext */ - ct, /* [in] ciphertext */ - tagcp, &taglen, - CCM_DECRYPT)) != CRYPT_OK) { - printf("ccm_memory error %s\n", error_to_string(err)); - return -1; + ccm_init(&ccm, find_cipher("aes"), key, 16, PACKET_SIZE, 16, size(NONCE))) != CRYPT_OK) { + whine_and_pout(err); } - /* now pt[0..31] should hold the original plaintext, - tagcp[0..15] and tag[0..15] should have the same contents */ + /* handle us some packets */ + for (;;) { + ptlen = make_packet_we_want_to_send(pt); + + /* use NONCE as counter (12 byte counter) */ + for (x = 11; x >= 0; x--) { + if (++NONCE[x]) { + break; + } + } + + if ((err = send_packet(pt, ptlen, NONCE, 12, NULL, 0, &ccm)) + != CRYPT_OK) { + whine_and_pout(err); + } + } + return EXIT_SUCCESS; } \end{verbatim} \end{small}