new file mode 100644
@@ -0,0 +1,248 @@
+/*
+ * QEMU Crypto akcipher algorithms
+ *
+ * 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 "der.h"
+#include "ecdsakey.h"
+
+#define QCRYPTO_ECDSA_PUBKEY_FMT_UNCOMPRESSED 0x04
+
+static int extract_mpi(void *ctx, const uint8_t *value,
+ size_t vlen, Error **errp)
+{
+ QCryptoAkCipherMPI *mpi = (QCryptoAkCipherMPI *)ctx;
+ if (vlen == 0) {
+ error_setg(errp, "Empty mpi field");
+ return -1;
+ }
+ mpi->data = g_memdup2(value, vlen);
+ mpi->len = vlen;
+ return 0;
+}
+
+static int extract_version(void *ctx, const uint8_t *value,
+ size_t vlen, Error **errp)
+{
+ uint8_t *version = (uint8_t *)ctx;
+ if (vlen != 1 || *value > 1) {
+ error_setg(errp, "Invalid ecdsa key version");
+ return -1;
+ }
+ *version = *value;
+ return 0;
+}
+
+static int extract_cons_content(void *ctx, const uint8_t *value,
+ size_t vlen, Error **errp)
+{
+ const uint8_t **content = (const uint8_t **)ctx;
+ if (vlen == 0) {
+ error_setg(errp, "Empty sequence");
+ return -1;
+ }
+ *content = value;
+ return 0;
+}
+
+static int qcrypto_akcipher_builtin_ecdsa_pubkey_parse2(
+ QCryptoAkCipherECDSAKey *ecdsa,
+ const uint8_t *key, size_t keylen, Error **errp);
+
+static int extract_pubkey(void *ctx, const uint8_t *value,
+ size_t vlen, Error **errp)
+{
+ QCryptoAkCipherECDSAKey *ecdsa = (QCryptoAkCipherECDSAKey *)ctx;
+ if (vlen < 4) {
+ error_setg(errp, "Public key part too short");
+ return -1;
+ }
+ /* Skip meta byte of BIT STRING */
+ if (*value != 0) {
+ error_setg(errp, "Invalid public key");
+ return -1;
+ }
+ value++;
+ vlen--;
+ return qcrypto_akcipher_builtin_ecdsa_pubkey_parse2(
+ ecdsa, value, vlen, errp);
+}
+
+/**
+ * ECDSASignature ::= SEQUENCE {
+ * r INTEGER
+ * s INTEGER
+ * }
+ */
+QCryptoAkCipherECDSASig *qcrypto_akcipher_ecdsasig_parse(
+ const uint8_t *signature, size_t len, Error **errp)
+{
+ g_autoptr(QCryptoAkCipherECDSASig) sig = g_new0(QCryptoAkCipherECDSASig, 1);
+ const uint8_t *seq;
+ size_t seq_length;
+ int decode_ret;
+
+ decode_ret = qcrypto_der_decode_seq(&signature, &len,
+ extract_cons_content, &seq, errp);
+
+ if (decode_ret < 0) {
+ return NULL;
+ }
+ if (len != 0) {
+ error_setg(errp, "Invalid ECDSA signature");
+ return NULL;
+ }
+ seq_length = decode_ret;
+
+ if (qcrypto_der_decode_int(&seq, &seq_length, extract_mpi,
+ &sig->r, errp) < 0 ||
+ qcrypto_der_decode_int(&seq, &seq_length, extract_mpi,
+ &sig->s, errp) < 0) {
+ return NULL;
+ }
+ if (seq_length != 0) {
+ error_setg(errp, "Invalid ECDSA signature");
+ return NULL;
+ }
+
+ return g_steal_pointer(&sig);
+}
+
+/**
+ * ECDSAPublicKey: compress-format || x coordinate || y coordinate
+ */
+static int qcrypto_akcipher_builtin_ecdsa_pubkey_parse2(
+ QCryptoAkCipherECDSAKey *ecdsa,
+ const uint8_t *key, size_t keylen, Error **errp)
+{
+ if (keylen < 3) {
+ error_setg(errp, "keylen is too short: %zu", keylen);
+ return -1;
+ }
+ if (key[0] != QCRYPTO_ECDSA_PUBKEY_FMT_UNCOMPRESSED) {
+ error_setg(errp, "Only uncompressed ECDSA public key is supported");
+ return -1;
+ }
+
+ /* Skip format byte */
+ key++;
+ keylen--;
+ if (keylen % 2 != 0) {
+ error_setg(errp, "ECDSA public key's length must be odd");
+ return -1;
+ }
+
+ ecdsa->pub_x.data = g_memdup2(key, keylen / 2);
+ ecdsa->pub_x.len = keylen / 2;
+ ecdsa->pub_y.data = g_memdup2(key + keylen / 2, keylen / 2);
+ ecdsa->pub_y.len = keylen / 2;
+
+ return 0;
+}
+
+static QCryptoAkCipherECDSAKey *qcrypto_akcipher_builtin_ecdsa_pubkey_parse(
+ const uint8_t *key, size_t keylen, Error **errp)
+{
+ g_autoptr(QCryptoAkCipherECDSAKey) ecdsa =
+ g_new0(QCryptoAkCipherECDSAKey, 1);
+ if (qcrypto_akcipher_builtin_ecdsa_pubkey_parse2(
+ ecdsa, key, keylen, errp) != 0) {
+ return NULL;
+ }
+ return g_steal_pointer(&ecdsa);
+}
+
+/**
+ * ECDSAPrivateKey ::= SEQUENCE {
+ * version INTEGER
+ * k OCTET STRING
+ * parameters [0] OID OPTIONAL
+ * publickey [1] BIT STRING OPTIONAL
+ * }
+ */
+static QCryptoAkCipherECDSAKey *qcrypto_akcipher_builtin_ecdsa_privkey_parse(
+ const uint8_t *key, size_t keylen, Error **errp)
+{
+ g_autoptr(QCryptoAkCipherECDSAKey) ecdsa =
+ g_new0(QCryptoAkCipherECDSAKey, 1);
+ uint8_t version;
+ const uint8_t *seq, *pubkey;
+ int decode_ret;
+ size_t seq_length, pubkey_length;
+
+ decode_ret = qcrypto_der_decode_seq(&key, &keylen, extract_cons_content,
+ &seq, errp);
+ if (decode_ret < 0) {
+ return NULL;
+ }
+ if (keylen != 0) {
+ error_setg(errp, "Invalid ECDSA private key");
+ return NULL;
+ }
+ seq_length = decode_ret;
+
+ if (qcrypto_der_decode_int(&seq, &seq_length, extract_version,
+ &version, errp) < 0 ||
+ qcrypto_der_decode_octet_str(&seq, &seq_length, extract_mpi,
+ &ecdsa->priv, errp) < 0) {
+ return NULL;
+ }
+
+ /* Here we just ignore curve id */
+ qcrypto_der_decode_ctx_tag(&seq, &seq_length, 0, NULL, NULL, NULL);
+
+ decode_ret = qcrypto_der_decode_ctx_tag(&seq, &seq_length, 1,
+ extract_cons_content,
+ &pubkey, NULL);
+ if (decode_ret > 0) {
+ pubkey_length = decode_ret;
+ if (qcrypto_der_decode_bit_str(&pubkey, &pubkey_length,
+ extract_pubkey, ecdsa, errp) < 0) {
+ return NULL;
+ }
+ if (pubkey_length != 0) {
+ error_setg(errp, "Invalid ECDSA private key");
+ return NULL;
+ }
+ }
+
+ if (seq_length != 0) {
+ error_setg(errp, "Invalid ECDSA private key");
+ return NULL;
+ }
+
+ return g_steal_pointer(&ecdsa);
+}
+
+QCryptoAkCipherECDSAKey *qcrypto_akcipher_ecdsakey_parse(
+ QCryptoAkCipherKeyType type,
+ const uint8_t *key, size_t keylen, Error **errp)
+{
+ switch (type) {
+ case QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
+ return qcrypto_akcipher_builtin_ecdsa_privkey_parse(key, keylen, errp);
+
+ case QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
+ return qcrypto_akcipher_builtin_ecdsa_pubkey_parse(key, keylen, errp);
+
+ default:
+ error_setg(errp, "Unknown key type: %d", type);
+ return NULL;
+ }
+}
new file mode 100644
@@ -0,0 +1,118 @@
+/*
+ * QEMU Crypto ECDSA key parser
+ *
+ * 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 "ecdsakey.h"
+#include "der.h"
+
+void qcrypto_akcipher_ecdsasig_free(QCryptoAkCipherECDSASig *sig)
+{
+ if (!sig) {
+ return;
+ }
+ g_free(sig->r.data);
+ g_free(sig->s.data);
+ g_free(sig);
+}
+
+void qcrypto_akcipher_ecdsasig_x9_62_encode(QCryptoAkCipherECDSASig *sig,
+ uint8_t *dst, size_t *dst_len)
+{
+ size_t r_len, s_len;
+ uint8_t *r_dst, *s_dst;
+ g_autofree uint8_t *buff = NULL;
+
+ qcrypto_der_encode_int(NULL, sig->r.len, NULL, &r_len);
+ qcrypto_der_encode_int(NULL, sig->s.len, NULL, &s_len);
+
+ buff = g_new0(uint8_t, r_len + s_len);
+ r_dst = buff;
+ qcrypto_der_encode_int(sig->r.data, sig->r.len, r_dst, &r_len);
+ s_dst = buff + r_len;
+ qcrypto_der_encode_int(sig->s.data, sig->s.len, s_dst, &s_len);
+
+ qcrypto_der_encode_seq(buff, r_len + s_len, dst, dst_len);
+}
+
+QCryptoAkCipherECDSASig *qcrypto_akcipher_ecdsasig_alloc(
+ QCryptoCurveID curve_id, Error **errp)
+{
+ int keylen;
+ QCryptoAkCipherECDSASig *sig;
+
+ switch (curve_id) {
+ case QCRYPTO_CURVE_ID_NIST_P192:
+ keylen = 192 / 8;
+ break;
+
+ case QCRYPTO_CURVE_ID_NIST_P256:
+ keylen = 256 / 8;
+ break;
+
+ case QCRYPTO_CURVE_ID_NIST_P384:
+ keylen = 384 / 8;
+ break;
+
+ default:
+ error_setg(errp, "Unknown curve id: %d", curve_id);
+ return NULL;
+ }
+
+ /*
+ * Note: when encoding positive bignum in tow'complement, we have to add
+ * a leading zero if the most significant byte is greater than or
+ * equal to 0x80.
+ */
+ sig = g_new0(QCryptoAkCipherECDSASig, 1);
+ sig->r.data = g_new0(uint8_t, keylen + 1);
+ sig->r.len = keylen + 1;
+ sig->s.data = g_new0(uint8_t, keylen + 1);
+ sig->s.len = keylen + 1;
+ return sig;
+}
+
+size_t qcrypto_akcipher_ecdsasig_x9_62_size(size_t keylen)
+{
+ size_t integer_len;
+ size_t seq_len;
+
+ /*
+ * Note: when encoding positive bignum in tow'complement, we have to add
+ * a leading zero if the most significant byte is greater than or
+ * equal to 0x80.
+ */
+ qcrypto_der_encode_int(NULL, keylen + 1, NULL, &integer_len);
+ qcrypto_der_encode_seq(NULL, integer_len * 2, NULL, &seq_len);
+ return seq_len;
+}
+
+void qcrypto_akcipher_ecdsakey_free(QCryptoAkCipherECDSAKey *ecdsa)
+{
+ if (!ecdsa) {
+ return;
+ }
+ g_free(ecdsa->priv.data);
+ g_free(ecdsa->pub_x.data);
+ g_free(ecdsa->pub_y.data);
+ g_free(ecdsa);
+}
+
+#include "ecdsakey-builtin.c.inc"
new file mode 100644
@@ -0,0 +1,66 @@
+/*
+ * QEMU Crypto ECDSA signature parser
+ *
+ * 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/>.
+ *
+ */
+
+#ifndef QCRYPTO_ECDSASIG_H
+#define QCRYPTO_ECDSASIG_H
+
+#include "qemu/host-utils.h"
+#include "crypto/akcipher.h"
+#include "crypto/rsakey.h"
+
+typedef struct QCryptoAkCipherECDSAKey QCryptoAkCipherECDSAKey;
+typedef struct QCryptoAkCipherECDSASig QCryptoAkCipherECDSASig;
+
+struct QCryptoAkCipherECDSASig {
+ QCryptoAkCipherMPI r;
+ QCryptoAkCipherMPI s;
+};
+
+struct QCryptoAkCipherECDSAKey {
+ QCryptoAkCipherMPI priv;
+ QCryptoAkCipherMPI pub_x;
+ QCryptoAkCipherMPI pub_y;
+};
+
+QCryptoAkCipherECDSASig *qcrypto_akcipher_ecdsasig_parse(
+ const uint8_t *sig, size_t len, Error **errp);
+
+QCryptoAkCipherECDSASig *qcrypto_akcipher_ecdsasig_alloc(
+ QCryptoCurveID curve_id, Error **errp);
+
+void qcrypto_akcipher_ecdsasig_free(QCryptoAkCipherECDSASig *sig);
+
+void qcrypto_akcipher_ecdsasig_x9_62_encode(
+ QCryptoAkCipherECDSASig *sig, uint8_t *dst, size_t *dst_len);
+
+size_t qcrypto_akcipher_ecdsasig_x9_62_size(size_t keylen);
+
+QCryptoAkCipherECDSAKey *qcrypto_akcipher_ecdsakey_parse(
+ QCryptoAkCipherKeyType type,
+ const uint8_t *key, size_t keylen, Error **errp);
+
+void qcrypto_akcipher_ecdsakey_free(QCryptoAkCipherECDSAKey *key);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoAkCipherECDSASig,
+ qcrypto_akcipher_ecdsasig_free);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoAkCipherECDSAKey,
+ qcrypto_akcipher_ecdsakey_free);
+#endif
@@ -7,6 +7,7 @@ crypto_ss.add(files(
'block.c',
'cipher.c',
'der.c',
+ 'ecdsakey.c',
'hash.c',
'hmac.c',
'ivgen-essiv.c',
Add ECDSA key parser and ECDSA signature parser. Signed-off-by: lei he <helei.sig11@bytedance.com> --- crypto/ecdsakey-builtin.c.inc | 248 ++++++++++++++++++++++++++++++++++++++++++ crypto/ecdsakey.c | 118 ++++++++++++++++++++ crypto/ecdsakey.h | 66 +++++++++++ crypto/meson.build | 1 + 4 files changed, 433 insertions(+) create mode 100644 crypto/ecdsakey-builtin.c.inc create mode 100644 crypto/ecdsakey.c create mode 100644 crypto/ecdsakey.h