xref: /openbmc/qemu/crypto/akcipher-gcrypt.c.inc (revision e53c136f)
1/*
2 * QEMU Crypto akcipher algorithms
3 *
4 * Copyright (c) 2022 Bytedance
5 * Author: lei he <helei.sig11@bytedance.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#include <gcrypt.h>
23
24#include "qemu/osdep.h"
25#include "qemu/host-utils.h"
26#include "crypto/akcipher.h"
27#include "crypto/random.h"
28#include "qapi/error.h"
29#include "sysemu/cryptodev.h"
30#include "rsakey.h"
31
32typedef struct QCryptoGcryptRSA {
33    QCryptoAkCipher akcipher;
34    gcry_sexp_t key;
35    QCryptoRSAPaddingAlgo padding_alg;
36    QCryptoHashAlgo hash_alg;
37} QCryptoGcryptRSA;
38
39static void qcrypto_gcrypt_rsa_free(QCryptoAkCipher *akcipher)
40{
41    QCryptoGcryptRSA *rsa = (QCryptoGcryptRSA *)akcipher;
42    if (!rsa) {
43        return;
44    }
45
46    gcry_sexp_release(rsa->key);
47    g_free(rsa);
48}
49
50static QCryptoGcryptRSA *qcrypto_gcrypt_rsa_new(
51    const QCryptoAkCipherOptionsRSA *opt,
52    QCryptoAkCipherKeyType type,
53    const uint8_t *key,  size_t keylen,
54    Error **errp);
55
56QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts,
57                                      QCryptoAkCipherKeyType type,
58                                      const uint8_t *key, size_t keylen,
59                                      Error **errp)
60{
61    switch (opts->alg) {
62    case QCRYPTO_AK_CIPHER_ALGO_RSA:
63        return (QCryptoAkCipher *)qcrypto_gcrypt_rsa_new(
64            &opts->u.rsa, type, key, keylen, errp);
65
66    default:
67        error_setg(errp, "Unsupported algorithm: %u", opts->alg);
68        return NULL;
69    }
70
71    return NULL;
72}
73
74static void qcrypto_gcrypt_set_rsa_size(QCryptoAkCipher *akcipher, gcry_mpi_t n)
75{
76    size_t key_size = (gcry_mpi_get_nbits(n) + 7) / 8;
77    akcipher->max_plaintext_len = key_size;
78    akcipher->max_ciphertext_len = key_size;
79    akcipher->max_dgst_len = key_size;
80    akcipher->max_signature_len = key_size;
81}
82
83static int qcrypto_gcrypt_parse_rsa_private_key(
84    QCryptoGcryptRSA *rsa,
85    const uint8_t *key, size_t keylen, Error **errp)
86{
87    g_autoptr(QCryptoAkCipherRSAKey) rsa_key = qcrypto_akcipher_rsakey_parse(
88        QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE, key, keylen, errp);
89    gcry_mpi_t n = NULL, e = NULL, d = NULL, p = NULL, q = NULL, u = NULL;
90    bool compute_mul_inv = false;
91    int ret = -1;
92    gcry_error_t err;
93
94    if (!rsa_key) {
95        return ret;
96    }
97
98    err = gcry_mpi_scan(&n, GCRYMPI_FMT_STD,
99                        rsa_key->n.data, rsa_key->n.len, NULL);
100    if (gcry_err_code(err) != 0) {
101        error_setg(errp, "Failed to parse RSA parameter n: %s/%s",
102                   gcry_strsource(err), gcry_strerror(err));
103        goto cleanup;
104    }
105
106    err = gcry_mpi_scan(&e, GCRYMPI_FMT_STD,
107                        rsa_key->e.data, rsa_key->e.len, NULL);
108    if (gcry_err_code(err) != 0) {
109        error_setg(errp, "Failed to parse RSA parameter e: %s/%s",
110                   gcry_strsource(err), gcry_strerror(err));
111        goto cleanup;
112    }
113
114    err = gcry_mpi_scan(&d, GCRYMPI_FMT_STD,
115                        rsa_key->d.data, rsa_key->d.len, NULL);
116    if (gcry_err_code(err) != 0) {
117        error_setg(errp, "Failed to parse RSA parameter d: %s/%s",
118                   gcry_strsource(err), gcry_strerror(err));
119        goto cleanup;
120    }
121
122    err = gcry_mpi_scan(&p, GCRYMPI_FMT_STD,
123                        rsa_key->p.data, rsa_key->p.len, NULL);
124    if (gcry_err_code(err) != 0) {
125        error_setg(errp, "Failed to parse RSA parameter p: %s/%s",
126                   gcry_strsource(err), gcry_strerror(err));
127        goto cleanup;
128    }
129
130    err = gcry_mpi_scan(&q, GCRYMPI_FMT_STD,
131                        rsa_key->q.data, rsa_key->q.len, NULL);
132    if (gcry_err_code(err) != 0) {
133        error_setg(errp, "Failed to parse RSA parameter q: %s/%s",
134                   gcry_strsource(err), gcry_strerror(err));
135        goto cleanup;
136    }
137
138    if (gcry_mpi_cmp_ui(p, 0) > 0 && gcry_mpi_cmp_ui(q, 0) > 0) {
139        compute_mul_inv = true;
140
141        u = gcry_mpi_new(0);
142        if (gcry_mpi_cmp(p, q) > 0) {
143            gcry_mpi_swap(p, q);
144        }
145        gcry_mpi_invm(u, p, q);
146    }
147
148    if (compute_mul_inv) {
149        err = gcry_sexp_build(&rsa->key, NULL,
150            "(private-key (rsa (n %m) (e %m) (d %m) (p %m) (q %m) (u %m)))",
151            n, e, d, p, q, u);
152    } else {
153        err = gcry_sexp_build(&rsa->key, NULL,
154            "(private-key (rsa (n %m) (e %m) (d %m)))", n, e, d);
155    }
156    if (gcry_err_code(err) != 0) {
157        error_setg(errp, "Failed to build RSA private key: %s/%s",
158                   gcry_strsource(err), gcry_strerror(err));
159        goto cleanup;
160    }
161    qcrypto_gcrypt_set_rsa_size((QCryptoAkCipher *)rsa,  n);
162    ret = 0;
163
164cleanup:
165    gcry_mpi_release(n);
166    gcry_mpi_release(e);
167    gcry_mpi_release(d);
168    gcry_mpi_release(p);
169    gcry_mpi_release(q);
170    gcry_mpi_release(u);
171    return ret;
172}
173
174static int qcrypto_gcrypt_parse_rsa_public_key(QCryptoGcryptRSA *rsa,
175                                               const uint8_t *key,
176                                               size_t keylen,
177                                               Error **errp)
178{
179
180    g_autoptr(QCryptoAkCipherRSAKey) rsa_key = qcrypto_akcipher_rsakey_parse(
181        QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC, key, keylen, errp);
182    gcry_mpi_t n = NULL, e = NULL;
183    int ret = -1;
184    gcry_error_t err;
185
186    if (!rsa_key) {
187        return ret;
188    }
189
190    err = gcry_mpi_scan(&n, GCRYMPI_FMT_STD,
191                        rsa_key->n.data, rsa_key->n.len, NULL);
192    if (gcry_err_code(err) != 0) {
193        error_setg(errp, "Failed to parse RSA parameter n: %s/%s",
194                   gcry_strsource(err), gcry_strerror(err));
195        goto cleanup;
196    }
197
198    err = gcry_mpi_scan(&e, GCRYMPI_FMT_STD,
199                        rsa_key->e.data, rsa_key->e.len, NULL);
200    if (gcry_err_code(err) != 0) {
201        error_setg(errp, "Failed to parse RSA parameter e: %s/%s",
202                   gcry_strsource(err), gcry_strerror(err));
203        goto cleanup;
204    }
205
206    err = gcry_sexp_build(&rsa->key, NULL,
207                          "(public-key (rsa (n %m) (e %m)))", n, e);
208    if (gcry_err_code(err) != 0) {
209        error_setg(errp, "Failed to build RSA public key: %s/%s",
210                   gcry_strsource(err), gcry_strerror(err));
211        goto cleanup;
212    }
213    qcrypto_gcrypt_set_rsa_size((QCryptoAkCipher *)rsa, n);
214    ret = 0;
215
216cleanup:
217    gcry_mpi_release(n);
218    gcry_mpi_release(e);
219    return ret;
220}
221
222static int qcrypto_gcrypt_rsa_encrypt(QCryptoAkCipher *akcipher,
223                                      const void *in, size_t in_len,
224                                      void *out, size_t out_len,
225                                      Error **errp)
226{
227    QCryptoGcryptRSA *rsa = (QCryptoGcryptRSA *)akcipher;
228    int ret = -1;
229    gcry_sexp_t data_sexp = NULL, cipher_sexp = NULL;
230    gcry_sexp_t cipher_sexp_item = NULL;
231    gcry_mpi_t cipher_mpi = NULL;
232    const char *result;
233    gcry_error_t err;
234    size_t actual_len;
235
236    if (in_len > akcipher->max_plaintext_len) {
237        error_setg(errp, "Plaintext length is greater than key size: %d",
238                   akcipher->max_plaintext_len);
239        return ret;
240    }
241
242    err = gcry_sexp_build(&data_sexp, NULL,
243                          "(data (flags %s) (value %b))",
244                          QCryptoRSAPaddingAlgo_str(rsa->padding_alg),
245                          in_len, in);
246    if (gcry_err_code(err) != 0) {
247        error_setg(errp, "Failed to build plaintext: %s/%s",
248                   gcry_strsource(err), gcry_strerror(err));
249        goto cleanup;
250    }
251
252    err = gcry_pk_encrypt(&cipher_sexp, data_sexp, rsa->key);
253    if (gcry_err_code(err) != 0) {
254        error_setg(errp, "Failed to encrypt: %s/%s",
255                   gcry_strsource(err), gcry_strerror(err));
256        goto cleanup;
257    }
258
259    /* S-expression of cipher: (enc-val (rsa (a a-mpi))) */
260    cipher_sexp_item = gcry_sexp_find_token(cipher_sexp, "a", 0);
261    if (!cipher_sexp_item || gcry_sexp_length(cipher_sexp_item) != 2) {
262        error_setg(errp, "Invalid ciphertext result");
263        goto cleanup;
264    }
265
266    if (rsa->padding_alg == QCRYPTO_RSA_PADDING_ALGO_RAW) {
267        cipher_mpi = gcry_sexp_nth_mpi(cipher_sexp_item, 1, GCRYMPI_FMT_USG);
268        if (!cipher_mpi) {
269            error_setg(errp, "Invalid ciphertext result");
270            goto cleanup;
271        }
272        err = gcry_mpi_print(GCRYMPI_FMT_USG, out, out_len,
273                             &actual_len, cipher_mpi);
274        if (gcry_err_code(err) != 0) {
275            error_setg(errp, "Failed to print MPI: %s/%s",
276                       gcry_strsource(err), gcry_strerror(err));
277            goto cleanup;
278        }
279
280        if (actual_len > out_len) {
281            error_setg(errp, "Ciphertext buffer length is too small");
282            goto cleanup;
283        }
284
285        /* We always padding leading-zeros for RSA-RAW */
286        if (actual_len < out_len) {
287            memmove((uint8_t *)out + (out_len - actual_len), out, actual_len);
288            memset(out, 0, out_len - actual_len);
289        }
290        ret = out_len;
291
292    } else {
293        result = gcry_sexp_nth_data(cipher_sexp_item, 1, &actual_len);
294        if (!result) {
295            error_setg(errp, "Invalid ciphertext result");
296            goto cleanup;
297        }
298        if (actual_len > out_len) {
299            error_setg(errp, "Ciphertext buffer length is too small");
300            goto cleanup;
301        }
302        memcpy(out, result, actual_len);
303        ret = actual_len;
304    }
305
306cleanup:
307    gcry_sexp_release(data_sexp);
308    gcry_sexp_release(cipher_sexp);
309    gcry_sexp_release(cipher_sexp_item);
310    gcry_mpi_release(cipher_mpi);
311    return ret;
312}
313
314static int qcrypto_gcrypt_rsa_decrypt(QCryptoAkCipher *akcipher,
315                                      const void *in, size_t in_len,
316                                      void *out, size_t out_len,
317                                      Error **errp)
318{
319    QCryptoGcryptRSA *rsa = (QCryptoGcryptRSA *)akcipher;
320    int ret = -1;
321    gcry_sexp_t data_sexp = NULL, cipher_sexp = NULL;
322    gcry_mpi_t data_mpi = NULL;
323    gcry_error_t err;
324    size_t actual_len;
325    const char *result;
326
327    if (in_len > akcipher->max_ciphertext_len) {
328        error_setg(errp, "Ciphertext length is greater than key size: %d",
329                   akcipher->max_ciphertext_len);
330        return ret;
331    }
332
333    err = gcry_sexp_build(&cipher_sexp, NULL,
334                          "(enc-val (flags %s) (rsa (a %b) ))",
335                          QCryptoRSAPaddingAlgo_str(rsa->padding_alg),
336                          in_len, in);
337    if (gcry_err_code(err) != 0) {
338        error_setg(errp, "Failed to build ciphertext: %s/%s",
339                   gcry_strsource(err), gcry_strerror(err));
340        goto cleanup;
341    }
342
343    err = gcry_pk_decrypt(&data_sexp, cipher_sexp, rsa->key);
344    if (gcry_err_code(err) != 0) {
345        error_setg(errp, "Failed to decrypt: %s/%s",
346                   gcry_strsource(err), gcry_strerror(err));
347        goto cleanup;
348    }
349
350    /* S-expression of plaintext: (value plaintext) */
351    if (rsa->padding_alg == QCRYPTO_RSA_PADDING_ALGO_RAW) {
352        data_mpi = gcry_sexp_nth_mpi(data_sexp, 1, GCRYMPI_FMT_USG);
353        if (!data_mpi) {
354            error_setg(errp, "Invalid plaintext result");
355            goto cleanup;
356        }
357        err = gcry_mpi_print(GCRYMPI_FMT_USG, out, out_len,
358                             &actual_len, data_mpi);
359        if (gcry_err_code(err) != 0) {
360            error_setg(errp, "Failed to print MPI: %s/%s",
361                       gcry_strsource(err), gcry_strerror(err));
362            goto cleanup;
363        }
364        if (actual_len > out_len) {
365            error_setg(errp, "Plaintext buffer length is too small");
366            goto cleanup;
367        }
368        /* We always padding leading-zeros for RSA-RAW */
369        if (actual_len < out_len) {
370            memmove((uint8_t *)out + (out_len - actual_len), out, actual_len);
371            memset(out, 0, out_len - actual_len);
372        }
373        ret = out_len;
374    } else {
375        result = gcry_sexp_nth_data(data_sexp, 1, &actual_len);
376        if (!result) {
377            error_setg(errp, "Invalid plaintext result");
378            goto cleanup;
379        }
380        if (actual_len > out_len) {
381            error_setg(errp, "Plaintext buffer length is too small");
382            goto cleanup;
383        }
384        memcpy(out, result, actual_len);
385        ret = actual_len;
386    }
387
388cleanup:
389    gcry_sexp_release(cipher_sexp);
390    gcry_sexp_release(data_sexp);
391    gcry_mpi_release(data_mpi);
392    return ret;
393}
394
395static int qcrypto_gcrypt_rsa_sign(QCryptoAkCipher *akcipher,
396                                   const void *in, size_t in_len,
397                                   void *out, size_t out_len, Error **errp)
398{
399    QCryptoGcryptRSA *rsa = (QCryptoGcryptRSA *)akcipher;
400    int ret = -1;
401    gcry_sexp_t dgst_sexp = NULL, sig_sexp = NULL;
402    gcry_sexp_t sig_sexp_item = NULL;
403    const char *result;
404    gcry_error_t err;
405    size_t actual_len;
406
407    if (in_len > akcipher->max_dgst_len) {
408        error_setg(errp, "Data length is greater than key size: %d",
409                   akcipher->max_dgst_len);
410        return ret;
411    }
412
413    if (rsa->padding_alg != QCRYPTO_RSA_PADDING_ALGO_PKCS1) {
414        error_setg(errp, "Invalid padding %u", rsa->padding_alg);
415        return ret;
416    }
417
418    err = gcry_sexp_build(&dgst_sexp, NULL,
419                          "(data (flags pkcs1) (hash %s %b))",
420                          QCryptoHashAlgo_str(rsa->hash_alg),
421                          in_len, in);
422    if (gcry_err_code(err) != 0) {
423        error_setg(errp, "Failed to build dgst: %s/%s",
424                   gcry_strsource(err), gcry_strerror(err));
425        goto cleanup;
426    }
427
428    err = gcry_pk_sign(&sig_sexp, dgst_sexp, rsa->key);
429    if (gcry_err_code(err) != 0) {
430        error_setg(errp, "Failed to make signature: %s/%s",
431                   gcry_strsource(err), gcry_strerror(err));
432        goto cleanup;
433    }
434
435    /* S-expression of signature: (sig-val (rsa (s s-mpi))) */
436    sig_sexp_item = gcry_sexp_find_token(sig_sexp, "s", 0);
437    if (!sig_sexp_item || gcry_sexp_length(sig_sexp_item) != 2) {
438        error_setg(errp, "Invalid signature result");
439        goto cleanup;
440    }
441
442    result = gcry_sexp_nth_data(sig_sexp_item, 1, &actual_len);
443    if (!result) {
444        error_setg(errp, "Invalid signature result");
445        goto cleanup;
446    }
447
448    if (actual_len > out_len) {
449        error_setg(errp, "Signature buffer length is too small");
450        goto cleanup;
451    }
452    memcpy(out, result, actual_len);
453    ret = actual_len;
454
455cleanup:
456    gcry_sexp_release(dgst_sexp);
457    gcry_sexp_release(sig_sexp);
458    gcry_sexp_release(sig_sexp_item);
459
460    return ret;
461}
462
463static int qcrypto_gcrypt_rsa_verify(QCryptoAkCipher *akcipher,
464                                     const void *in, size_t in_len,
465                                     const void *in2, size_t in2_len,
466                                     Error **errp)
467{
468    QCryptoGcryptRSA *rsa = (QCryptoGcryptRSA *)akcipher;
469    int ret = -1;
470    gcry_sexp_t sig_sexp = NULL, dgst_sexp = NULL;
471    gcry_error_t err;
472
473    if (in_len > akcipher->max_signature_len) {
474        error_setg(errp, "Signature length is greater than key size: %d",
475                   akcipher->max_signature_len);
476        return ret;
477    }
478
479    if (in2_len > akcipher->max_dgst_len) {
480        error_setg(errp, "Data length is greater than key size: %d",
481                   akcipher->max_dgst_len);
482        return ret;
483    }
484
485    if (rsa->padding_alg != QCRYPTO_RSA_PADDING_ALGO_PKCS1) {
486        error_setg(errp, "Invalid padding %u", rsa->padding_alg);
487        return ret;
488    }
489
490    err = gcry_sexp_build(&sig_sexp, NULL,
491                          "(sig-val (rsa (s %b)))", in_len, in);
492    if (gcry_err_code(err) != 0) {
493        error_setg(errp, "Failed to build signature: %s/%s",
494                   gcry_strsource(err), gcry_strerror(err));
495        goto cleanup;
496    }
497
498    err = gcry_sexp_build(&dgst_sexp, NULL,
499                          "(data (flags pkcs1) (hash %s %b))",
500                          QCryptoHashAlgo_str(rsa->hash_alg),
501                          in2_len, in2);
502    if (gcry_err_code(err) != 0) {
503        error_setg(errp, "Failed to build dgst: %s/%s",
504                   gcry_strsource(err), gcry_strerror(err));
505        goto cleanup;
506    }
507
508    err = gcry_pk_verify(sig_sexp, dgst_sexp, rsa->key);
509    if (gcry_err_code(err) != 0) {
510        error_setg(errp, "Failed to verify signature: %s/%s",
511                   gcry_strsource(err), gcry_strerror(err));
512        goto cleanup;
513    }
514    ret = 0;
515
516cleanup:
517    gcry_sexp_release(dgst_sexp);
518    gcry_sexp_release(sig_sexp);
519
520    return ret;
521}
522
523QCryptoAkCipherDriver gcrypt_rsa = {
524    .encrypt = qcrypto_gcrypt_rsa_encrypt,
525    .decrypt = qcrypto_gcrypt_rsa_decrypt,
526    .sign = qcrypto_gcrypt_rsa_sign,
527    .verify = qcrypto_gcrypt_rsa_verify,
528    .free = qcrypto_gcrypt_rsa_free,
529};
530
531static QCryptoGcryptRSA *qcrypto_gcrypt_rsa_new(
532    const QCryptoAkCipherOptionsRSA *opt,
533    QCryptoAkCipherKeyType type,
534    const uint8_t *key, size_t keylen,
535    Error **errp)
536{
537    QCryptoGcryptRSA *rsa = g_new0(QCryptoGcryptRSA, 1);
538    rsa->padding_alg = opt->padding_alg;
539    rsa->hash_alg = opt->hash_alg;
540    rsa->akcipher.driver = &gcrypt_rsa;
541
542    switch (type) {
543    case QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE:
544        if (qcrypto_gcrypt_parse_rsa_private_key(rsa, key, keylen, errp) != 0) {
545            goto error;
546        }
547        break;
548
549    case QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC:
550        if (qcrypto_gcrypt_parse_rsa_public_key(rsa, key, keylen, errp) != 0) {
551            goto error;
552        }
553        break;
554
555    default:
556        error_setg(errp, "Unknown akcipher key type %d", type);
557        goto error;
558    }
559
560    return rsa;
561
562error:
563    qcrypto_gcrypt_rsa_free((QCryptoAkCipher *)rsa);
564    return NULL;
565}
566
567
568bool qcrypto_akcipher_supports(QCryptoAkCipherOptions *opts)
569{
570    switch (opts->alg) {
571    case QCRYPTO_AK_CIPHER_ALGO_RSA:
572        switch (opts->u.rsa.padding_alg) {
573        case QCRYPTO_RSA_PADDING_ALGO_RAW:
574            return true;
575
576        case QCRYPTO_RSA_PADDING_ALGO_PKCS1:
577            switch (opts->u.rsa.hash_alg) {
578            case QCRYPTO_HASH_ALGO_MD5:
579            case QCRYPTO_HASH_ALGO_SHA1:
580            case QCRYPTO_HASH_ALGO_SHA256:
581            case QCRYPTO_HASH_ALGO_SHA512:
582                return true;
583
584            default:
585                return false;
586            }
587
588        default:
589            return false;
590        }
591
592    default:
593        return true;
594    }
595}
596