Re: [RFC 1/2] shared/aes-cmac: Add support for AES-CMAC-128

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

On 13 May 2014 12:44, Lukasz Rymanowski <lukasz.rymanowski@xxxxxxxxx> wrote:
> This patch adds handling AES-CMAC-128 signing as specified in the NIST
> Special Publication 800-38B
> ---
>  src/shared/aes-cmac.c | 346 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  src/shared/aes-cmac.h |  38 ++++++
>  2 files changed, 384 insertions(+)
>  create mode 100644 src/shared/aes-cmac.c
>  create mode 100644 src/shared/aes-cmac.h
>
> diff --git a/src/shared/aes-cmac.c b/src/shared/aes-cmac.c
> new file mode 100644
> index 0000000..660ceff
> --- /dev/null
> +++ b/src/shared/aes-cmac.c
> @@ -0,0 +1,346 @@
> +/*
> + *
> + *  BlueZ - Bluetooth protocol stack for Linux
> + *
> + *  Copyright (C) 2014  Intel Corporation. All rights reserved.
> + *
> + *
> + *  This library is free software; you can redistribute it and/or
> + *  modify it under typedef struct {
> +       uint64_t a, b;
> +} u128;the terms of the GNU Lesser General Public
> + *  License as published by the Free Software Foundation; either
> + *  version 2.1 of the License, or (at your option) any later version.
> + *
> + *  This library is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + *  Lesser General Public License for more details.
> + *
> + *  You should have received a copy of the GNU Lesser General Public
> + *  License along with this library; if not, write to the Free Software
> + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> + *
> + */
> +
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include "src/shared/aes-cmac.h"
> +#include "src/shared/util.h"
> +#include "src/shared/crypto.h"
> +
> +#include <stdio.h>
> +#include <string.h>
> +
> +struct bt_ac {
> +       struct bt_crypto *crypto;
> +
> +       uint8_t k[16];
> +       uint8_t k1[16];
> +       uint8_t k2[16];
> +
> +       bool sign_counter;
> +       uint32_t sign_counter_val;
> +};
> +
> +static void ac_subkey_gen(const uint8_t l[16], uint8_t sub_k[16])
> +{
> +       int i;
> +
> +       memset(sub_k, 0, 16);
> +
> +       /* Left shifting */
> +       for (i = 0; i < 16; i++) {
> +               uint16_t p = l[i];
> +
> +               p <<= 1;
> +               sub_k[i] |= p & 0xff;
> +
> +               if (i == 15)
> +                       break;
> +
> +               /* Don't forget moving bit */
> +               sub_k[i + 1] = p >> 8;
> +       }
> +
> +       if (l[15] & 0x80)
> +               sub_k[0] ^= 0x87;
> +}
> +
> +/*
> + * This function generates sub keys according to AES-CMAC
> + *
> + * As an input we have k which is 128 bits
> + *
> + * There are two sub keys generated in following way:
> + *
> + * L = AES-128 (K, 0^16)
> + * for i = 1,2 {
> + *     if MSB(L)
> + *         K_i = L << 1 ^ 0x87
> + *     else
> + *         K_i = L << 1
> + *
> + *     L = K_i
> + * }
> + */
> +static bool ac_subkeys_gen(struct bt_crypto *crypto, const uint8_t k[16],
> +                                       uint8_t k1[16], uint8_t k2[16])
> +{
> +       uint8_t z[16];
> +       uint8_t l[16];
> +
> +       memset(z, 0, 16);
> +       memset(l, 0, 16);
> +
> +       /* AES encryption of 16 bytes z */
> +       if (!bt_crypto_e(crypto, k, z, l))
> +               return false;
> +
> +       /* Generate sub keys - K1, K2 */
> +       ac_subkey_gen(l, k1);
> +       ac_subkey_gen(k1, k2);
> +
> +       return true;
> +}
> +
> +struct bt_ac *bt_ac_new(const uint8_t k[16], bool sign_counter)
> +{
> +       struct bt_ac *ac;
> +       struct bt_crypto *crypto;
> +
> +       ac = new0(struct bt_ac, 1);
> +       if (!ac)
> +               return NULL;
> +
> +       crypto = bt_crypto_new();
> +       if (!crypto)
> +               goto failed;
> +
> +       if (!ac_subkeys_gen(crypto, k, ac->k1, ac->k2))
> +               goto failed;
> +
> +       memcpy(ac->k, k, 16);
> +       ac->crypto = crypto;
> +
> +       /*
> +        * Enable sign counter as specified in BT Core Spec 4.1 Vol[3], Part H
> +        * Chapter 2.4.5
> +        */
> +       ac->sign_counter = sign_counter;
> +
> +       return ac;
> +
> +failed:
> +       free(ac);
> +       return NULL;
> +}
> +
> +void bt_ac_destroy(struct bt_ac *aes_cmac)
> +{
> +       if (!aes_cmac)
> +               return;
> +
> +       bt_crypto_unref(aes_cmac->crypto);
> +
> +       free(aes_cmac);
> +}
> +
> +typedef struct {
> +       uint64_t a, b;
> +} u128;
> +
> +static inline void u128_xor(const uint8_t p[16], const uint8_t q[16],
> +                                                               uint8_t r[16])
> +{
> +       u128 pp, qq, rr;
> +
> +       memcpy(&pp, p, 16);
> +       memcpy(&qq, q, 16);
> +
> +       rr.a = pp.a ^ qq.a;
> +       rr.b = pp.b ^ qq.b;
> +
> +       memcpy(r, &rr, 16);
> +}
> +
> +static inline void swap128(const uint8_t src[16], uint8_t dst[16])
> +{
> +       int i;
> +
> +       for (i = 0; i < 16; i++)
> +               dst[15 - i] = src[i];
> +}
> +
> +static bool encode_block(struct bt_ac *aes_cmac, const uint8_t m[16],
> +                                                               uint8_t c[16])
> +{
> +       uint8_t tmp[16];
> +       uint8_t mp[16];
> +
> +       /* Swap the message so crypto will get less significant byte in m[0] */
> +       swap128(m, tmp);
> +
> +       /* Xor with previous encrypted block */
> +       u128_xor(tmp, c, mp);
> +
> +       /* AES-128 using k */
> +       if (!bt_crypto_e(aes_cmac->crypto, aes_cmac->k, mp, c))
> +               return false;
> +
> +       return true;
> +}
> +
> +static bool encode_last_block(struct bt_ac *aes_cmac, const uint8_t m[16],
> +                                               uint16_t len, uint8_t c[16])
> +{
> +       uint8_t tmp[16];
> +       uint8_t mp[16];
> +       uint8_t mpp[16];
> +       bool flag;
> +
> +       /* Flag if msg is complete */
> +       flag = (len != 16);
> +
> +       /* Swap the message so crypto will get less significant byte in m[0] */
> +       swap128(m, tmp);
> +
> +       /* Note: add padding on swapped message*/
> +       if (flag)
> +               tmp[15-len] |= 0x80;
> +
> +       /* Last block we need to xor with K1 or K2 */
> +       u128_xor(tmp, flag ? aes_cmac->k2 : aes_cmac->k1, mp);
> +
> +       /* Xor with previous encrypted block */
> +       u128_xor(mp, c, mpp);
> +
> +       /* AES-128 using k */
> +       if (!bt_crypto_e(aes_cmac->crypto, aes_cmac->k, mpp, c))
> +               return false;
> +
> +       return true;
> +}
> +
> +/*
> + * AES-CMAC-128 signing function
> + *
> + * This function is used to sign data with algorithm defined in the NIST Special
> + * Publication 800-38B
> + *
> + * The following are inputs of the signing algorithm:
> + *     m is a variable length
> + *     key k is 128 bits
> + *     sign_counter is 32 bits
> + *
> + * Before this functions is called, sub keys k1 and k2 are generated.
> + * See ac_subkeys_gen.
> + *
> + * If sing_counter has been enabled then message before will be concatenated
> + * with 32 bits sing_counter as described in BT Core Spec 4.1 Vol[3], Part H
> + * Chapter 2.4.5
> + *
> + *     M = m || sign_counter
> + *
> + * Message is build from blocks M_i:
> + *     M = M_1 || M_2 || ... || M_{n-1} || M_n
> + *
> + * Length of M_i is 128 bits for 0 < i < n and the length of last block is less
> + * than or equal to 128 bits.
> + *
> + *     If length of M_n is less than 128 then
> + *         (M_n)' = (M_n || padding) ^ K2
> + *     else
> + *         (M_n)' = M_n ^ K1
> + *
> + * Where padding is 10^i and i = 128 - 8 * len(M_n) - 1
> + *
> + * For example: M_n            = 0x30c81c46a35ce411
> + *              M_n || padding = 0x30c81c46a35ce4118000000000000000
> + *
> + * Signing procedure uses AES-CBC (Cipher block chaining mode):
> + *
> + * C = 0
> + * For each M_i
> + *     Y = C ^ M_i
> + *     C = AES-128(K,Y)
> + *
> + * Last encrypted block X, truncated to 12 octects taken in MSB order is the
> + * sign result T.
> + */
> +bool bt_ac_sign(struct bt_ac *aes_cmac, const uint8_t *m, uint16_t m_len,
> +                                                               uint8_t t[12])
> +{
> +       uint8_t sc_len = aes_cmac->sign_counter ? sizeof(uint32_t) : 0;
> +       uint16_t len = m_len + sc_len;
> +       uint8_t mp[len];
> +       uint8_t c[16];
> +       uint8_t tmp[16];
> +       int i = 0;
> +
> +       /* Handle sign_counter if necessary */
> +       memset(mp, 0, len);
> +       memcpy(mp, m, m_len);
> +
> +       if (aes_cmac->sign_counter)
> +               put_le32(aes_cmac->sign_counter_val++, mp + m_len);
> +       /*
> +        * We are doing AES-CBC. In C we will keep last encrypted block.
> +        * C(0) is just 16 bytes zeros message.
> +        */
> +       memset(c, 0, 16);
> +
> +       /* Start AES-CBC of n-1 blocks of data.*/
> +       for (i = 0; len - i > 16; i += 16) {
> +               memcpy(tmp, mp + i , 16);
> +
> +               /*
> +                * In C we provide last encrypted block and next encrypted block
> +                * will be returned
> +                */
> +               if (!encode_block(aes_cmac, tmp, c))
> +                       return false;
> +       }
> +
> +       /* Handle last block of data */
> +       memset(tmp, 0, 16);
> +       memcpy(tmp, mp + i , len - i);
> +
> +       /* Padding is handled inside this function */
> +       if (!encode_last_block(aes_cmac, tmp, len - i, c))
> +               return false;
> +
> +       /* Signature is a 12 octets of last encrypted block C(n) */
> +       memcpy(t, c + 4, 12);
> +       return true;
> +}
> +
> +static bool verify(const uint8_t t[12], const uint8_t tp[12])
> +{
> +       int i;
> +       for (i = 0; i < 12; i++)
> +               if (t[i] != tp[i])
> +                       return false;
> +
> +       return true;
> +}
> +
> +bool bt_ac_verify(struct bt_ac *aes_cmac, const uint8_t *m,
> +                                               uint16_t m_len, uint8_t t[12])
> +{
> +       uint8_t tp[12];
> +
> +       memset(tp, 0, 12);
> +
> +       if (!bt_ac_sign(aes_cmac, m, m_len, tp))
> +               return false;
> +
> +       return verify(t, tp);
> +}
> +
> +uint32_t bt_ac_get_sign_counter(struct bt_ac *aes_cmac)
> +{
> +       return aes_cmac->sign_counter_val;
> +}
> diff --git a/src/shared/aes-cmac.h b/src/shared/aes-cmac.h
> new file mode 100644
> index 0000000..62068aa
> --- /dev/null
> +++ b/src/shared/aes-cmac.h
> @@ -0,0 +1,38 @@
> +/*
> + *
> + *  BlueZ - Bluetooth protocol stack for Linux
> + *
> + *  Copyright (C) 2014  Intel Corporation. All rights reserved.
> + *
> + *
> + *  This library is free software; you can redistribute it and/or
> + *  modify it under the terms of the GNU Lesser General Public
> + *  License as published by the Free Software Foundation; either
> + *  version 2.1 of the License, or (at your option) any later version.
> + *
> + *  This library is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + *  Lesser General Public License for more details.
> + *
> + *  You should have received a copy of the GNU Lesser General Public
> + *  License along with this library; if not, write to the Free Software
> + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> + *
> + */
> +
> +#include <stdbool.h>
> +#include <stdint.h>
> +
> +struct bt_ac;
> +
> +struct bt_ac *bt_ac_new(const uint8_t k[16], bool sign_counter);
> +void bt_ac_destroy(struct bt_ac *aes_cmac);
> +
> +bool bt_ac_sign(struct bt_ac *aes_cmac, const uint8_t *m, uint16_t m_len,
> +                                                               uint8_t t[12]);
> +
> +bool bt_ac_verify(struct bt_ac *aes_cmac, const uint8_t *m, uint16_t m_len,
> +                                                               uint8_t t[12]);
> +
> +uint32_t bt_ac_get_sign_counter(struct bt_ac *aes_cmac);
This method is not needed and will be removed. Sign_counter can be
hided from the upper layer.

\Łukasz
> --
> 1.8.4
>
--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux