Skip to content

Commit

Permalink
Merge tag 'ak-pull-request' of https://gitlab.com/berrange/qemu into …
Browse files Browse the repository at this point in the history
…staging

Merge asymmetric cipher crypto support

This extends the internal crypto APIs to support the use of asymmetric
ciphers.

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEE2vOm/bJrYpEtDo4/vobrtBUQT98FAmKPWdgACgkQvobrtBUQ
# T9/dXA//XozeQbIK9y/1wb60LXiqHiHDMi8Ct1oEpNsLaL4lsp09VjtmxggqMfad
# MjxQjKdOVMVPISRnrKCJQ6qiGKQB7C/php1ZxOPdG4zgf2Ofl312GHZCLjqLkpB8
# KnhdFB31coI45EQ+agk5ZO8Baml85yY4sALLofGXV3xatJswH1HoMAmDATe5ebko
# ox7qd/S9Q4bpZA4v+8fUbvX2zI95hZta8+4d2Irx542gO8KibYKRVmffJhcKx6hy
# 4x7iTEaGQQn3DFMbVxsvb4wLwx1v8sSS6C2rHuGZY67ZzDnYhAdaHG9CaWR3uvtS
# vs7EcEWqn45SfJ/FaYUyon/btsawJrXP9NISmns4J6TYoN6sJJVxk9T9A/hlqtEE
# /iwTfp/Se+o2JDLgC+JHQz8maj4igloGNhF8+u4lXBLEpT7tlvaxhkrcPo9Um7ay
# bWpmLoxVN5vEvOnsrfLhK6LGPIzfjP4tYX0xwWy5Lm/DZ1LinJOONPXjArFr3TaQ
# rcS6L15ZaiFu9bYUyN1Uf7V7VydiVV8RlkuTqJ614gSX0v+GCMR1J+0WsQ4DtPlT
# G6WP0EnnD4Ulg9XpSMte2GXKQ0d8c7hTKr3/RW+BuvvgP5T4P7guBTRhmufRiip6
# BByKpXrQ72yGm6U+nTtEVFdUWVER31U0ufsW64hdM+LGfiG7fUE=
# =X589
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu 26 May 2022 03:43:36 AM PDT
# gpg:                using RSA key DAF3A6FDB26B62912D0E8E3FBE86EBB415104FDF
# gpg: Good signature from "Daniel P. Berrange <dan@berrange.com>" [full]
# gpg:                 aka "Daniel P. Berrange <berrange@redhat.com>" [full]

* tag 'ak-pull-request' of https://gitlab.com/berrange/qemu:
  tests/crypto: Add test suite for RSA keys
  test/crypto: Add test suite for crypto akcipher
  crypto: Implement RSA algorithm by gcrypt
  crypto: Implement RSA algorithm by hogweed
  crypto: add ASN.1 DER decoder
  crypto: Introduce akcipher crypto class
  qapi: crypto-akcipher: Introduce akcipher types to qapi

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
  • Loading branch information
rth7680 committed May 26, 2022
2 parents 58b5366 + f0cfb76 commit 2417cbd
Show file tree
Hide file tree
Showing 20 changed files with 4,169 additions and 0 deletions.
595 changes: 595 additions & 0 deletions crypto/akcipher-gcrypt.c.inc

Large diffs are not rendered by default.

451 changes: 451 additions & 0 deletions crypto/akcipher-nettle.c.inc

Large diffs are not rendered by default.

108 changes: 108 additions & 0 deletions crypto/akcipher.c
@@ -0,0 +1,108 @@
/*
* QEMU Crypto akcipher algorithms
*
* Copyright (c) 2022 Bytedance
* Author: zhenwei pi <pizhenwei@bytedance.com>
*
* 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, see <http://www.gnu.org/licenses/>.
*
*/

#include "qemu/osdep.h"
#include "crypto/akcipher.h"
#include "akcipherpriv.h"

#if defined(CONFIG_GCRYPT)
#include "akcipher-gcrypt.c.inc"
#elif defined(CONFIG_NETTLE) && defined(CONFIG_HOGWEED)
#include "akcipher-nettle.c.inc"
#else
QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts,
QCryptoAkCipherKeyType type,
const uint8_t *key, size_t keylen,
Error **errp)
{
QCryptoAkCipher *akcipher = NULL;

return akcipher;
}

bool qcrypto_akcipher_supports(QCryptoAkCipherOptions *opts)
{
return false;
}
#endif

int qcrypto_akcipher_encrypt(QCryptoAkCipher *akcipher,
const void *in, size_t in_len,
void *out, size_t out_len, Error **errp)
{
const QCryptoAkCipherDriver *drv = akcipher->driver;

return drv->encrypt(akcipher, in, in_len, out, out_len, errp);
}

int qcrypto_akcipher_decrypt(QCryptoAkCipher *akcipher,
const void *in, size_t in_len,
void *out, size_t out_len, Error **errp)
{
const QCryptoAkCipherDriver *drv = akcipher->driver;

return drv->decrypt(akcipher, in, in_len, out, out_len, errp);
}

int qcrypto_akcipher_sign(QCryptoAkCipher *akcipher,
const void *in, size_t in_len,
void *out, size_t out_len, Error **errp)
{
const QCryptoAkCipherDriver *drv = akcipher->driver;

return drv->sign(akcipher, in, in_len, out, out_len, errp);
}

int qcrypto_akcipher_verify(QCryptoAkCipher *akcipher,
const void *in, size_t in_len,
const void *in2, size_t in2_len, Error **errp)
{
const QCryptoAkCipherDriver *drv = akcipher->driver;

return drv->verify(akcipher, in, in_len, in2, in2_len, errp);
}

int qcrypto_akcipher_max_plaintext_len(QCryptoAkCipher *akcipher)
{
return akcipher->max_plaintext_len;
}

int qcrypto_akcipher_max_ciphertext_len(QCryptoAkCipher *akcipher)
{
return akcipher->max_ciphertext_len;
}

int qcrypto_akcipher_max_signature_len(QCryptoAkCipher *akcipher)
{
return akcipher->max_signature_len;
}

int qcrypto_akcipher_max_dgst_len(QCryptoAkCipher *akcipher)
{
return akcipher->max_dgst_len;
}

void qcrypto_akcipher_free(QCryptoAkCipher *akcipher)
{
const QCryptoAkCipherDriver *drv = akcipher->driver;

drv->free(akcipher);
}
55 changes: 55 additions & 0 deletions crypto/akcipherpriv.h
@@ -0,0 +1,55 @@
/*
* QEMU Crypto asymmetric algorithms
*
* Copyright (c) 2022 Bytedance
* Author: zhenwei pi <pizhenwei@bytedance.com>
*
* 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, see <http://www.gnu.org/licenses/>.
*
*/

#ifndef QCRYPTO_AKCIPHERPRIV_H
#define QCRYPTO_AKCIPHERPRIV_H

#include "qapi/qapi-types-crypto.h"

typedef struct QCryptoAkCipherDriver QCryptoAkCipherDriver;

struct QCryptoAkCipher {
QCryptoAkCipherAlgorithm alg;
QCryptoAkCipherKeyType type;
int max_plaintext_len;
int max_ciphertext_len;
int max_signature_len;
int max_dgst_len;
QCryptoAkCipherDriver *driver;
};

struct QCryptoAkCipherDriver {
int (*encrypt)(QCryptoAkCipher *akcipher,
const void *in, size_t in_len,
void *out, size_t out_len, Error **errp);
int (*decrypt)(QCryptoAkCipher *akcipher,
const void *out, size_t out_len,
void *in, size_t in_len, Error **errp);
int (*sign)(QCryptoAkCipher *akcipher,
const void *in, size_t in_len,
void *out, size_t out_len, Error **errp);
int (*verify)(QCryptoAkCipher *akcipher,
const void *in, size_t in_len,
const void *in2, size_t in2_len, Error **errp);
void (*free)(QCryptoAkCipher *akcipher);
};

#endif /* QCRYPTO_AKCIPHER_H */
189 changes: 189 additions & 0 deletions crypto/der.c
@@ -0,0 +1,189 @@
/*
* QEMU Crypto ASN.1 DER decoder
*
* Copyright (c) 2022 Bytedance
* Author: lei he <helei.sig11@bytedance.com>
*
* 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, see <http://www.gnu.org/licenses/>.
*
*/

#include "qemu/osdep.h"
#include "crypto/der.h"

enum QCryptoDERTypeTag {
QCRYPTO_DER_TYPE_TAG_BOOL = 0x1,
QCRYPTO_DER_TYPE_TAG_INT = 0x2,
QCRYPTO_DER_TYPE_TAG_BIT_STR = 0x3,
QCRYPTO_DER_TYPE_TAG_OCT_STR = 0x4,
QCRYPTO_DER_TYPE_TAG_OCT_NULL = 0x5,
QCRYPTO_DER_TYPE_TAG_OCT_OID = 0x6,
QCRYPTO_DER_TYPE_TAG_SEQ = 0x10,
QCRYPTO_DER_TYPE_TAG_SET = 0x11,
};

#define QCRYPTO_DER_CONSTRUCTED_MASK 0x20
#define QCRYPTO_DER_SHORT_LEN_MASK 0x80

static uint8_t qcrypto_der_peek_byte(const uint8_t **data, size_t *dlen)
{
return **data;
}

static void qcrypto_der_cut_nbytes(const uint8_t **data,
size_t *dlen,
size_t nbytes)
{
*data += nbytes;
*dlen -= nbytes;
}

static uint8_t qcrypto_der_cut_byte(const uint8_t **data, size_t *dlen)
{
uint8_t val = qcrypto_der_peek_byte(data, dlen);

qcrypto_der_cut_nbytes(data, dlen, 1);

return val;
}

static int qcrypto_der_invoke_callback(QCryptoDERDecodeCb cb, void *ctx,
const uint8_t *value, size_t vlen,
Error **errp)
{
if (!cb) {
return 0;
}

return cb(ctx, value, vlen, errp);
}

static int qcrypto_der_extract_definite_data(const uint8_t **data, size_t *dlen,
QCryptoDERDecodeCb cb, void *ctx,
Error **errp)
{
const uint8_t *value;
size_t vlen = 0;
uint8_t byte_count = qcrypto_der_cut_byte(data, dlen);

/* short format of definite-length */
if (!(byte_count & QCRYPTO_DER_SHORT_LEN_MASK)) {
if (byte_count > *dlen) {
error_setg(errp, "Invalid content length: %u", byte_count);
return -1;
}

value = *data;
vlen = byte_count;
qcrypto_der_cut_nbytes(data, dlen, vlen);

if (qcrypto_der_invoke_callback(cb, ctx, value, vlen, errp) != 0) {
return -1;
}
return vlen;
}

/* Ignore highest bit */
byte_count &= ~QCRYPTO_DER_SHORT_LEN_MASK;

/*
* size_t is enough to store the value of length, although the DER
* encoding standard supports larger length.
*/
if (byte_count > sizeof(size_t)) {
error_setg(errp, "Invalid byte count of content length: %u",
byte_count);
return -1;
}

if (byte_count > *dlen) {
error_setg(errp, "Invalid content length: %u", byte_count);
return -1;
}
while (byte_count--) {
vlen <<= 8;
vlen += qcrypto_der_cut_byte(data, dlen);
}

if (vlen > *dlen) {
error_setg(errp, "Invalid content length: %zu", vlen);
return -1;
}

value = *data;
qcrypto_der_cut_nbytes(data, dlen, vlen);

if (qcrypto_der_invoke_callback(cb, ctx, value, vlen, errp) != 0) {
return -1;
}
return vlen;
}

static int qcrypto_der_extract_data(const uint8_t **data, size_t *dlen,
QCryptoDERDecodeCb cb, void *ctx,
Error **errp)
{
uint8_t val;
if (*dlen < 1) {
error_setg(errp, "Need more data");
return -1;
}
val = qcrypto_der_peek_byte(data, dlen);

/* must use definite length format */
if (val == QCRYPTO_DER_SHORT_LEN_MASK) {
error_setg(errp, "Only definite length format is allowed");
return -1;
}

return qcrypto_der_extract_definite_data(data, dlen, cb, ctx, errp);
}

int qcrypto_der_decode_int(const uint8_t **data, size_t *dlen,
QCryptoDERDecodeCb cb, void *ctx, Error **errp)
{
uint8_t tag;
if (*dlen < 1) {
error_setg(errp, "Need more data");
return -1;
}
tag = qcrypto_der_cut_byte(data, dlen);

/* INTEGER must encoded in primitive-form */
if (tag != QCRYPTO_DER_TYPE_TAG_INT) {
error_setg(errp, "Invalid integer type tag: %u", tag);
return -1;
}

return qcrypto_der_extract_data(data, dlen, cb, ctx, errp);
}

int qcrypto_der_decode_seq(const uint8_t **data, size_t *dlen,
QCryptoDERDecodeCb cb, void *ctx, Error **errp)
{
uint8_t tag;
if (*dlen < 1) {
error_setg(errp, "Need more data");
return -1;
}
tag = qcrypto_der_cut_byte(data, dlen);

/* SEQUENCE must use constructed form */
if (tag != (QCRYPTO_DER_TYPE_TAG_SEQ | QCRYPTO_DER_CONSTRUCTED_MASK)) {
error_setg(errp, "Invalid type sequence tag: %u", tag);
return -1;
}

return qcrypto_der_extract_data(data, dlen, cb, ctx, errp);
}

0 comments on commit 2417cbd

Please sign in to comment.