/* Typical AEAD (i.e. GCM) encryption/decryption. * During decryption the tag is verified. */ static int auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop, struct scatterlist *auth_sg, uint32_t auth_len, struct scatterlist *src_sg, struct scatterlist *dst_sg, uint32_t len) { int ret; struct crypt_auth_op *caop = &kcaop->caop; int max_tag_len; max_tag_len = cryptodev_cipher_get_tag_size(&ses_ptr->cdata); if (unlikely(caop->tag_len > max_tag_len)) { dprintk(0, KERN_ERR, "Illegal tag length: %d\n", caop->tag_len); return -EINVAL; } if (caop->tag_len) cryptodev_cipher_set_tag_size(&ses_ptr->cdata, caop->tag_len); else caop->tag_len = max_tag_len; cryptodev_cipher_auth(&ses_ptr->cdata, auth_sg, auth_len); if (caop->op == COP_ENCRYPT) { ret = cryptodev_cipher_encrypt(&ses_ptr->cdata, src_sg, dst_sg, len); if (unlikely(ret)) { dprintk(0, KERN_ERR, "cryptodev_cipher_encrypt: %d\n", ret); return ret; } kcaop->dst_len = len + caop->tag_len; caop->tag = caop->dst + len; } else { ret = cryptodev_cipher_decrypt(&ses_ptr->cdata, src_sg, dst_sg, len); if (unlikely(ret)) { dprintk(0, KERN_ERR, "cryptodev_cipher_decrypt: %d\n", ret); return ret; } kcaop->dst_len = len - caop->tag_len; caop->tag = caop->dst + len - caop->tag_len; } return 0; }
static int hash_n_crypt(struct csession *ses_ptr, struct crypt_op *cop, struct scatterlist *src_sg, struct scatterlist *dst_sg, uint32_t len) { int ret; /* Always hash before encryption and after decryption. Maybe * we should introduce a flag to switch... TBD later on. */ if (cop->op == COP_ENCRYPT) { if (ses_ptr->hdata.init != 0) { ret = cryptodev_hash_update(&ses_ptr->hdata, src_sg, len); if (unlikely(ret)) goto out_err; } if (ses_ptr->cdata.init != 0) { ret = cryptodev_cipher_encrypt(&ses_ptr->cdata, src_sg, dst_sg, len); if (unlikely(ret)) goto out_err; } } else { if (ses_ptr->cdata.init != 0) { ret = cryptodev_cipher_decrypt(&ses_ptr->cdata, src_sg, dst_sg, len); if (unlikely(ret)) goto out_err; } if (ses_ptr->hdata.init != 0) { ret = cryptodev_hash_update(&ses_ptr->hdata, dst_sg, len); if (unlikely(ret)) goto out_err; } } return 0; out_err: derr(0, "CryptoAPI failure: %d", ret); return ret; }
/* Authenticate and encrypt the SRTP way. During decryption * it verifies the tag and returns -EBADMSG on error. */ static int srtp_auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop, struct scatterlist *auth_sg, uint32_t auth_len, struct scatterlist *dst_sg, uint32_t len) { int ret, fail = 0; struct crypt_auth_op *caop = &kcaop->caop; uint8_t vhash[AALG_MAX_RESULT_LEN]; uint8_t hash_output[AALG_MAX_RESULT_LEN]; /* SRTP authenticates the encrypted data. */ if (caop->op == COP_ENCRYPT) { if (ses_ptr->cdata.init != 0) { ret = cryptodev_cipher_encrypt(&ses_ptr->cdata, dst_sg, dst_sg, len); if (unlikely(ret)) { dprintk(0, KERN_ERR, "cryptodev_cipher_encrypt: %d\n", ret); return ret; } } if (ses_ptr->hdata.init != 0) { if (auth_len > 0) { ret = cryptodev_hash_update(&ses_ptr->hdata, auth_sg, auth_len); if (unlikely(ret)) { dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret); return ret; } } ret = cryptodev_hash_final(&ses_ptr->hdata, hash_output); if (unlikely(ret)) { dprintk(0, KERN_ERR, "cryptodev_hash_final: %d\n", ret); return ret; } if (unlikely(copy_to_user(caop->tag, hash_output, caop->tag_len))) { return -EFAULT; } } } else { if (ses_ptr->hdata.init != 0) { if (unlikely(caop->tag_len > sizeof(vhash) || caop->tag_len > len)) { dprintk(1, KERN_ERR, "Illegal tag len size\n"); return -EINVAL; } if (unlikely(copy_from_user(vhash, caop->tag, caop->tag_len))) { return -EFAULT; } ret = cryptodev_hash_update(&ses_ptr->hdata, auth_sg, auth_len); if (unlikely(ret)) { dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret); return ret; } ret = cryptodev_hash_final(&ses_ptr->hdata, hash_output); if (unlikely(ret)) { dprintk(0, KERN_ERR, "cryptodev_hash_final: %d\n", ret); return ret; } if (memcmp(vhash, hash_output, caop->tag_len) != 0 || fail != 0) { dprintk(2, KERN_ERR, "MAC verification failed\n"); return -EBADMSG; } } if (ses_ptr->cdata.init != 0) { ret = cryptodev_cipher_decrypt(&ses_ptr->cdata, dst_sg, dst_sg, len); if (unlikely(ret)) { dprintk(0, KERN_ERR, "cryptodev_cipher_decrypt: %d\n", ret); return ret; } } } kcaop->dst_len = len; return 0; }
/* Authenticate and encrypt the TLS way (also perform padding). * During decryption it verifies the pad and tag and returns -EBADMSG on error. */ static int tls_auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop, struct scatterlist *auth_sg, uint32_t auth_len, struct scatterlist *dst_sg, uint32_t len) { int ret, fail = 0; struct crypt_auth_op *caop = &kcaop->caop; uint8_t vhash[AALG_MAX_RESULT_LEN]; uint8_t hash_output[AALG_MAX_RESULT_LEN]; /* TLS authenticates the plaintext except for the padding. */ if (caop->op == COP_ENCRYPT) { if (ses_ptr->hdata.init != 0) { if (auth_len > 0) { ret = cryptodev_hash_update(&ses_ptr->hdata, auth_sg, auth_len); if (unlikely(ret)) { dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret); return ret; } } if (len > 0) { ret = cryptodev_hash_update(&ses_ptr->hdata, dst_sg, len); if (unlikely(ret)) { dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret); return ret; } } ret = cryptodev_hash_final(&ses_ptr->hdata, hash_output); if (unlikely(ret)) { dprintk(0, KERN_ERR, "cryptodev_hash_final: %d\n", ret); return ret; } copy_tls_hash( dst_sg, len, hash_output, caop->tag_len); len += caop->tag_len; } if (ses_ptr->cdata.init != 0) { if (ses_ptr->cdata.blocksize > 1) { ret = pad_record(dst_sg, len, ses_ptr->cdata.blocksize); len += ret; } ret = cryptodev_cipher_encrypt(&ses_ptr->cdata, dst_sg, dst_sg, len); if (unlikely(ret)) { dprintk(0, KERN_ERR, "cryptodev_cipher_encrypt: %d\n", ret); return ret; } } } else { if (ses_ptr->cdata.init != 0) { ret = cryptodev_cipher_decrypt(&ses_ptr->cdata, dst_sg, dst_sg, len); if (unlikely(ret)) { dprintk(0, KERN_ERR, "cryptodev_cipher_decrypt: %d\n", ret); return ret; } if (ses_ptr->cdata.blocksize > 1) { ret = verify_tls_record_pad(dst_sg, len, ses_ptr->cdata.blocksize); if (unlikely(ret < 0)) { dprintk(2, KERN_ERR, "verify_record_pad: %d\n", ret); fail = 1; } else { len -= ret; } } } if (ses_ptr->hdata.init != 0) { if (unlikely(caop->tag_len > sizeof(vhash) || caop->tag_len > len)) { dprintk(1, KERN_ERR, "Illegal tag len size\n"); return -EINVAL; } read_tls_hash( dst_sg, len, vhash, caop->tag_len); len -= caop->tag_len; if (auth_len > 0) { ret = cryptodev_hash_update(&ses_ptr->hdata, auth_sg, auth_len); if (unlikely(ret)) { dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret); return ret; } } if (len > 0) { ret = cryptodev_hash_update(&ses_ptr->hdata, dst_sg, len); if (unlikely(ret)) { dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret); return ret; } } ret = cryptodev_hash_final(&ses_ptr->hdata, hash_output); if (unlikely(ret)) { dprintk(0, KERN_ERR, "cryptodev_hash_final: %d\n", ret); return ret; } if (memcmp(vhash, hash_output, caop->tag_len) != 0 || fail != 0) { dprintk(2, KERN_ERR, "MAC verification failed (tag_len: %d)\n", caop->tag_len); return -EBADMSG; } } } kcaop->dst_len = len; return 0; }