Skip to content

Commit

Permalink
Add RSA
Browse files Browse the repository at this point in the history
Signed-off-by: Johnny Huang <[email protected]>
  • Loading branch information
johnydhuang committed Mar 16, 2021
1 parent e0c25e2 commit 9097bc9
Show file tree
Hide file tree
Showing 5 changed files with 326 additions and 8 deletions.
245 changes: 245 additions & 0 deletions cryptlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
#include <crypto/authenc.h>
#include "cryptodev_int.h"
#include "cipherapi.h"
#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0))
#include <linux/asn1_ber_bytecode.h>
#include <crypto/akcipher.h>
#endif

#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0))
extern const struct crypto_type crypto_givcipher_type;
Expand Down Expand Up @@ -490,3 +494,244 @@ int cryptodev_hash_copy(struct hash_data *dst, struct hash_data *src)
return ret;
}
#endif /* CIOCCPHASH */

#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0))
/* This function is necessary because the bignums in Linux kernel are MSB first
* (big endian) as opposed to LSB first as OpenBSD crypto layer uses */
void reverse_buf(uint8_t *buf, size_t buf_len)
{
int i;
int nmsb;
uint8_t *end;
uint8_t tmp;

nmsb = buf_len;

for (i = buf_len - 1; i >= 0; i--) {
if(buf[i] == 0)
nmsb--;
else
break;
}

end = buf + nmsb;

for (i = 0; i < nmsb/2; i++) {
end--;

tmp = *buf;
*buf = *end;
*end = tmp;

buf++;
}
}

int ber_wr_tag(uint8_t **ber_ptr, uint8_t tag)
{
**ber_ptr = tag;
*ber_ptr += 1;

return 0;
}

int ber_wr_len(uint8_t **ber_ptr, size_t len, size_t sz)
{
if (len < 127) {
**ber_ptr = len;
*ber_ptr += 1;
} else {
size_t sz_save = sz;

sz--;
**ber_ptr = 0x80 | sz;

while (sz > 0) {
*(*ber_ptr + sz) = len & 0xff;
len >>= 8;
sz--;
}
*ber_ptr += sz_save;
}

return 0;
}

int ber_wr_int(uint8_t **ber_ptr, uint8_t *crp_p, size_t sz)
{
int ret;

ret = copy_from_user(*ber_ptr, crp_p, sz);
// reverse_buf(*ber_ptr, sz);

*ber_ptr += sz;

return ret;
}

/* calculate the size of the length field itself in BER encoding */
size_t ber_enc_len(size_t len)
{
size_t sz;

sz = 1;
if (len > 127) { /* long encoding */
while (len != 0) {
len >>= 8;
sz++;
}
}

return sz;
}

void *cryptodev_alloc_rsa_pub_key(struct kernel_crypt_pkop *pkop,
uint32_t *key_len)
{
struct crypt_kop *cop = &pkop->pkop;
uint8_t *ber_key;
uint8_t *ber_ptr;
uint32_t ber_key_len;
size_t s_sz;
size_t e_sz;
size_t n_sz;
size_t s_enc_len;
size_t e_enc_len;
size_t n_enc_len;
int err;

/* BER public key format:
* SEQUENCE TAG 1 byte
* SEQUENCE LENGTH s_enc_len bytes
* INTEGER TAG 1 byte
* INTEGER LENGTH n_enc_len bytes
* INTEGER (n modulus) n_sz bytes
* INTEGER TAG 1 byte
* INTEGER LENGTH e_enc_len bytes
* INTEGER (e exponent) e_sz bytes
*/

e_sz = (cop->crk_param[1].crp_nbits + 7)/8;
n_sz = (cop->crk_param[2].crp_nbits + 7)/8;

e_enc_len = ber_enc_len(e_sz);
n_enc_len = ber_enc_len(n_sz);

/*
* Sequence length is the size of all the fields following the sequence
* tag, added together. The two added bytes account for the two INT
* tags in the Public Key sequence
*/
s_sz = e_sz + e_enc_len + n_sz + n_enc_len + 2;
s_enc_len = ber_enc_len(s_sz);

/* The added byte accounts for the SEQ tag at the start of the key */
ber_key_len = s_sz + s_enc_len + 1;

/* Linux asn1_ber_decoder doesn't like keys that are too large */
if (ber_key_len > 65535) {
return NULL;
}

ber_key = kmalloc(ber_key_len, GFP_DMA);
if (!ber_key) {
return NULL;
}

ber_ptr = ber_key;

err = ber_wr_tag(&ber_ptr, _tag(UNIV, CONS, SEQ)) ||
ber_wr_len(&ber_ptr, s_sz, s_enc_len) ||
ber_wr_tag(&ber_ptr, _tag(UNIV, PRIM, INT)) ||
ber_wr_len(&ber_ptr, n_sz, n_enc_len) ||
ber_wr_int(&ber_ptr, cop->crk_param[2].crp_p, n_sz) ||
ber_wr_tag(&ber_ptr, _tag(UNIV, PRIM, INT)) ||
ber_wr_len(&ber_ptr, e_sz, e_enc_len) ||
ber_wr_int(&ber_ptr, cop->crk_param[1].crp_p, e_sz);
if (err != 0) {
goto free_key;
}

*key_len = ber_key_len;
return ber_key;

free_key:
kfree(ber_key);
return NULL;
}

int crypto_bn_modexp(struct kernel_crypt_pkop *pkop)
{
struct crypt_kop *cop = &pkop->pkop;
uint8_t *ber_key;
uint32_t ber_key_len;
size_t m_sz;
size_t c_sz;
size_t c_sz_max;
uint8_t *m_buf;
uint8_t *c_buf;
struct scatterlist src;
struct scatterlist dst;
int err;

ber_key = cryptodev_alloc_rsa_pub_key(pkop, &ber_key_len);
if (!ber_key) {
return -ENOMEM;
}

err = crypto_akcipher_set_pub_key(pkop->s, ber_key, ber_key_len);
if (err != 0) {
goto free_key;
}

m_sz = (cop->crk_param[0].crp_nbits + 7)/8;
c_sz = (cop->crk_param[3].crp_nbits + 7)/8;

m_buf = kmalloc(m_sz, GFP_DMA);
if (!m_buf) {
err = -ENOMEM;
goto free_key;
}

err = copy_from_user(m_buf, cop->crk_param[0].crp_p, m_sz);
if (err != 0) {
goto free_m_buf;
}
// reverse_buf(m_buf, m_sz);

c_sz_max = crypto_akcipher_maxsize(pkop->s);
if (c_sz > c_sz_max) {
err = -EINVAL;
goto free_m_buf;
}

c_buf = kzalloc(c_sz_max, GFP_KERNEL);
if (!c_buf) {
goto free_m_buf;
}

sg_init_one(&src, m_buf, m_sz);
sg_init_one(&dst, c_buf, c_sz);

init_completion(&pkop->result.completion);
akcipher_request_set_callback(pkop->req, 0,
cryptodev_complete, &pkop->result);
akcipher_request_set_crypt(pkop->req, &src, &dst, m_sz, c_sz);

err = crypto_akcipher_encrypt(pkop->req);
err = waitfor(&pkop->result, err);

if (err == 0) {
reverse_buf(c_buf, c_sz);
err = copy_to_user(cop->crk_param[3].crp_p, c_buf, c_sz);
}

kfree(c_buf);
free_m_buf:
kfree(m_buf);
free_key:
kfree(ber_key);

return err;
}
#endif
9 changes: 3 additions & 6 deletions cryptlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@
# define CRYPTLIB_H

#include <linux/version.h>

struct cryptodev_result {
struct completion completion;
int err;
};

#include "cipherapi.h"

struct cipher_data {
Expand Down Expand Up @@ -105,5 +99,8 @@ int cryptodev_hash_init(struct hash_data *hdata, const char *alg_name,
int cryptodev_hash_copy(struct hash_data *dst, struct hash_data *src);
#endif

#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0))
int crypto_bn_modexp(struct kernel_crypt_pkop *pkop);
#endif

#endif
20 changes: 20 additions & 0 deletions cryptodev_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
#include <linux/scatterlist.h>
#include <crypto/cryptodev.h>
#include <crypto/aead.h>
#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0))
#include <crypto/internal/rsa.h>
#endif

#define PFX "cryptodev: "
#define dprintk(level, severity, format, a...) \
Expand All @@ -35,6 +38,11 @@
#define ddebug(level, format, a...) dprintk(level, KERN_DEBUG, format, ##a)


struct cryptodev_result {
struct completion completion;
int err;
};

extern int cryptodev_verbosity;

struct fcrypt {
Expand Down Expand Up @@ -106,6 +114,18 @@ struct kernel_crypt_auth_op {
struct mm_struct *mm;
};

#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0))
struct kernel_crypt_pkop {
struct crypt_kop pkop;

struct crypto_akcipher *s; /* Transform pointer from CryptoAPI */
struct akcipher_request *req; /* PKC request allocated from CryptoAPI */
struct cryptodev_result result; /* updated by completion handler */
};

int crypto_run_asym(struct kernel_crypt_pkop *pkop);
#endif

/* auth */

int kcaop_from_user(struct kernel_crypt_auth_op *kcop,
Expand Down
17 changes: 16 additions & 1 deletion ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,9 @@ cryptodev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg_)
struct session_op sop;
struct kernel_crypt_op kcop;
struct kernel_crypt_auth_op kcaop;
#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0))
struct kernel_crypt_pkop pkop;
#endif
struct crypt_priv *pcr = filp->private_data;
struct fcrypt *fcr;
struct session_info_op siop;
Expand All @@ -864,7 +867,11 @@ cryptodev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg_)

switch (cmd) {
case CIOCASYMFEAT:
return put_user(0, p);
ses = 0;
if (crypto_has_alg("rsa", 0, 0)) {
ses = CRF_MOD_EXP;
}
return put_user(ses, p);
case CRIOGET:
fd = clonefd(filp);
ret = put_user(fd, p);
Expand Down Expand Up @@ -912,6 +919,14 @@ cryptodev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg_)
return -EFAULT;
return crypto_copy_hash_state(fcr, cphop.dst_ses, cphop.src_ses);
#endif /* CIOCPHASH */
#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0))
case CIOCKEY:
ret = copy_from_user(&pkop.pkop, arg, sizeof(struct crypt_kop));
if (ret == 0) {
ret = crypto_run_asym(&pkop);
}
return ret;
#endif
case CIOCCRYPT:
if (unlikely(ret = kcop_from_user(&kcop, fcr, arg))) {
dwarning(1, "Error copying from user");
Expand Down
Loading

0 comments on commit 9097bc9

Please sign in to comment.