add aes ccm support

This commit is contained in:
Baojun Wang 2017-05-31 19:33:48 -07:00 committed by Olivier Chéron
parent 28f604f7bd
commit 55bf620365
6 changed files with 356 additions and 1 deletions

View File

@ -59,6 +59,7 @@ instance BlockCipher CSTR where \
; ctrCombine (CSTR aes) (IV iv) = encryptCTR aes (IV iv) \
; aeadInit AEAD_GCM (CSTR aes) iv = CryptoPassed $ AEAD (gcmMode aes) (gcmInit aes iv) \
; aeadInit AEAD_OCB (CSTR aes) iv = CryptoPassed $ AEAD (ocbMode aes) (ocbInit aes iv) \
; aeadInit (AEAD_CCM n m l) (CSTR aes) iv = CryptoPassed $ AEAD (ccmMode aes) (ccmInit aes iv n m l) \
; aeadInit _ _ _ = CryptoFailed CryptoError_AEADModeNotSupported \
}; \
instance BlockCipher128 CSTR where \

View File

@ -44,6 +44,10 @@ module Crypto.Cipher.AES.Primitive
-- * Incremental OCB
, ocbMode
, ocbInit
-- * CCM
, ccmMode
, ccmInit
) where
import Data.Word
@ -73,6 +77,7 @@ instance BlockCipher AES where
ctrCombine = encryptCTR
aeadInit AEAD_GCM aes iv = CryptoPassed $ AEAD (gcmMode aes) (gcmInit aes iv)
aeadInit AEAD_OCB aes iv = CryptoPassed $ AEAD (ocbMode aes) (ocbInit aes iv)
aeadInit (AEAD_CCM n m l) aes iv = CryptoPassed $ AEAD (ccmMode aes) (ccmInit aes iv n m l)
aeadInit _ _ _ = CryptoFailed CryptoError_AEADModeNotSupported
instance BlockCipher128 AES where
xtsEncrypt = encryptXTS
@ -96,6 +101,14 @@ ocbMode aes = AEADModeImpl
, aeadImplFinalize = ocbFinish aes
}
-- | Create an AES AEAD implementation for GCM
ccmMode :: AES -> AEADModeImpl AESCCM
ccmMode aes = AEADModeImpl
{ aeadImplAppendHeader = ccmAppendAAD
, aeadImplEncrypt = ccmEncrypt aes
, aeadImplDecrypt = ccmDecrypt aes
, aeadImplFinalize = ccmFinish aes
}
-- | AES Context (pre-processed key)
newtype AES = AES ScrubbedBytes
@ -109,12 +122,19 @@ newtype AESGCM = AESGCM ScrubbedBytes
newtype AESOCB = AESOCB ScrubbedBytes
deriving (NFData)
-- | AESCCM State
newtype AESCCM = AESCCM ScrubbedBytes
deriving (NFData)
sizeGCM :: Int
sizeGCM = 80
sizeOCB :: Int
sizeOCB = 160
sizeCCM :: Int
sizeCCM = 544
keyToPtr :: AES -> (Ptr AES -> IO a) -> IO a
keyToPtr (AES b) f = withByteArray b (f . castPtr)
@ -152,6 +172,16 @@ withOCBKeyAndCopySt aes (AESOCB gcmSt) f =
a <- withByteArray newSt $ \gcmStPtr -> f (castPtr gcmStPtr) aesPtr
return (a, AESOCB newSt)
withCCMKeyAndCopySt :: AES -> AESCCM -> (Ptr AESCCM -> Ptr AES -> IO a) -> IO (a, AESCCM)
withCCMKeyAndCopySt aes (AESCCM ccmSt) f =
keyToPtr aes $ \aesPtr -> do
newSt <- B.copy ccmSt (\_ -> return ())
a <- withByteArray newSt $ \ccmStPtr -> f (castPtr ccmStPtr) aesPtr
return (a, AESCCM newSt)
withNewCCMSt :: AESCCM -> (Ptr AESCCM -> IO ()) -> IO AESCCM
withNewCCMSt (AESCCM ccmSt) f = B.copy ccmSt (f . castPtr) >>= \sm2 -> return (AESCCM sm2)
-- | Initialize a new context with a key
--
-- Key needs to be of length 16, 24 or 32 bytes. Any other values will return failure
@ -447,6 +477,78 @@ ocbFinish ctx ocb taglen = AuthTag $ B.take taglen computeTag
where computeTag = B.allocAndFreeze 16 $ \t ->
withOCBKeyAndCopySt ctx ocb (c_aes_ocb_finish (castPtr t)) >> return ()
ccmGetM :: CCM_M -> Int
ccmGetL :: CCM_L -> Int
ccmGetM m = case m of
CCM_M4 -> 4
CCM_M6 -> 6
CCM_M8 -> 8
CCM_M10 -> 10
CCM_M12 -> 12
CCM_M14 -> 14
CCM_M16 -> 16
ccmGetL l = case l of
CCM_L2 -> 2
CCM_L3 -> 3
CCM_L4 -> 4
-- | initialize a ccm context
{-# NOINLINE ccmInit #-}
ccmInit :: ByteArrayAccess iv => AES -> iv -> Int -> CCM_M -> CCM_L -> AESCCM
ccmInit ctx iv n m l = unsafeDoIO $ do
sm <- B.alloc sizeCCM $ \ccmStPtr ->
withKeyAndIV ctx iv $ \k v ->
c_aes_ccm_init (castPtr ccmStPtr) k v (fromIntegral $ B.length iv) (fromIntegral n) (fromIntegral (ccmGetM m)) (fromIntegral (ccmGetL l))
return $ AESCCM sm
-- | append data which is only going to be authenticated to the CCM context.
--
-- needs to happen after initialization and before appending encryption/decryption data.
{-# NOINLINE ccmAppendAAD #-}
ccmAppendAAD :: ByteArrayAccess aad => AESCCM -> aad -> AESCCM
ccmAppendAAD ccmSt input = unsafeDoIO doAppend
where doAppend =
withNewCCMSt ccmSt $ \ccmStPtr ->
withByteArray input $ \i ->
c_aes_ccm_aad ccmStPtr i (fromIntegral $ B.length input)
doCTR :: (ByteArray ba, BlockCipher cipher) => cipher -> ba -> ba -> ba
doCTR ctx iv0 input = ctrCombine ctx (ivAdd (IV (B.convert iv0 :: B.Bytes)) 1) input
-- | append data to encrypt and append to the CCM context
--
-- the bytearray needs to be a multiple of AES block size, unless it's the last call to this function.
-- needs to happen after AAD appending, or after initialization if no AAD data.
{-# NOINLINE ccmEncrypt #-}
ccmEncrypt :: ByteArray ba => AES -> AESCCM -> ba -> (ba, AESCCM)
ccmEncrypt ctx ccm input = unsafeDoIO $ (withCCMKeyAndCopySt ctx ccm cbcmacAndIv >>= \(iv0, cc) -> return (doCTR ctx iv0 input, cc))
where len = B.length input
cbcmacAndIv ccmStPtr aesPtr =
B.alloc 16 $ \o ->
withByteArray input $ \i ->
c_aes_ccm_encrypt (castPtr o) ccmStPtr aesPtr i (fromIntegral len)
-- | append data to decrypt and append to the CCM context
--
-- the bytearray needs to be a multiple of AES block size, unless it's the last call to this function.
-- needs to happen after AAD appending, or after initialization if no AAD data.
{-# NOINLINE ccmDecrypt #-}
ccmDecrypt :: ByteArray ba => AES -> AESCCM -> ba -> (ba, AESCCM)
ccmDecrypt ctx ccm input = unsafeDoIO $ withCCMKeyAndCopySt ctx ccm doDec
where len = B.length input
doDec ccmStPtr aesPtr =
B.alloc len $ \o ->
withByteArray input $ \i ->
c_aes_ccm_decrypt (castPtr o) ccmStPtr aesPtr i (fromIntegral len)
-- | Generate the Tag from CCM context
{-# NOINLINE ccmFinish #-}
ccmFinish :: AES -> AESCCM -> Int -> AuthTag
ccmFinish ctx ccm taglen = AuthTag $ B.take taglen computeTag
where computeTag = B.allocAndFreeze 16 $ \t ->
withCCMKeyAndCopySt ctx ccm (c_aes_ccm_finish (castPtr t)) >> return ()
------------------------------------------------------------------------
foreign import ccall "cryptonite_aes.h cryptonite_aes_initkey"
c_aes_init :: Ptr AES -> CString -> CUInt -> IO ()
@ -508,3 +610,17 @@ foreign import ccall "cryptonite_aes.h cryptonite_aes_ocb_decrypt"
foreign import ccall "cryptonite_aes.h cryptonite_aes_ocb_finish"
c_aes_ocb_finish :: CString -> Ptr AESOCB -> Ptr AES -> IO ()
foreign import ccall "cryptonite_aes.h cryptonite_aes_ccm_init"
c_aes_ccm_init :: Ptr AESCCM -> Ptr AES -> Ptr Word8 -> CUInt -> CULong -> CInt -> CInt -> IO ()
foreign import ccall "cryptonite_aes.h cryptonite_aes_ccm_aad"
c_aes_ccm_aad :: Ptr AESCCM -> CString -> CUInt -> IO ()
foreign import ccall "cryptonite_aes.h cryptonite_aes_ccm_encrypt"
c_aes_ccm_encrypt :: CString -> Ptr AESCCM -> Ptr AES -> CString -> CUInt -> IO ()
foreign import ccall "cryptonite_aes.h cryptonite_aes_ccm_decrypt"
c_aes_ccm_decrypt :: CString -> Ptr AESCCM -> Ptr AES -> CString -> CUInt -> IO ()
foreign import ccall "cryptonite_aes.h cryptonite_aes_ccm_finish"
c_aes_ccm_finish :: CString -> Ptr AESCCM -> Ptr AES -> IO ()

View File

@ -21,6 +21,8 @@ module Crypto.Cipher.Types
-- , cfb8Decrypt
-- * AEAD functions
, AEADMode(..)
, CCM_M(..)
, CCM_L(..)
, module Crypto.Cipher.Types.AEAD
-- * Initial Vector type and constructor
, IV

View File

@ -14,6 +14,8 @@ module Crypto.Cipher.Types.Base
, Cipher(..)
, AuthTag(..)
, AEADMode(..)
, CCM_M(..)
, CCM_L(..)
, DataUnitOffset
) where
@ -39,10 +41,13 @@ newtype AuthTag = AuthTag { unAuthTag :: Bytes }
instance Eq AuthTag where
(AuthTag a) == (AuthTag b) = B.constEq a b
data CCM_M = CCM_M4 | CCM_M6 | CCM_M8 | CCM_M10 | CCM_M12 | CCM_M14 | CCM_M16 deriving (Show, Eq)
data CCM_L = CCM_L2 | CCM_L3 | CCM_L4 deriving (Show, Eq)
-- | AEAD Mode
data AEADMode =
AEAD_OCB -- OCB3
| AEAD_CCM
| AEAD_CCM Int CCM_M CCM_L
| AEAD_EAX
| AEAD_CWC
| AEAD_GCM

View File

@ -52,6 +52,8 @@ void cryptonite_aes_generic_gcm_encrypt(uint8_t *output, aes_gcm *gcm, aes_key *
void cryptonite_aes_generic_gcm_decrypt(uint8_t *output, aes_gcm *gcm, aes_key *key, uint8_t *input, uint32_t length);
void cryptonite_aes_generic_ocb_encrypt(uint8_t *output, aes_ocb *ocb, aes_key *key, uint8_t *input, uint32_t length);
void cryptonite_aes_generic_ocb_decrypt(uint8_t *output, aes_ocb *ocb, aes_key *key, uint8_t *input, uint32_t length);
void cryptonite_aes_generic_ccm_encrypt(uint8_t *output, aes_ccm *ccm, aes_key *key, uint8_t *input, uint32_t length);
void cryptonite_aes_generic_ccm_decrypt(uint8_t *output, aes_ccm *ccm, aes_key *key, uint8_t *input, uint32_t length);
enum {
/* init */
@ -76,6 +78,9 @@ enum {
/* ocb */
ENCRYPT_OCB_128, ENCRYPT_OCB_192, ENCRYPT_OCB_256,
DECRYPT_OCB_128, DECRYPT_OCB_192, DECRYPT_OCB_256,
/* ccm */
ENCRYPT_CCM_128, ENCRYPT_CCM_192, ENCRYPT_CCM_256,
DECRYPT_CCM_128, DECRYPT_CCM_192, DECRYPT_CCM_256,
};
void *cryptonite_aes_branch_table[] = {
@ -129,6 +134,13 @@ void *cryptonite_aes_branch_table[] = {
[DECRYPT_OCB_128] = cryptonite_aes_generic_ocb_decrypt,
[DECRYPT_OCB_192] = cryptonite_aes_generic_ocb_decrypt,
[DECRYPT_OCB_256] = cryptonite_aes_generic_ocb_decrypt,
/* CCM */
[ENCRYPT_CCM_128] = cryptonite_aes_generic_ccm_encrypt,
[ENCRYPT_CCM_192] = cryptonite_aes_generic_ccm_encrypt,
[ENCRYPT_CCM_256] = cryptonite_aes_generic_ccm_encrypt,
[DECRYPT_CCM_128] = cryptonite_aes_generic_ccm_decrypt,
[DECRYPT_CCM_192] = cryptonite_aes_generic_ccm_decrypt,
[DECRYPT_CCM_256] = cryptonite_aes_generic_ccm_decrypt,
};
typedef void (*init_f)(aes_key *, uint8_t *, uint8_t);
@ -138,6 +150,7 @@ typedef void (*ctr_f)(uint8_t *output, aes_key *key, aes_block *iv, uint8_t *inp
typedef void (*xts_f)(aes_block *output, aes_key *k1, aes_key *k2, aes_block *dataunit, uint32_t spoint, aes_block *input, uint32_t nb_blocks);
typedef void (*gcm_crypt_f)(uint8_t *output, aes_gcm *gcm, aes_key *key, uint8_t *input, uint32_t length);
typedef void (*ocb_crypt_f)(uint8_t *output, aes_ocb *ocb, aes_key *key, uint8_t *input, uint32_t length);
typedef void (*ccm_crypt_f)(uint8_t *output, aes_ccm *ccm, aes_key *key, uint8_t *input, uint32_t length);
typedef void (*block_f)(aes_block *output, aes_key *key, aes_block *input);
#ifdef WITH_AESNI
@ -165,6 +178,10 @@ typedef void (*block_f)(aes_block *output, aes_key *key, aes_block *input);
((ocb_crypt_f) (cryptonite_aes_branch_table[ENCRYPT_OCB_128 + strength]))
#define GET_OCB_DECRYPT(strength) \
((ocb_crypt_f) (cryptonite_aes_branch_table[DECRYPT_OCB_128 + strength]))
#define GET_CCM_ENCRYPT(strength) \
((ccm_crypt_f) (cryptonite_aes_branch_table[ENCRYPT_CCM_128 + strength]))
#define GET_CCM_DECRYPT(strength) \
((ccm_crypt_f) (cryptonite_aes_branch_table[DECRYPT_CCM_128 + strength]))
#define cryptonite_aes_encrypt_block(o,k,i) \
(((block_f) (cryptonite_aes_branch_table[ENCRYPT_BLOCK_128 + k->strength]))(o,k,i))
#define cryptonite_aes_decrypt_block(o,k,i) \
@ -182,6 +199,8 @@ typedef void (*block_f)(aes_block *output, aes_key *key, aes_block *input);
#define GET_GCM_DECRYPT(strength) cryptonite_aes_generic_gcm_decrypt
#define GET_OCB_ENCRYPT(strength) cryptonite_aes_generic_ocb_encrypt
#define GET_OCB_DECRYPT(strength) cryptonite_aes_generic_ocb_decrypt
#define GET_CCM_ENCRYPT(strength) cryptonite_aes_generic_ccm_encrypt
#define GET_CCM_DECRYPT(strength) cryptonite_aes_generic_ccm_decrypt
#define cryptonite_aes_encrypt_block(o,k,i) cryptonite_aes_generic_encrypt_block(o,k,i)
#define cryptonite_aes_decrypt_block(o,k,i) cryptonite_aes_generic_decrypt_block(o,k,i)
#endif
@ -321,6 +340,18 @@ void cryptonite_aes_gcm_decrypt(uint8_t *output, aes_gcm *gcm, aes_key *key, uin
d(output, gcm, key, input, length);
}
void cryptonite_aes_ccm_encrypt(uint8_t *output, aes_ccm *ccm, aes_key *key, uint8_t *input, uint32_t length)
{
ccm_crypt_f e = GET_CCM_ENCRYPT(key->strength);
e(output, ccm, key, input, length);
}
void cryptonite_aes_ccm_decrypt(uint8_t *output, aes_ccm *ccm, aes_key *key, uint8_t *input, uint32_t length)
{
ccm_crypt_f d = GET_CCM_DECRYPT(key->strength);
d(output, ccm, key, input, length);
}
void cryptonite_aes_ocb_encrypt(uint8_t *output, aes_ocb *ocb, aes_key *key, uint8_t *input, uint32_t length)
{
ocb_crypt_f e = GET_OCB_ENCRYPT(key->strength);
@ -406,6 +437,156 @@ void cryptonite_aes_gcm_finish(uint8_t *tag, aes_gcm *gcm, aes_key *key)
}
}
static inline int ccm_b0_flags(int has_adata, int m, int l)
{
return 8*m + l + (has_adata? 64: 0);
}
/* depends on input size */
static void ccm_encode_b0(block128* output, aes_ccm* ccm, int has_adata)
{
int last = 15;
int m = ccm->length_M;
int l = ccm->length_L;
uint64_t msg_len = ccm->length_input;
block128_zero(output);
block128_copy(output, &ccm->nonce);
output->b[0] = ccm_b0_flags(has_adata, (m-2)/2, l-1);
while (msg_len > 0) {
output->b[last--] = msg_len & 0xff;
msg_len >>= 8;
}
}
/* encode adata length */
static int ccm_encode_la(block128* output, uint64_t la)
{
if (la < ( (1 << 16) - (1 << 8)) ) {
output->b[0] = (la >> 8) & 0xff;
output->b[1] = la & 0xff;
return 2;
} else if (la < (1ull << 32)) {
output->b[0] = 0xff;
output->b[1] = 0xfe;
output->b[2] = (la >> 24) & 0xff;
output->b[3] = (la >> 16) & 0xff;
output->b[4] = (la >> 8) & 0xff;
output->b[5] = la & 0xff;
return 6;
} else {
output->b[0] = 0xff;
output->b[1] = 0xff;
output->b[2] = (la >> 56) & 0xff;
output->b[3] = (la >> 48) & 0xff;
output->b[4] = (la >> 40) & 0xff;
output->b[5] = (la >> 32) & 0xff;
output->b[6] = (la >> 24) & 0xff;
output->b[7] = (la >> 16) & 0xff;
output->b[8] = (la >> 8) & 0xff;
output->b[9] = la & 0xff;
return 10;
}
}
static void ccm_encode_ctr(block128* out, aes_ccm* ccm, unsigned int cnt)
{
int last = 15;
block128_copy(out, &ccm->nonce);
out->b[0] = ccm->length_L - 1;
while (cnt > 0) {
out->b[last--] = cnt & 0xff;
cnt >>= 8;
}
}
static void ccm_cbcmac_add(aes_ccm* ccm, aes_key* key, block128* bi)
{
block128_xor(&ccm->xi, bi);
cryptonite_aes_generic_encrypt_block(&ccm->xi, key, &ccm->xi);
}
/* even though it is possible to support message size as large as 2^64, we support up to 2^32 only */
void cryptonite_aes_ccm_init(aes_ccm *ccm, aes_key *key, uint8_t *nonce, uint32_t nonce_len, uint64_t input_size, int m, int l)
{
memset(ccm, 0, sizeof(aes_ccm));
if (l < 2 || l > 4) return;
if (m != 4 && m != 6 && m != 8 && m != 10
&& m != 12 && m != 14 && m != 16) return;
if (nonce_len != 15 - l) {
nonce_len = 15 - l;
}
if (l <= 4) {
if (input_size >= (1ull << (8*l))) return;
}
ccm->length_L = l;
ccm->length_M = m;
ccm->length_input = input_size;
memcpy(&ccm->nonce.b[1], nonce, 15 - l);
memcpy(&ccm->aad_key, key, sizeof(aes_key));
ccm_encode_b0(&ccm->b0, ccm, 1); /* assume aad is present */
ccm_encode_ctr(&ccm->iv, ccm, 0);
cryptonite_aes_encrypt_block(&ccm->xi, key, &ccm->b0);
}
/* even though l(a) can be as large as 2^64, we only handle aad up to 2 ^ 32 for practical reasons.
Also we don't support incremental aad add, because the 1st encoded adata has length information
*/
void cryptonite_aes_ccm_aad(aes_ccm *ccm, uint8_t *input, uint32_t length)
{
block128 tmp;
aes_key* key = &ccm->aad_key;
ccm->length_aad = length;
int len_len;
block128_zero(&tmp);
len_len = ccm_encode_la(&tmp, length);
if (length < 16 - len_len) {
memcpy(&tmp.b[len_len], input, length);
length = 0;
} else {
memcpy(&tmp.b[len_len], input, 16 - len_len);
input += 16 - len_len;
length -= 16 - len_len;
}
ccm_cbcmac_add(ccm, key, &tmp);
for (; length >= 16; input += 16, length -= 16) {
block128_copy(&tmp, (block128*)input);
ccm_cbcmac_add(ccm, key, &tmp);
}
if (length > 0) {
block128_zero(&tmp);
block128_copy_bytes(&tmp, input, length);
ccm_cbcmac_add(ccm, key, &tmp);
}
memset(&ccm->aad_key, 0, sizeof(aes_key));
}
void cryptonite_aes_ccm_finish(uint8_t *tag, aes_ccm *ccm, aes_key *key)
{
block128 iv, s0;
block128 u;
ccm_encode_ctr(&iv, ccm, 0);
cryptonite_aes_encrypt_block(&s0, key, &iv);
block128_vxor(&u, &ccm->xi, &s0);
memcpy(tag, u.b, ccm->length_M);
}
static inline void ocb_block_double(block128 *d, block128 *s)
{
unsigned int i;
@ -739,6 +920,37 @@ static void ocb_generic_crypt(uint8_t *output, aes_ocb *ocb, aes_key *key,
}
}
void cryptonite_aes_generic_ccm_encrypt(uint8_t *output, aes_ccm *ccm, aes_key *key, uint8_t *input, uint32_t length)
{
block128 tmp;
/* when aad is absent, reset b0 block */
if (ccm->length_aad == 0) {
ccm_encode_b0(&ccm->b0, ccm, 0); /* assume aad is present */
cryptonite_aes_encrypt_block(&ccm->xi, key, &ccm->b0);
}
if (length != ccm->length_input) {
return;
}
for (;length >= 16; input += 16, length -= 16) {
block128_copy(&tmp, (block128*)input);
ccm_cbcmac_add(ccm, key, &tmp);
}
if (length > 0) {
block128_zero(&tmp);
block128_copy_bytes(&tmp, input, length);
ccm_cbcmac_add(ccm, key, &tmp);
}
block128_copy((block128*)output, &ccm->iv);
}
void cryptonite_aes_generic_ccm_decrypt(uint8_t *output, aes_ccm *ccm, aes_key *key, uint8_t *input, uint32_t length)
{
cryptonite_aes_generic_ccm_encrypt(output, ccm, key, input, length);
}
void cryptonite_aes_generic_ocb_encrypt(uint8_t *output, aes_ocb *ocb, aes_key *key, uint8_t *input, uint32_t length)
{
ocb_generic_crypt(output, ocb, key, input, length, 1);

View File

@ -55,6 +55,19 @@ typedef struct {
uint64_t length_input;
} aes_gcm;
/* size = 544 */
typedef struct {
aes_block iv; /* iv with counter = 0 block */
aes_block xi; /* X_i: cbc mac */
aes_block b0; /* block b0 */
aes_block nonce;
aes_key aad_key;
uint64_t length_aad;
uint64_t length_input;
int length_M;
int length_L;
} aes_ccm;
typedef struct {
block128 offset_aad;
block128 offset_enc;
@ -97,4 +110,10 @@ void cryptonite_aes_ocb_encrypt(uint8_t *output, aes_ocb *ocb, aes_key *key, uin
void cryptonite_aes_ocb_decrypt(uint8_t *output, aes_ocb *ocb, aes_key *key, uint8_t *input, uint32_t length);
void cryptonite_aes_ocb_finish(uint8_t *tag, aes_ocb *ocb, aes_key *key);
void cryptonite_aes_ccm_init(aes_ccm *ccm, aes_key *key, uint8_t *nonce, uint32_t len, uint64_t msg_size, int m, int l);
void cryptonite_aes_ccm_aad(aes_ccm *ccm, uint8_t *input, uint32_t length);
void cryptonite_aes_ccm_encrypt(uint8_t *output, aes_ccm *ccm, aes_key *key, uint8_t *input, uint32_t length);
void cryptonite_aes_ccm_decrypt(uint8_t *output, aes_ccm *ccm, aes_key *key, uint8_t *input, uint32_t length);
void cryptonite_aes_ccm_finish(uint8_t *tag, aes_ccm *ccm, aes_key *key);
#endif