diff options
Diffstat (limited to 'drivers/usb/wusbcore/crypto.c')
-rw-r--r-- | drivers/usb/wusbcore/crypto.c | 169 |
1 files changed, 44 insertions, 125 deletions
diff --git a/drivers/usb/wusbcore/crypto.c b/drivers/usb/wusbcore/crypto.c index edb7263bff40..9ee66483ee54 100644 --- a/drivers/usb/wusbcore/crypto.c +++ b/drivers/usb/wusbcore/crypto.c @@ -31,6 +31,9 @@ * funneled through AES are...16 bytes in size! */ +#include <crypto/aes.h> +#include <crypto/algapi.h> +#include <crypto/hash.h> #include <crypto/skcipher.h> #include <linux/crypto.h> #include <linux/module.h> @@ -109,16 +112,6 @@ struct aes_ccm_a { __be16 counter; /* Value of x */ } __attribute__((packed)); -static void bytewise_xor(void *_bo, const void *_bi1, const void *_bi2, - size_t size) -{ - u8 *bo = _bo; - const u8 *bi1 = _bi1, *bi2 = _bi2; - size_t itr; - for (itr = 0; itr < size; itr++) - bo[itr] = bi1[itr] ^ bi2[itr]; -} - /* Scratch space for MAC calculations. */ struct wusb_mac_scratch { struct aes_ccm_b0 b0; @@ -150,8 +143,7 @@ struct wusb_mac_scratch { * @a: ASCII string, 14 bytes long (I guess zero padded if needed; * we use exactly 14 bytes). * - * @b: data stream to be processed; cannot be a global or const local - * (will confuse the scatterlists) + * @b: data stream to be processed * * @blen: size of b... * @@ -160,16 +152,10 @@ struct wusb_mac_scratch { * @key. We bytewise xor B0 with B1 (1) and AES-crypt that. Then we * take the payload and divide it in blocks (16 bytes), xor them with * the previous crypto result (16 bytes) and crypt it, repeat the next - * block with the output of the previous one, rinse wash (I guess this - * is what AES CBC mode means...but I truly have no idea). So we use - * the CBC(AES) blkcipher, that does precisely that. The IV (Initial + * block with the output of the previous one, rinse wash. So we use + * the CBC-MAC(AES) shash, that does precisely that. The IV (Initial * Vector) is 16 bytes and is set to zero, so * - * See rfc3610. Linux crypto has a CBC implementation, but the - * documentation is scarce, to say the least, and the example code is - * so intricated that is difficult to understand how things work. Most - * of this is guess work -- bite me. - * * (1) Created as 6.5 says, again, using as l(a) 'Blen + 14', and * using the 14 bytes of @a to fill up * b1.{mac_header,e0,security_reserved,padding}. @@ -189,44 +175,24 @@ struct wusb_mac_scratch { * NOTE: blen is not aligned to a block size, we'll pad zeros, that's * what sg[4] is for. Maybe there is a smarter way to do this. */ -static int wusb_ccm_mac(struct crypto_sync_skcipher *tfm_cbc, - struct crypto_cipher *tfm_aes, +static int wusb_ccm_mac(struct crypto_shash *tfm_cbcmac, struct wusb_mac_scratch *scratch, void *mic, const struct aes_ccm_nonce *n, const struct aes_ccm_label *a, const void *b, size_t blen) { - int result = 0; - SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm_cbc); - struct scatterlist sg[4], sg_dst; - void *dst_buf; - size_t dst_size; - u8 *iv; - size_t zero_padding; + SHASH_DESC_ON_STACK(desc, tfm_cbcmac); + u8 iv[AES_BLOCK_SIZE]; /* * These checks should be compile time optimized out * ensure @a fills b1's mac_header and following fields */ - WARN_ON(sizeof(*a) != sizeof(scratch->b1) - sizeof(scratch->b1.la)); - WARN_ON(sizeof(scratch->b0) != sizeof(struct aes_ccm_block)); - WARN_ON(sizeof(scratch->b1) != sizeof(struct aes_ccm_block)); - WARN_ON(sizeof(scratch->ax) != sizeof(struct aes_ccm_block)); - - result = -ENOMEM; - zero_padding = blen % sizeof(struct aes_ccm_block); - if (zero_padding) - zero_padding = sizeof(struct aes_ccm_block) - zero_padding; - dst_size = blen + sizeof(scratch->b0) + sizeof(scratch->b1) + - zero_padding; - dst_buf = kzalloc(dst_size, GFP_KERNEL); - if (!dst_buf) - goto error_dst_buf; - - iv = kzalloc(crypto_sync_skcipher_ivsize(tfm_cbc), GFP_KERNEL); - if (!iv) - goto error_iv; + BUILD_BUG_ON(sizeof(*a) != sizeof(scratch->b1) - sizeof(scratch->b1.la)); + BUILD_BUG_ON(sizeof(scratch->b0) != sizeof(struct aes_ccm_block)); + BUILD_BUG_ON(sizeof(scratch->b1) != sizeof(struct aes_ccm_block)); + BUILD_BUG_ON(sizeof(scratch->ax) != sizeof(struct aes_ccm_block)); /* Setup B0 */ scratch->b0.flags = 0x59; /* Format B0 */ @@ -243,46 +209,28 @@ static int wusb_ccm_mac(struct crypto_sync_skcipher *tfm_cbc, scratch->b1.la = cpu_to_be16(blen + 14); memcpy(&scratch->b1.mac_header, a, sizeof(*a)); - sg_init_table(sg, ARRAY_SIZE(sg)); - sg_set_buf(&sg[0], &scratch->b0, sizeof(scratch->b0)); - sg_set_buf(&sg[1], &scratch->b1, sizeof(scratch->b1)); - sg_set_buf(&sg[2], b, blen); - /* 0 if well behaved :) */ - sg_set_page(&sg[3], ZERO_PAGE(0), zero_padding, 0); - sg_init_one(&sg_dst, dst_buf, dst_size); - - skcipher_request_set_sync_tfm(req, tfm_cbc); - skcipher_request_set_callback(req, 0, NULL, NULL); - skcipher_request_set_crypt(req, sg, &sg_dst, dst_size, iv); - result = crypto_skcipher_encrypt(req); - skcipher_request_zero(req); - if (result < 0) { - printk(KERN_ERR "E: can't compute CBC-MAC tag (MIC): %d\n", - result); - goto error_cbc_crypt; - } + desc->tfm = tfm_cbcmac; + crypto_shash_init(desc); + crypto_shash_update(desc, (u8 *)&scratch->b0, sizeof(scratch->b0) + + sizeof(scratch->b1)); + crypto_shash_finup(desc, b, blen, iv); /* Now we crypt the MIC Tag (*iv) with Ax -- values per WUSB1.0[6.5] * The procedure is to AES crypt the A0 block and XOR the MIC * Tag against it; we only do the first 8 bytes and place it * directly in the destination buffer. - * - * POS Crypto API: size is assumed to be AES's block size. - * Thanks for documenting it -- tip taken from airo.c */ scratch->ax.flags = 0x01; /* as per WUSB 1.0 spec */ scratch->ax.ccm_nonce = *n; scratch->ax.counter = 0; - crypto_cipher_encrypt_one(tfm_aes, (void *)&scratch->ax, - (void *)&scratch->ax); - bytewise_xor(mic, &scratch->ax, iv, 8); - result = 8; -error_cbc_crypt: - kfree(iv); -error_iv: - kfree(dst_buf); -error_dst_buf: - return result; + + /* reuse the CBC-MAC transform to perform the single block encryption */ + crypto_shash_digest(desc, (u8 *)&scratch->ax, sizeof(scratch->ax), + (u8 *)&scratch->ax); + + crypto_xor_cpy(mic, (u8 *)&scratch->ax, iv, 8); + + return 8; } /* @@ -298,45 +246,28 @@ ssize_t wusb_prf(void *out, size_t out_size, { ssize_t result, bytes = 0, bitr; struct aes_ccm_nonce n = *_n; - struct crypto_sync_skcipher *tfm_cbc; - struct crypto_cipher *tfm_aes; - struct wusb_mac_scratch *scratch; + struct crypto_shash *tfm_cbcmac; + struct wusb_mac_scratch scratch; u64 sfn = 0; __le64 sfn_le; - tfm_cbc = crypto_alloc_sync_skcipher("cbc(aes)", 0, 0); - if (IS_ERR(tfm_cbc)) { - result = PTR_ERR(tfm_cbc); - printk(KERN_ERR "E: can't load CBC(AES): %d\n", (int)result); - goto error_alloc_cbc; - } - result = crypto_sync_skcipher_setkey(tfm_cbc, key, 16); - if (result < 0) { - printk(KERN_ERR "E: can't set CBC key: %d\n", (int)result); - goto error_setkey_cbc; + tfm_cbcmac = crypto_alloc_shash("cbcmac(aes)", 0, 0); + if (IS_ERR(tfm_cbcmac)) { + result = PTR_ERR(tfm_cbcmac); + printk(KERN_ERR "E: can't load CBCMAC-AES: %d\n", (int)result); + goto error_alloc_cbcmac; } - tfm_aes = crypto_alloc_cipher("aes", 0, 0); - if (IS_ERR(tfm_aes)) { - result = PTR_ERR(tfm_aes); - printk(KERN_ERR "E: can't load AES: %d\n", (int)result); - goto error_alloc_aes; - } - result = crypto_cipher_setkey(tfm_aes, key, 16); + result = crypto_shash_setkey(tfm_cbcmac, key, AES_BLOCK_SIZE); if (result < 0) { - printk(KERN_ERR "E: can't set AES key: %d\n", (int)result); - goto error_setkey_aes; - } - scratch = kmalloc(sizeof(*scratch), GFP_KERNEL); - if (!scratch) { - result = -ENOMEM; - goto error_alloc_scratch; + printk(KERN_ERR "E: can't set CBCMAC-AES key: %d\n", (int)result); + goto error_setkey_cbcmac; } for (bitr = 0; bitr < (len + 63) / 64; bitr++) { sfn_le = cpu_to_le64(sfn++); memcpy(&n.sfn, &sfn_le, sizeof(n.sfn)); /* n.sfn++... */ - result = wusb_ccm_mac(tfm_cbc, tfm_aes, scratch, out + bytes, + result = wusb_ccm_mac(tfm_cbcmac, &scratch, out + bytes, &n, a, b, blen); if (result < 0) goto error_ccm_mac; @@ -344,15 +275,10 @@ ssize_t wusb_prf(void *out, size_t out_size, } result = bytes; - kfree(scratch); -error_alloc_scratch: error_ccm_mac: -error_setkey_aes: - crypto_free_cipher(tfm_aes); -error_alloc_aes: -error_setkey_cbc: - crypto_free_sync_skcipher(tfm_cbc); -error_alloc_cbc: +error_setkey_cbcmac: + crypto_free_shash(tfm_cbcmac); +error_alloc_cbcmac: return result; } @@ -377,12 +303,8 @@ static int wusb_oob_mic_verify(void) { int result; u8 mic[8]; - /* WUSB1.0[A.2] test vectors - * - * Need to keep it in the local stack as GCC 4.1.3something - * messes up and generates noise. - */ - struct usb_handshake stv_hsmic_hs = { + /* WUSB1.0[A.2] test vectors */ + static const struct usb_handshake stv_hsmic_hs = { .bMessageNumber = 2, .bStatus = 00, .tTKID = { 0x76, 0x98, 0x01 }, @@ -457,11 +379,8 @@ static int wusb_key_derive_verify(void) { int result = 0; struct wusb_keydvt_out keydvt_out; - /* These come from WUSB1.0[A.1] + 2006/12 errata - * NOTE: can't make this const or global -- somehow it seems - * the scatterlists for crypto get confused and we get - * bad data. There is no doc on this... */ - struct wusb_keydvt_in stv_keydvt_in_a1 = { + /* These come from WUSB1.0[A.1] + 2006/12 errata */ + static const struct wusb_keydvt_in stv_keydvt_in_a1 = { .hnonce = { 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f |