/** Hash a block of memory and store the digest. @param hash The hash you wish to use @param in The data you wish to hash @param inlen The length of the data to hash (octets) @param out [out] Where to store the digest @param outlen [in/out] Max size and resulting size of the digest @return CRYPT_OK if successful */ int hash_memory(const struct algo_properties_st *hash, const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen) { int err; struct hash_data hdata; LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); if ((err = hash_is_valid(hash)) != CRYPT_OK) { return err; } if (*outlen < hash->digest_size) { *outlen = hash->digest_size; return CRYPT_BUFFER_OVERFLOW; } err = cryptodev_hash_init(&hdata, hash->kstr, NULL, 0); if (err < 0) { err = CRYPT_INVALID_HASH; goto LBL_ERR; } if ((err = _cryptodev_hash_update(&hdata, in, inlen)) < 0) { err = CRYPT_ERROR; goto LBL_ERR; } err = cryptodev_hash_final(&hdata, out); *outlen = hash->digest_size; LBL_ERR: cryptodev_hash_deinit(&hdata); return err; }
/* 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; }
/* 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; }
int crypto_run(struct fcrypt *fcr, struct kernel_crypt_op *kcop) { struct csession *ses_ptr; struct crypt_op *cop = &kcop->cop; int ret; if (unlikely(cop->op != COP_ENCRYPT && cop->op != COP_DECRYPT)) { ddebug(1, "invalid operation op=%u", cop->op); return -EINVAL; } /* this also enters ses_ptr->sem */ ses_ptr = crypto_get_session_by_sid(fcr, cop->ses); if (unlikely(!ses_ptr)) { derr(1, "invalid session ID=0x%08X", cop->ses); return -EINVAL; } if (ses_ptr->hdata.init != 0 && (cop->flags == 0 || cop->flags & COP_FLAG_RESET)) { ret = cryptodev_hash_reset(&ses_ptr->hdata); if (unlikely(ret)) { derr(1, "error in cryptodev_hash_reset()"); goto out_unlock; } } if (ses_ptr->cdata.init != 0) { int blocksize = ses_ptr->cdata.blocksize; if (unlikely(cop->len % blocksize)) { derr(1, "data size (%u) isn't a multiple of block size (%u)", cop->len, blocksize); ret = -EINVAL; goto out_unlock; } cryptodev_cipher_set_iv(&ses_ptr->cdata, kcop->iv, min(ses_ptr->cdata.ivsize, kcop->ivlen)); } if (likely(cop->len)) { if (cop->flags & COP_FLAG_NO_ZC) { if (unlikely(ses_ptr->alignmask && !IS_ALIGNED((unsigned long)cop->src, ses_ptr->alignmask))) { dwarning(2, "source address %p is not %d byte aligned - disabling zero copy", cop->src, ses_ptr->alignmask + 1); cop->flags &= ~COP_FLAG_NO_ZC; } if (unlikely(ses_ptr->alignmask && !IS_ALIGNED((unsigned long)cop->dst, ses_ptr->alignmask))) { dwarning(2, "destination address %p is not %d byte aligned - disabling zero copy", cop->dst, ses_ptr->alignmask + 1); cop->flags &= ~COP_FLAG_NO_ZC; } } if (cop->flags & COP_FLAG_NO_ZC) ret = __crypto_run_std(ses_ptr, &kcop->cop); else ret = __crypto_run_zc(ses_ptr, kcop); if (unlikely(ret)) goto out_unlock; } if (ses_ptr->cdata.init != 0) { cryptodev_cipher_get_iv(&ses_ptr->cdata, kcop->iv, min(ses_ptr->cdata.ivsize, kcop->ivlen)); } if (ses_ptr->hdata.init != 0 && ((cop->flags & COP_FLAG_FINAL) || (!(cop->flags & COP_FLAG_UPDATE) || cop->len == 0))) { ret = cryptodev_hash_final(&ses_ptr->hdata, kcop->hash_output); if (unlikely(ret)) { derr(0, "CryptoAPI failure: %d", ret); goto out_unlock; } kcop->digestsize = ses_ptr->hdata.digestsize; } if (ses_ptr->rdata.init != 0 && cop->len > 0) { kcop->rng_output = kmalloc(cop->len, GFP_KERNEL); if (unlikely(!kcop->rng_output)) { derr(0, "Not enough space to store %d random bytes.", cop->len); ret = -ENOMEM; goto out_unlock; } ret = cryptodev_rng_get_bytes(&ses_ptr->rdata, kcop->rng_output, cop->len); // some RNGs return 0 for success, while // some return the number of bytes generated if (unlikely(ret != 0 && ret != cop->len)) { derr(0, "RNG failure: %d", ret); kfree(kcop->rng_output); kcop->rng_output = NULL; goto out_unlock; } ret = 0; kcop->rnglen = cop->len; } out_unlock: crypto_put_session(ses_ptr); return ret; }