Bugfixes and Daniel Berrange's crypto library.
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQEcBAABCAAGBQJVnQWdAAoJEL/70l94x66D6OgIAKJlzQfmy5w7Q9WD4vCMhD76 JrpLSsn7Gx/Bws0Nu9nLQlqun5z4hiUxyG2kP/WqD9+tV3cpSMSyrG6ImVdqKnQ5 +Z8WJZuREkQv0aqDUjQVST+eIDZuh2LWJXAjhgsCXUHY77eWb/7WmKT79xJOa+5C 5xB1qxudqX5IsTvpiKKPbmUGYkAcvRX1dUSaFwRIMO0UyKn59B9WfM9a5slIbLW7 XfI8+wEJshTVLuQkkTfdidWQc5M5DwlmO7ESUNR/BRPCPFeyjcDqgQY5pBM5XVo9 C+S0R3zIt3Ew0fhCtLRyjlIT0bGfwjbU5HRiHcyldBKhNUZZjSUoOWJnYRHXUDY= =H8wA -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging Bugfixes and Daniel Berrange's crypto library. # gpg: Signature made Wed Jul 8 12:12:29 2015 BST using RSA key ID 78C7AE83 # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" # gpg: WARNING: This key is not certified with sufficiently trusted signatures! # gpg: It is not certain that the signature belongs to the owner. # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1 # Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83 * remotes/bonzini/tags/for-upstream: ossaudio: fix memory leak ui: convert VNC to use generic cipher API block: convert qcow/qcow2 to use generic cipher API ui: convert VNC websockets to use crypto APIs block: convert quorum blockdrv to use crypto APIs crypto: add a nettle cipher implementation crypto: add a gcrypt cipher implementation crypto: introduce generic cipher API & built-in implementation crypto: move built-in D3DES implementation into crypto/ crypto: move built-in AES implementation into crypto/ crypto: introduce new module for computing hash digests vl: move rom_load_all after machine init done Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
		
						commit
						acf7b7fdf3
					
				| @ -1052,6 +1052,13 @@ S: Supported | ||||
| F: qemu-seccomp.c | ||||
| F: include/sysemu/seccomp.h | ||||
| 
 | ||||
| Cryptography | ||||
| M: Daniel P. Berrange <berrange@redhat.com> | ||||
| S: Maintained | ||||
| F: crypto/ | ||||
| F: include/crypto/ | ||||
| F: tests/test-crypto-* | ||||
| 
 | ||||
| Usermode Emulation | ||||
| ------------------ | ||||
| Overall | ||||
|  | ||||
| @ -2,6 +2,7 @@ | ||||
| # Common libraries for tools and emulators
 | ||||
| stub-obj-y = stubs/ | ||||
| util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o qapi-event.o | ||||
| util-obj-y += crypto/ | ||||
| 
 | ||||
| #######################################################################
 | ||||
| # block-obj-y is code used by both qemu system emulation and qemu-img
 | ||||
|  | ||||
| @ -853,6 +853,7 @@ static void *oss_audio_init (void) | ||||
| 
 | ||||
|     if (access(conf->devpath_in, R_OK | W_OK) < 0 || | ||||
|         access(conf->devpath_out, R_OK | W_OK) < 0) { | ||||
|         g_free(conf); | ||||
|         return NULL; | ||||
|     } | ||||
|     return conf; | ||||
|  | ||||
| @ -3,7 +3,7 @@ block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-c | ||||
| block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o | ||||
| block-obj-y += qed-check.o | ||||
| block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o | ||||
| block-obj-$(CONFIG_QUORUM) += quorum.o | ||||
| block-obj-y += quorum.o | ||||
| block-obj-y += parallels.o blkdebug.o blkverify.o | ||||
| block-obj-y += block-backend.o snapshot.o qapi.o | ||||
| block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o | ||||
|  | ||||
							
								
								
									
										102
									
								
								block/qcow.c
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								block/qcow.c
									
									
									
									
									
								
							| @ -26,7 +26,7 @@ | ||||
| #include "qemu/module.h" | ||||
| #include <zlib.h> | ||||
| #include "qapi/qmp/qerror.h" | ||||
| #include "qemu/aes.h" | ||||
| #include "crypto/cipher.h" | ||||
| #include "migration/migration.h" | ||||
| 
 | ||||
| /**************************************************************/ | ||||
| @ -72,10 +72,8 @@ typedef struct BDRVQcowState { | ||||
|     uint8_t *cluster_cache; | ||||
|     uint8_t *cluster_data; | ||||
|     uint64_t cluster_cache_offset; | ||||
|     uint32_t crypt_method; /* current crypt method, 0 if no key yet */ | ||||
|     QCryptoCipher *cipher; /* NULL if no key yet */ | ||||
|     uint32_t crypt_method_header; | ||||
|     AES_KEY aes_encrypt_key; | ||||
|     AES_KEY aes_decrypt_key; | ||||
|     CoMutex lock; | ||||
|     Error *migration_blocker; | ||||
| } BDRVQcowState; | ||||
| @ -154,6 +152,11 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|     } | ||||
|     if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128)) { | ||||
|         error_setg(errp, "AES cipher not available"); | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|     } | ||||
|     s->crypt_method_header = header.crypt_method; | ||||
|     if (s->crypt_method_header) { | ||||
|         bs->encrypted = 1; | ||||
| @ -260,6 +263,7 @@ static int qcow_set_key(BlockDriverState *bs, const char *key) | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     uint8_t keybuf[16]; | ||||
|     int len, i; | ||||
|     Error *err; | ||||
| 
 | ||||
|     memset(keybuf, 0, 16); | ||||
|     len = strlen(key); | ||||
| @ -271,38 +275,67 @@ static int qcow_set_key(BlockDriverState *bs, const char *key) | ||||
|         keybuf[i] = key[i]; | ||||
|     } | ||||
|     assert(bs->encrypted); | ||||
|     s->crypt_method = s->crypt_method_header; | ||||
| 
 | ||||
|     if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0) | ||||
|         return -1; | ||||
|     if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0) | ||||
|     qcrypto_cipher_free(s->cipher); | ||||
|     s->cipher = qcrypto_cipher_new( | ||||
|         QCRYPTO_CIPHER_ALG_AES_128, | ||||
|         QCRYPTO_CIPHER_MODE_CBC, | ||||
|         keybuf, G_N_ELEMENTS(keybuf), | ||||
|         &err); | ||||
| 
 | ||||
|     if (!s->cipher) { | ||||
|         /* XXX would be nice if errors in this method could
 | ||||
|          * be properly propagate to the caller. Would need | ||||
|          * the bdrv_set_key() API signature to be fixed. */ | ||||
|         error_free(err); | ||||
|         return -1; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| /* The crypt function is compatible with the linux cryptoloop
 | ||||
|    algorithm for < 4 GB images. NOTE: out_buf == in_buf is | ||||
|    supported */ | ||||
| static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num, | ||||
|                             uint8_t *out_buf, const uint8_t *in_buf, | ||||
|                             int nb_sectors, int enc, | ||||
|                             const AES_KEY *key) | ||||
| static int encrypt_sectors(BDRVQcowState *s, int64_t sector_num, | ||||
|                            uint8_t *out_buf, const uint8_t *in_buf, | ||||
|                            int nb_sectors, bool enc, Error **errp) | ||||
| { | ||||
|     union { | ||||
|         uint64_t ll[2]; | ||||
|         uint8_t b[16]; | ||||
|     } ivec; | ||||
|     int i; | ||||
|     int ret; | ||||
| 
 | ||||
|     for(i = 0; i < nb_sectors; i++) { | ||||
|         ivec.ll[0] = cpu_to_le64(sector_num); | ||||
|         ivec.ll[1] = 0; | ||||
|         AES_cbc_encrypt(in_buf, out_buf, 512, key, | ||||
|                         ivec.b, enc); | ||||
|         if (qcrypto_cipher_setiv(s->cipher, | ||||
|                                  ivec.b, G_N_ELEMENTS(ivec.b), | ||||
|                                  errp) < 0) { | ||||
|             return -1; | ||||
|         } | ||||
|         if (enc) { | ||||
|             ret = qcrypto_cipher_encrypt(s->cipher, | ||||
|                                          in_buf, | ||||
|                                          out_buf, | ||||
|                                          512, | ||||
|                                          errp); | ||||
|         } else { | ||||
|             ret = qcrypto_cipher_decrypt(s->cipher, | ||||
|                                          in_buf, | ||||
|                                          out_buf, | ||||
|                                          512, | ||||
|                                          errp); | ||||
|         } | ||||
|         if (ret < 0) { | ||||
|             return -1; | ||||
|         } | ||||
|         sector_num++; | ||||
|         in_buf += 512; | ||||
|         out_buf += 512; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| /* 'allocate' is:
 | ||||
| @ -416,15 +449,20 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | ||||
|                 if (bs->encrypted && | ||||
|                     (n_end - n_start) < s->cluster_sectors) { | ||||
|                     uint64_t start_sect; | ||||
|                     assert(s->crypt_method); | ||||
|                     assert(s->cipher); | ||||
|                     start_sect = (offset & ~(s->cluster_size - 1)) >> 9; | ||||
|                     memset(s->cluster_data + 512, 0x00, 512); | ||||
|                     for(i = 0; i < s->cluster_sectors; i++) { | ||||
|                         if (i < n_start || i >= n_end) { | ||||
|                             encrypt_sectors(s, start_sect + i, | ||||
|                                             s->cluster_data, | ||||
|                                             s->cluster_data + 512, 1, 1, | ||||
|                                             &s->aes_encrypt_key); | ||||
|                             Error *err = NULL; | ||||
|                             if (encrypt_sectors(s, start_sect + i, | ||||
|                                                 s->cluster_data, | ||||
|                                                 s->cluster_data + 512, 1, | ||||
|                                                 true, &err) < 0) { | ||||
|                                 error_free(err); | ||||
|                                 errno = EIO; | ||||
|                                 return -1; | ||||
|                             } | ||||
|                             if (bdrv_pwrite(bs->file, cluster_offset + i * 512, | ||||
|                                             s->cluster_data, 512) != 512) | ||||
|                                 return -1; | ||||
| @ -464,7 +502,7 @@ static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs, | ||||
|     if (!cluster_offset) { | ||||
|         return 0; | ||||
|     } | ||||
|     if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->crypt_method) { | ||||
|     if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->cipher) { | ||||
|         return BDRV_BLOCK_DATA; | ||||
|     } | ||||
|     cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS); | ||||
| @ -531,6 +569,7 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, | ||||
|     QEMUIOVector hd_qiov; | ||||
|     uint8_t *buf; | ||||
|     void *orig_buf; | ||||
|     Error *err = NULL; | ||||
| 
 | ||||
|     if (qiov->niov > 1) { | ||||
|         buf = orig_buf = qemu_try_blockalign(bs, qiov->size); | ||||
| @ -594,10 +633,11 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, | ||||
|                 break; | ||||
|             } | ||||
|             if (bs->encrypted) { | ||||
|                 assert(s->crypt_method); | ||||
|                 encrypt_sectors(s, sector_num, buf, buf, | ||||
|                                 n, 0, | ||||
|                                 &s->aes_decrypt_key); | ||||
|                 assert(s->cipher); | ||||
|                 if (encrypt_sectors(s, sector_num, buf, buf, | ||||
|                                     n, false, &err) < 0) { | ||||
|                     goto fail; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         ret = 0; | ||||
| @ -618,6 +658,7 @@ done: | ||||
|     return ret; | ||||
| 
 | ||||
| fail: | ||||
|     error_free(err); | ||||
|     ret = -EIO; | ||||
|     goto done; | ||||
| } | ||||
| @ -666,12 +707,17 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, | ||||
|             break; | ||||
|         } | ||||
|         if (bs->encrypted) { | ||||
|             assert(s->crypt_method); | ||||
|             Error *err = NULL; | ||||
|             assert(s->cipher); | ||||
|             if (!cluster_data) { | ||||
|                 cluster_data = g_malloc0(s->cluster_size); | ||||
|             } | ||||
|             encrypt_sectors(s, sector_num, cluster_data, buf, | ||||
|                             n, 1, &s->aes_encrypt_key); | ||||
|             if (encrypt_sectors(s, sector_num, cluster_data, buf, | ||||
|                                 n, true, &err) < 0) { | ||||
|                 error_free(err); | ||||
|                 ret = -EIO; | ||||
|                 break; | ||||
|             } | ||||
|             src_buf = cluster_data; | ||||
|         } else { | ||||
|             src_buf = buf; | ||||
| @ -708,6 +754,8 @@ static void qcow_close(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
| 
 | ||||
|     qcrypto_cipher_free(s->cipher); | ||||
|     s->cipher = NULL; | ||||
|     g_free(s->l1_table); | ||||
|     qemu_vfree(s->l2_cache); | ||||
|     g_free(s->cluster_cache); | ||||
|  | ||||
| @ -339,26 +339,47 @@ static int count_contiguous_free_clusters(uint64_t nb_clusters, uint64_t *l2_tab | ||||
| /* The crypt function is compatible with the linux cryptoloop
 | ||||
|    algorithm for < 4 GB images. NOTE: out_buf == in_buf is | ||||
|    supported */ | ||||
| void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, | ||||
|                            uint8_t *out_buf, const uint8_t *in_buf, | ||||
|                            int nb_sectors, int enc, | ||||
|                            const AES_KEY *key) | ||||
| int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, | ||||
|                           uint8_t *out_buf, const uint8_t *in_buf, | ||||
|                           int nb_sectors, bool enc, | ||||
|                           Error **errp) | ||||
| { | ||||
|     union { | ||||
|         uint64_t ll[2]; | ||||
|         uint8_t b[16]; | ||||
|     } ivec; | ||||
|     int i; | ||||
|     int ret; | ||||
| 
 | ||||
|     for(i = 0; i < nb_sectors; i++) { | ||||
|         ivec.ll[0] = cpu_to_le64(sector_num); | ||||
|         ivec.ll[1] = 0; | ||||
|         AES_cbc_encrypt(in_buf, out_buf, 512, key, | ||||
|                         ivec.b, enc); | ||||
|         if (qcrypto_cipher_setiv(s->cipher, | ||||
|                                  ivec.b, G_N_ELEMENTS(ivec.b), | ||||
|                                  errp) < 0) { | ||||
|             return -1; | ||||
|         } | ||||
|         if (enc) { | ||||
|             ret = qcrypto_cipher_encrypt(s->cipher, | ||||
|                                          in_buf, | ||||
|                                          out_buf, | ||||
|                                          512, | ||||
|                                          errp); | ||||
|         } else { | ||||
|             ret = qcrypto_cipher_decrypt(s->cipher, | ||||
|                                          in_buf, | ||||
|                                          out_buf, | ||||
|                                          512, | ||||
|                                          errp); | ||||
|         } | ||||
|         if (ret < 0) { | ||||
|             return -1; | ||||
|         } | ||||
|         sector_num++; | ||||
|         in_buf += 512; | ||||
|         out_buf += 512; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int coroutine_fn copy_sectors(BlockDriverState *bs, | ||||
| @ -401,10 +422,15 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs, | ||||
|     } | ||||
| 
 | ||||
|     if (bs->encrypted) { | ||||
|         assert(s->crypt_method); | ||||
|         qcow2_encrypt_sectors(s, start_sect + n_start, | ||||
|                         iov.iov_base, iov.iov_base, n, 1, | ||||
|                         &s->aes_encrypt_key); | ||||
|         Error *err = NULL; | ||||
|         assert(s->cipher); | ||||
|         if (qcow2_encrypt_sectors(s, start_sect + n_start, | ||||
|                                   iov.iov_base, iov.iov_base, n, | ||||
|                                   true, &err) < 0) { | ||||
|             ret = -EIO; | ||||
|             error_free(err); | ||||
|             goto out; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ret = qcow2_pre_write_overlap_check(bs, 0, | ||||
|  | ||||
| @ -25,7 +25,6 @@ | ||||
| #include "block/block_int.h" | ||||
| #include "qemu/module.h" | ||||
| #include <zlib.h> | ||||
| #include "qemu/aes.h" | ||||
| #include "block/qcow2.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "qapi/qmp/qerror.h" | ||||
| @ -699,6 +698,11 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|     } | ||||
|     if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128)) { | ||||
|         error_setg(errp, "AES cipher not available"); | ||||
|         ret = -EINVAL; | ||||
|         goto fail; | ||||
|     } | ||||
|     s->crypt_method_header = header.crypt_method; | ||||
|     if (s->crypt_method_header) { | ||||
|         bs->encrypted = 1; | ||||
| @ -1032,6 +1036,7 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key) | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     uint8_t keybuf[16]; | ||||
|     int len, i; | ||||
|     Error *err = NULL; | ||||
| 
 | ||||
|     memset(keybuf, 0, 16); | ||||
|     len = strlen(key); | ||||
| @ -1043,30 +1048,21 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key) | ||||
|         keybuf[i] = key[i]; | ||||
|     } | ||||
|     assert(bs->encrypted); | ||||
|     s->crypt_method = s->crypt_method_header; | ||||
| 
 | ||||
|     if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0) | ||||
|     qcrypto_cipher_free(s->cipher); | ||||
|     s->cipher = qcrypto_cipher_new( | ||||
|         QCRYPTO_CIPHER_ALG_AES_128, | ||||
|         QCRYPTO_CIPHER_MODE_CBC, | ||||
|         keybuf, G_N_ELEMENTS(keybuf), | ||||
|         &err); | ||||
| 
 | ||||
|     if (!s->cipher) { | ||||
|         /* XXX would be nice if errors in this method could
 | ||||
|          * be properly propagate to the caller. Would need | ||||
|          * the bdrv_set_key() API signature to be fixed. */ | ||||
|         error_free(err); | ||||
|         return -1; | ||||
|     if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0) | ||||
|         return -1; | ||||
| #if 0 | ||||
|     /* test */ | ||||
|     { | ||||
|         uint8_t in[16]; | ||||
|         uint8_t out[16]; | ||||
|         uint8_t tmp[16]; | ||||
|         for(i=0;i<16;i++) | ||||
|             in[i] = i; | ||||
|         AES_encrypt(in, tmp, &s->aes_encrypt_key); | ||||
|         AES_decrypt(tmp, out, &s->aes_decrypt_key); | ||||
|         for(i = 0; i < 16; i++) | ||||
|             printf(" %02x", tmp[i]); | ||||
|         printf("\n"); | ||||
|         for(i = 0; i < 16; i++) | ||||
|             printf(" %02x", out[i]); | ||||
|         printf("\n"); | ||||
|     } | ||||
| #endif | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| @ -1109,7 +1105,7 @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs, | ||||
|     } | ||||
| 
 | ||||
|     if (cluster_offset != 0 && ret != QCOW2_CLUSTER_COMPRESSED && | ||||
|         !s->crypt_method) { | ||||
|         !s->cipher) { | ||||
|         index_in_cluster = sector_num & (s->cluster_sectors - 1); | ||||
|         cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS); | ||||
|         status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset; | ||||
| @ -1159,7 +1155,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, | ||||
| 
 | ||||
|         /* prepare next request */ | ||||
|         cur_nr_sectors = remaining_sectors; | ||||
|         if (s->crypt_method) { | ||||
|         if (s->cipher) { | ||||
|             cur_nr_sectors = MIN(cur_nr_sectors, | ||||
|                 QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors); | ||||
|         } | ||||
| @ -1231,7 +1227,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, | ||||
|             } | ||||
| 
 | ||||
|             if (bs->encrypted) { | ||||
|                 assert(s->crypt_method); | ||||
|                 assert(s->cipher); | ||||
| 
 | ||||
|                 /*
 | ||||
|                  * For encrypted images, read everything into a temporary | ||||
| @ -1264,9 +1260,15 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, | ||||
|                 goto fail; | ||||
|             } | ||||
|             if (bs->encrypted) { | ||||
|                 assert(s->crypt_method); | ||||
|                 qcow2_encrypt_sectors(s, sector_num,  cluster_data, | ||||
|                     cluster_data, cur_nr_sectors, 0, &s->aes_decrypt_key); | ||||
|                 assert(s->cipher); | ||||
|                 Error *err = NULL; | ||||
|                 if (qcow2_encrypt_sectors(s, sector_num,  cluster_data, | ||||
|                                           cluster_data, cur_nr_sectors, false, | ||||
|                                           &err) < 0) { | ||||
|                     error_free(err); | ||||
|                     ret = -EIO; | ||||
|                     goto fail; | ||||
|                 } | ||||
|                 qemu_iovec_from_buf(qiov, bytes_done, | ||||
|                     cluster_data, 512 * cur_nr_sectors); | ||||
|             } | ||||
| @ -1344,7 +1346,8 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs, | ||||
|             cur_nr_sectors * 512); | ||||
| 
 | ||||
|         if (bs->encrypted) { | ||||
|             assert(s->crypt_method); | ||||
|             Error *err = NULL; | ||||
|             assert(s->cipher); | ||||
|             if (!cluster_data) { | ||||
|                 cluster_data = qemu_try_blockalign(bs->file, | ||||
|                                                    QCOW_MAX_CRYPT_CLUSTERS | ||||
| @ -1359,8 +1362,13 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs, | ||||
|                    QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); | ||||
|             qemu_iovec_to_buf(&hd_qiov, 0, cluster_data, hd_qiov.size); | ||||
| 
 | ||||
|             qcow2_encrypt_sectors(s, sector_num, cluster_data, | ||||
|                 cluster_data, cur_nr_sectors, 1, &s->aes_encrypt_key); | ||||
|             if (qcow2_encrypt_sectors(s, sector_num, cluster_data, | ||||
|                                       cluster_data, cur_nr_sectors, | ||||
|                                       true, &err) < 0) { | ||||
|                 error_free(err); | ||||
|                 ret = -EIO; | ||||
|                 goto fail; | ||||
|             } | ||||
| 
 | ||||
|             qemu_iovec_reset(&hd_qiov); | ||||
|             qemu_iovec_add(&hd_qiov, cluster_data, | ||||
| @ -1466,6 +1474,9 @@ static void qcow2_close(BlockDriverState *bs) | ||||
|     qcow2_cache_destroy(bs, s->l2_table_cache); | ||||
|     qcow2_cache_destroy(bs, s->refcount_block_cache); | ||||
| 
 | ||||
|     qcrypto_cipher_free(s->cipher); | ||||
|     s->cipher = NULL; | ||||
| 
 | ||||
|     g_free(s->unknown_header_fields); | ||||
|     cleanup_unknown_header_ext(bs); | ||||
| 
 | ||||
| @ -1482,9 +1493,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int flags = s->flags; | ||||
|     AES_KEY aes_encrypt_key; | ||||
|     AES_KEY aes_decrypt_key; | ||||
|     uint32_t crypt_method = 0; | ||||
|     QCryptoCipher *cipher = NULL; | ||||
|     QDict *options; | ||||
|     Error *local_err = NULL; | ||||
|     int ret; | ||||
| @ -1494,12 +1503,8 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp) | ||||
|      * that means we don't have to worry about reopening them here. | ||||
|      */ | ||||
| 
 | ||||
|     if (bs->encrypted) { | ||||
|         assert(s->crypt_method); | ||||
|         crypt_method = s->crypt_method; | ||||
|         memcpy(&aes_encrypt_key, &s->aes_encrypt_key, sizeof(aes_encrypt_key)); | ||||
|         memcpy(&aes_decrypt_key, &s->aes_decrypt_key, sizeof(aes_decrypt_key)); | ||||
|     } | ||||
|     cipher = s->cipher; | ||||
|     s->cipher = NULL; | ||||
| 
 | ||||
|     qcow2_close(bs); | ||||
| 
 | ||||
| @ -1524,11 +1529,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp) | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (bs->encrypted) { | ||||
|         s->crypt_method = crypt_method; | ||||
|         memcpy(&s->aes_encrypt_key, &aes_encrypt_key, sizeof(aes_encrypt_key)); | ||||
|         memcpy(&s->aes_decrypt_key, &aes_decrypt_key, sizeof(aes_decrypt_key)); | ||||
|     } | ||||
|     s->cipher = cipher; | ||||
| } | ||||
| 
 | ||||
| static size_t header_ext_add(char *buf, uint32_t magic, const void *s, | ||||
| @ -2729,8 +2730,9 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, | ||||
|             backing_format = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT); | ||||
|         } else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT)) { | ||||
|             encrypt = qemu_opt_get_bool(opts, BLOCK_OPT_ENCRYPT, | ||||
|                                         s->crypt_method); | ||||
|             if (encrypt != !!s->crypt_method) { | ||||
|                                         !!s->cipher); | ||||
| 
 | ||||
|             if (encrypt != !!s->cipher) { | ||||
|                 fprintf(stderr, "Changing the encryption flag is not " | ||||
|                         "supported.\n"); | ||||
|                 return -ENOTSUP; | ||||
|  | ||||
| @ -25,7 +25,7 @@ | ||||
| #ifndef BLOCK_QCOW2_H | ||||
| #define BLOCK_QCOW2_H | ||||
| 
 | ||||
| #include "qemu/aes.h" | ||||
| #include "crypto/cipher.h" | ||||
| #include "block/coroutine.h" | ||||
| 
 | ||||
| //#define DEBUG_ALLOC
 | ||||
| @ -253,10 +253,8 @@ typedef struct BDRVQcowState { | ||||
| 
 | ||||
|     CoMutex lock; | ||||
| 
 | ||||
|     uint32_t crypt_method; /* current crypt method, 0 if no key yet */ | ||||
|     QCryptoCipher *cipher; /* current cipher, NULL if no key yet */ | ||||
|     uint32_t crypt_method_header; | ||||
|     AES_KEY aes_encrypt_key; | ||||
|     AES_KEY aes_decrypt_key; | ||||
|     uint64_t snapshots_offset; | ||||
|     int snapshots_size; | ||||
|     unsigned int nb_snapshots; | ||||
| @ -536,10 +534,9 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, | ||||
| int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index); | ||||
| void qcow2_l2_cache_reset(BlockDriverState *bs); | ||||
| int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); | ||||
| void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, | ||||
|                      uint8_t *out_buf, const uint8_t *in_buf, | ||||
|                      int nb_sectors, int enc, | ||||
|                      const AES_KEY *key); | ||||
| int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, | ||||
|                           uint8_t *out_buf, const uint8_t *in_buf, | ||||
|                           int nb_sectors, bool enc, Error **errp); | ||||
| 
 | ||||
| int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||||
|     int *num, uint64_t *cluster_offset); | ||||
|  | ||||
| @ -13,8 +13,6 @@ | ||||
|  * See the COPYING file in the top-level directory. | ||||
|  */ | ||||
| 
 | ||||
| #include <gnutls/gnutls.h> | ||||
| #include <gnutls/crypto.h> | ||||
| #include "block/block_int.h" | ||||
| #include "qapi/qmp/qbool.h" | ||||
| #include "qapi/qmp/qdict.h" | ||||
| @ -24,6 +22,7 @@ | ||||
| #include "qapi/qmp/qlist.h" | ||||
| #include "qapi/qmp/qstring.h" | ||||
| #include "qapi-event.h" | ||||
| #include "crypto/hash.h" | ||||
| 
 | ||||
| #define HASH_LENGTH 32 | ||||
| 
 | ||||
| @ -34,7 +33,7 @@ | ||||
| 
 | ||||
| /* This union holds a vote hash value */ | ||||
| typedef union QuorumVoteValue { | ||||
|     char h[HASH_LENGTH];       /* SHA-256 hash */ | ||||
|     uint8_t h[HASH_LENGTH];    /* SHA-256 hash */ | ||||
|     int64_t l;                 /* simpler 64 bits hash */ | ||||
| } QuorumVoteValue; | ||||
| 
 | ||||
| @ -428,25 +427,21 @@ static void quorum_free_vote_list(QuorumVotes *votes) | ||||
| 
 | ||||
| static int quorum_compute_hash(QuorumAIOCB *acb, int i, QuorumVoteValue *hash) | ||||
| { | ||||
|     int j, ret; | ||||
|     gnutls_hash_hd_t dig; | ||||
|     QEMUIOVector *qiov = &acb->qcrs[i].qiov; | ||||
|     size_t len = sizeof(hash->h); | ||||
|     uint8_t *data = hash->h; | ||||
| 
 | ||||
|     ret = gnutls_hash_init(&dig, GNUTLS_DIG_SHA256); | ||||
| 
 | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     /* XXX - would be nice if we could pass in the Error **
 | ||||
|      * and propagate that back, but this quorum code is | ||||
|      * restricted to just errno values currently */ | ||||
|     if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALG_SHA256, | ||||
|                             qiov->iov, qiov->niov, | ||||
|                             &data, &len, | ||||
|                             NULL) < 0) { | ||||
|         return -EINVAL; | ||||
|     } | ||||
| 
 | ||||
|     for (j = 0; j < qiov->niov; j++) { | ||||
|         ret = gnutls_hash(dig, qiov->iov[j].iov_base, qiov->iov[j].iov_len); | ||||
|         if (ret < 0) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     gnutls_hash_deinit(dig, (void *) hash); | ||||
|     return ret; | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static QuorumVoteVersion *quorum_get_vote_winner(QuorumVotes *votes) | ||||
| @ -870,6 +865,12 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, | ||||
|     int i; | ||||
|     int ret = 0; | ||||
| 
 | ||||
|     if (!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA256)) { | ||||
|         error_setg(errp, | ||||
|                    "SHA256 hash support is required for quorum device"); | ||||
|         return -EINVAL; | ||||
|     } | ||||
| 
 | ||||
|     qdict_flatten(options); | ||||
| 
 | ||||
|     /* count how many different children are present */ | ||||
|  | ||||
							
								
								
									
										160
									
								
								configure
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										160
									
								
								configure
									
									
									
									
										vendored
									
									
								
							| @ -246,7 +246,6 @@ vnc_tls="" | ||||
| vnc_sasl="" | ||||
| vnc_jpeg="" | ||||
| vnc_png="" | ||||
| vnc_ws="" | ||||
| xen="" | ||||
| xen_ctrl_version="" | ||||
| xen_pci_passthrough="" | ||||
| @ -331,11 +330,12 @@ glusterfs_zerofill="no" | ||||
| archipelago="no" | ||||
| gtk="" | ||||
| gtkabi="" | ||||
| gnutls="" | ||||
| gnutls_hash="" | ||||
| vte="" | ||||
| tpm="yes" | ||||
| libssh2="" | ||||
| vhdx="" | ||||
| quorum="" | ||||
| numa="" | ||||
| tcmalloc="no" | ||||
| 
 | ||||
| @ -896,10 +896,6 @@ for opt do | ||||
|   ;; | ||||
|   --enable-vnc-png) vnc_png="yes" | ||||
|   ;; | ||||
|   --disable-vnc-ws) vnc_ws="no" | ||||
|   ;; | ||||
|   --enable-vnc-ws) vnc_ws="yes" | ||||
|   ;; | ||||
|   --disable-slirp) slirp="no" | ||||
|   ;; | ||||
|   --disable-uuid) uuid="no" | ||||
| @ -1119,6 +1115,10 @@ for opt do | ||||
|   ;; | ||||
|   --enable-gtk) gtk="yes" | ||||
|   ;; | ||||
|   --disable-gnutls) gnutls="no" | ||||
|   ;; | ||||
|   --enable-gnutls) gnutls="yes" | ||||
|   ;; | ||||
|   --enable-rdma) rdma="yes" | ||||
|   ;; | ||||
|   --disable-rdma) rdma="no" | ||||
| @ -1141,10 +1141,6 @@ for opt do | ||||
|   ;; | ||||
|   --disable-vhdx) vhdx="no" | ||||
|   ;; | ||||
|   --disable-quorum) quorum="no" | ||||
|   ;; | ||||
|   --enable-quorum) quorum="yes" | ||||
|   ;; | ||||
|   --disable-numa) numa="no" | ||||
|   ;; | ||||
|   --enable-numa) numa="yes" | ||||
| @ -1329,6 +1325,7 @@ disabled with --disable-FEATURE, default is enabled if available: | ||||
|   debug-info      debugging information | ||||
|   sparse          sparse checker | ||||
| 
 | ||||
|   gnutls          GNUTLS cryptography support | ||||
|   sdl             SDL UI | ||||
|   --with-sdlabi     select preferred SDL ABI 1.2 or 2.0 | ||||
|   gtk             gtk UI | ||||
| @ -1376,7 +1373,6 @@ disabled with --disable-FEATURE, default is enabled if available: | ||||
|   tpm             TPM support | ||||
|   libssh2         ssh block device support | ||||
|   vhdx            support for the Microsoft VHDX image format | ||||
|   quorum          quorum block filter support | ||||
|   numa            libnuma support | ||||
|   tcmalloc        tcmalloc support | ||||
| 
 | ||||
| @ -2116,6 +2112,86 @@ if test "$gtk" != "no"; then | ||||
|     fi | ||||
| fi | ||||
| 
 | ||||
| 
 | ||||
| ########################################## | ||||
| # GNUTLS probe | ||||
| 
 | ||||
| gnutls_gcrypt=no | ||||
| gnutls_nettle=no | ||||
| if test "$gnutls" != "no"; then | ||||
|     if $pkg_config --exists "gnutls"; then | ||||
|         gnutls_cflags=`$pkg_config --cflags gnutls` | ||||
|         gnutls_libs=`$pkg_config --libs gnutls` | ||||
|         libs_softmmu="$gnutls_libs $libs_softmmu" | ||||
|         libs_tools="$gnutls_libs $libs_tools" | ||||
| 	QEMU_CFLAGS="$QEMU_CFLAGS $gnutls_cflags" | ||||
|         gnutls="yes" | ||||
| 
 | ||||
| 	# gnutls_hash_init requires >= 2.9.10 | ||||
| 	if $pkg_config --exists "gnutls >= 2.9.10"; then | ||||
|             gnutls_hash="yes" | ||||
| 	else | ||||
| 	    gnutls_hash="no" | ||||
| 	fi | ||||
| 
 | ||||
| 	if $pkg_config --exists 'gnutls >= 3.0'; then | ||||
| 	    gnutls_gcrypt=no | ||||
| 	    gnutls_nettle=yes | ||||
| 	elif $pkg_config --exists 'gnutls >= 2.12'; then | ||||
| 	    case `$pkg_config --libs --static gnutls` in | ||||
| 		*gcrypt*) | ||||
| 		    gnutls_gcrypt=yes | ||||
| 		    gnutls_nettle=no | ||||
| 		    ;; | ||||
| 		*nettle*) | ||||
| 		    gnutls_gcrypt=no | ||||
| 		    gnutls_nettle=yes | ||||
| 		    ;; | ||||
| 		*) | ||||
| 		    gnutls_gcrypt=yes | ||||
| 		    gnutls_nettle=no | ||||
| 		    ;; | ||||
| 	    esac | ||||
| 	else | ||||
| 	    gnutls_gcrypt=yes | ||||
| 	    gnutls_nettle=no | ||||
| 	fi | ||||
|     elif test "$gnutls" = "yes"; then | ||||
| 	feature_not_found "gnutls" "Install gnutls devel" | ||||
|     else | ||||
|         gnutls="no" | ||||
|         gnutls_hash="no" | ||||
|     fi | ||||
| else | ||||
|     gnutls_hash="no" | ||||
| fi | ||||
| 
 | ||||
| if test "$gnutls_gcrypt" != "no"; then | ||||
|     if has "libgcrypt-config"; then | ||||
|         gcrypt_cflags=`libgcrypt-config --cflags` | ||||
|         gcrypt_libs=`libgcrypt-config --libs` | ||||
|         libs_softmmu="$gcrypt_libs $libs_softmmu" | ||||
|         libs_tools="$gcrypt_libs $libs_tools" | ||||
|         QEMU_CFLAGS="$QEMU_CFLAGS $gcrypt_cflags" | ||||
|     else | ||||
|         feature_not_found "gcrypt" "Install gcrypt devel" | ||||
|     fi | ||||
| fi | ||||
| 
 | ||||
| 
 | ||||
| if test "$gnutls_nettle" != "no"; then | ||||
|     if $pkg_config --exists "nettle"; then | ||||
|         nettle_cflags=`$pkg_config --cflags nettle` | ||||
|         nettle_libs=`$pkg_config --libs nettle` | ||||
|         libs_softmmu="$nettle_libs $libs_softmmu" | ||||
|         libs_tools="$nettle_libs $libs_tools" | ||||
|         QEMU_CFLAGS="$QEMU_CFLAGS $nettle_cflags" | ||||
|     else | ||||
|         feature_not_found "nettle" "Install nettle devel" | ||||
|     fi | ||||
| fi | ||||
| 
 | ||||
| 
 | ||||
| ########################################## | ||||
| # VTE probe | ||||
| 
 | ||||
| @ -2263,7 +2339,7 @@ fi | ||||
| 
 | ||||
| ########################################## | ||||
| # VNC TLS/WS detection | ||||
| if test "$vnc" = "yes" -a \( "$vnc_tls" != "no" -o "$vnc_ws" != "no" \) ; then | ||||
| if test "$vnc" = "yes" -a "$vnc_tls" != "no" ; then | ||||
|   cat > $TMPC <<EOF | ||||
| #include <gnutls/gnutls.h> | ||||
| int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; } | ||||
| @ -2274,50 +2350,16 @@ EOF | ||||
|     if test "$vnc_tls" != "no" ; then | ||||
|       vnc_tls=yes | ||||
|     fi | ||||
|     if test "$vnc_ws" != "no" ; then | ||||
|       vnc_ws=yes | ||||
|     fi | ||||
|     libs_softmmu="$vnc_tls_libs $libs_softmmu" | ||||
|     QEMU_CFLAGS="$QEMU_CFLAGS $vnc_tls_cflags" | ||||
|   else | ||||
|     if test "$vnc_tls" = "yes" ; then | ||||
|       feature_not_found "vnc-tls" "Install gnutls devel" | ||||
|     fi | ||||
|     if test "$vnc_ws" = "yes" ; then | ||||
|       feature_not_found "vnc-ws" "Install gnutls devel" | ||||
|     fi | ||||
|     vnc_tls=no | ||||
|     vnc_ws=no | ||||
|   fi | ||||
| fi | ||||
| 
 | ||||
| ########################################## | ||||
| # Quorum probe (check for gnutls) | ||||
| if test "$quorum" != "no" ; then | ||||
| cat > $TMPC <<EOF | ||||
| #include <gnutls/gnutls.h> | ||||
| #include <gnutls/crypto.h> | ||||
| int main(void) {char data[4096], digest[32]; | ||||
| gnutls_hash_fast(GNUTLS_DIG_SHA256, data, 4096, digest); | ||||
| return 0; | ||||
| } | ||||
| EOF | ||||
| quorum_tls_cflags=`$pkg_config --cflags gnutls 2> /dev/null` | ||||
| quorum_tls_libs=`$pkg_config --libs gnutls 2> /dev/null` | ||||
| if compile_prog "$quorum_tls_cflags" "$quorum_tls_libs" ; then | ||||
|   qcow_tls=yes | ||||
|   libs_softmmu="$quorum_tls_libs $libs_softmmu" | ||||
|   libs_tools="$quorum_tls_libs $libs_softmmu" | ||||
|   QEMU_CFLAGS="$QEMU_CFLAGS $quorum_tls_cflags" | ||||
|   quorum="yes" | ||||
| else | ||||
|   if test "$quorum" = "yes"; then | ||||
|     feature_not_found "gnutls" "gnutls > 2.10.0 required to compile Quorum" | ||||
|   fi | ||||
|   quorum="no" | ||||
| fi | ||||
| fi | ||||
| 
 | ||||
| ########################################## | ||||
| # VNC SASL detection | ||||
| if test "$vnc" = "yes" -a "$vnc_sasl" != "no" ; then | ||||
| @ -4445,6 +4487,10 @@ fi | ||||
| echo "pixman            $pixman" | ||||
| echo "SDL support       $sdl" | ||||
| echo "GTK support       $gtk" | ||||
| echo "GNUTLS support    $gnutls" | ||||
| echo "GNUTLS hash       $gnutls_hash" | ||||
| echo "GNUTLS gcrypt     $gnutls_gcrypt" | ||||
| echo "GNUTLS nettle     $gnutls_nettle" | ||||
| echo "VTE support       $vte" | ||||
| echo "curses support    $curses" | ||||
| echo "curl support      $curl" | ||||
| @ -4459,7 +4505,6 @@ if test "$vnc" = "yes" ; then | ||||
|     echo "VNC SASL support  $vnc_sasl" | ||||
|     echo "VNC JPEG support  $vnc_jpeg" | ||||
|     echo "VNC PNG support   $vnc_png" | ||||
|     echo "VNC WS support    $vnc_ws" | ||||
| fi | ||||
| if test -n "$sparc_cpu"; then | ||||
|     echo "Target Sparc Arch $sparc_cpu" | ||||
| @ -4523,7 +4568,6 @@ echo "libssh2 support   $libssh2" | ||||
| echo "TPM passthrough   $tpm_passthrough" | ||||
| echo "QOM debugging     $qom_cast_debug" | ||||
| echo "vhdx              $vhdx" | ||||
| echo "Quorum            $quorum" | ||||
| echo "lzo support       $lzo" | ||||
| echo "snappy support    $snappy" | ||||
| echo "bzip2 support     $bzip2" | ||||
| @ -4676,10 +4720,6 @@ fi | ||||
| if test "$vnc_png" = "yes" ; then | ||||
|   echo "CONFIG_VNC_PNG=y" >> $config_host_mak | ||||
| fi | ||||
| if test "$vnc_ws" = "yes" ; then | ||||
|   echo "CONFIG_VNC_WS=y" >> $config_host_mak | ||||
|   echo "VNC_WS_CFLAGS=$vnc_ws_cflags" >> $config_host_mak | ||||
| fi | ||||
| if test "$fnmatch" = "yes" ; then | ||||
|   echo "CONFIG_FNMATCH=y" >> $config_host_mak | ||||
| fi | ||||
| @ -4807,6 +4847,18 @@ if test "$gtk" = "yes" ; then | ||||
|   echo "CONFIG_GTKABI=$gtkabi" >> $config_host_mak | ||||
|   echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak | ||||
| fi | ||||
| if test "$gnutls" = "yes" ; then | ||||
|   echo "CONFIG_GNUTLS=y" >> $config_host_mak | ||||
| fi | ||||
| if test "$gnutls_hash" = "yes" ; then | ||||
|   echo "CONFIG_GNUTLS_HASH=y" >> $config_host_mak | ||||
| fi | ||||
| if test "$gnutls_gcrypt" = "yes" ; then | ||||
|   echo "CONFIG_GNUTLS_GCRYPT=y" >> $config_host_mak | ||||
| fi | ||||
| if test "$gnutls_nettle" = "yes" ; then | ||||
|   echo "CONFIG_GNUTLS_NETTLE=y" >> $config_host_mak | ||||
| fi | ||||
| if test "$vte" = "yes" ; then | ||||
|   echo "CONFIG_VTE=y" >> $config_host_mak | ||||
|   echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak | ||||
| @ -4996,10 +5048,6 @@ if test "$libssh2" = "yes" ; then | ||||
|   echo "LIBSSH2_LIBS=$libssh2_libs" >> $config_host_mak | ||||
| fi | ||||
| 
 | ||||
| if test "$quorum" = "yes" ; then | ||||
|   echo "CONFIG_QUORUM=y" >> $config_host_mak | ||||
| fi | ||||
| 
 | ||||
| if test "$vhdx" = "yes" ; then | ||||
|   echo "CONFIG_VHDX=y" >> $config_host_mak | ||||
| fi | ||||
|  | ||||
							
								
								
									
										5
									
								
								crypto/Makefile.objs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								crypto/Makefile.objs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| util-obj-y += init.o | ||||
| util-obj-y += hash.o | ||||
| util-obj-y += aes.o | ||||
| util-obj-y += desrfb.o | ||||
| util-obj-y += cipher.o | ||||
| @ -28,7 +28,7 @@ | ||||
|  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  */ | ||||
| #include "qemu-common.h" | ||||
| #include "qemu/aes.h" | ||||
| #include "crypto/aes.h" | ||||
| 
 | ||||
| typedef uint32_t u32; | ||||
| typedef uint8_t u8; | ||||
							
								
								
									
										398
									
								
								crypto/cipher-builtin.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										398
									
								
								crypto/cipher-builtin.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,398 @@ | ||||
| /*
 | ||||
|  * QEMU Crypto cipher built-in algorithms | ||||
|  * | ||||
|  * Copyright (c) 2015 Red Hat, Inc. | ||||
|  * | ||||
|  * 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 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 "crypto/aes.h" | ||||
| #include "crypto/desrfb.h" | ||||
| 
 | ||||
| typedef struct QCryptoCipherBuiltinAES QCryptoCipherBuiltinAES; | ||||
| struct QCryptoCipherBuiltinAES { | ||||
|     AES_KEY encrypt_key; | ||||
|     AES_KEY decrypt_key; | ||||
|     uint8_t *iv; | ||||
|     size_t niv; | ||||
| }; | ||||
| typedef struct QCryptoCipherBuiltinDESRFB QCryptoCipherBuiltinDESRFB; | ||||
| struct QCryptoCipherBuiltinDESRFB { | ||||
|     uint8_t *key; | ||||
|     size_t nkey; | ||||
| }; | ||||
| 
 | ||||
| typedef struct QCryptoCipherBuiltin QCryptoCipherBuiltin; | ||||
| struct QCryptoCipherBuiltin { | ||||
|     union { | ||||
|         QCryptoCipherBuiltinAES aes; | ||||
|         QCryptoCipherBuiltinDESRFB desrfb; | ||||
|     } state; | ||||
|     void (*free)(QCryptoCipher *cipher); | ||||
|     int (*setiv)(QCryptoCipher *cipher, | ||||
|                  const uint8_t *iv, size_t niv, | ||||
|                  Error **errp); | ||||
|     int (*encrypt)(QCryptoCipher *cipher, | ||||
|                    const void *in, | ||||
|                    void *out, | ||||
|                    size_t len, | ||||
|                    Error **errp); | ||||
|     int (*decrypt)(QCryptoCipher *cipher, | ||||
|                    const void *in, | ||||
|                    void *out, | ||||
|                    size_t len, | ||||
|                    Error **errp); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static void qcrypto_cipher_free_aes(QCryptoCipher *cipher) | ||||
| { | ||||
|     QCryptoCipherBuiltin *ctxt = cipher->opaque; | ||||
| 
 | ||||
|     g_free(ctxt->state.aes.iv); | ||||
|     g_free(ctxt); | ||||
|     cipher->opaque = NULL; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int qcrypto_cipher_encrypt_aes(QCryptoCipher *cipher, | ||||
|                                       const void *in, | ||||
|                                       void *out, | ||||
|                                       size_t len, | ||||
|                                       Error **errp) | ||||
| { | ||||
|     QCryptoCipherBuiltin *ctxt = cipher->opaque; | ||||
| 
 | ||||
|     if (cipher->mode == QCRYPTO_CIPHER_MODE_ECB) { | ||||
|         const uint8_t *inptr = in; | ||||
|         uint8_t *outptr = out; | ||||
|         while (len) { | ||||
|             if (len > AES_BLOCK_SIZE) { | ||||
|                 AES_encrypt(inptr, outptr, &ctxt->state.aes.encrypt_key); | ||||
|                 inptr += AES_BLOCK_SIZE; | ||||
|                 outptr += AES_BLOCK_SIZE; | ||||
|                 len -= AES_BLOCK_SIZE; | ||||
|             } else { | ||||
|                 uint8_t tmp1[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE]; | ||||
|                 memcpy(tmp1, inptr, len); | ||||
|                 /* Fill with 0 to avoid valgrind uninitialized reads */ | ||||
|                 memset(tmp1 + len, 0, sizeof(tmp1) - len); | ||||
|                 AES_encrypt(tmp1, tmp2, &ctxt->state.aes.encrypt_key); | ||||
|                 memcpy(outptr, tmp2, len); | ||||
|                 len = 0; | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
|         AES_cbc_encrypt(in, out, len, | ||||
|                         &ctxt->state.aes.encrypt_key, | ||||
|                         ctxt->state.aes.iv, 1); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int qcrypto_cipher_decrypt_aes(QCryptoCipher *cipher, | ||||
|                                       const void *in, | ||||
|                                       void *out, | ||||
|                                       size_t len, | ||||
|                                       Error **errp) | ||||
| { | ||||
|     QCryptoCipherBuiltin *ctxt = cipher->opaque; | ||||
| 
 | ||||
|     if (cipher->mode == QCRYPTO_CIPHER_MODE_ECB) { | ||||
|         const uint8_t *inptr = in; | ||||
|         uint8_t *outptr = out; | ||||
|         while (len) { | ||||
|             if (len > AES_BLOCK_SIZE) { | ||||
|                 AES_decrypt(inptr, outptr, &ctxt->state.aes.encrypt_key); | ||||
|                 inptr += AES_BLOCK_SIZE; | ||||
|                 outptr += AES_BLOCK_SIZE; | ||||
|                 len -= AES_BLOCK_SIZE; | ||||
|             } else { | ||||
|                 uint8_t tmp1[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE]; | ||||
|                 memcpy(tmp1, inptr, len); | ||||
|                 /* Fill with 0 to avoid valgrind uninitialized reads */ | ||||
|                 memset(tmp1 + len, 0, sizeof(tmp1) - len); | ||||
|                 AES_decrypt(tmp1, tmp2, &ctxt->state.aes.encrypt_key); | ||||
|                 memcpy(outptr, tmp2, len); | ||||
|                 len = 0; | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
|         AES_cbc_encrypt(in, out, len, | ||||
|                         &ctxt->state.aes.encrypt_key, | ||||
|                         ctxt->state.aes.iv, 1); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int qcrypto_cipher_setiv_aes(QCryptoCipher *cipher, | ||||
|                                      const uint8_t *iv, size_t niv, | ||||
|                                      Error **errp) | ||||
| { | ||||
|     QCryptoCipherBuiltin *ctxt = cipher->opaque; | ||||
|     if (niv != 16) { | ||||
|         error_setg(errp, "IV must be 16 bytes not %zu", niv); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     g_free(ctxt->state.aes.iv); | ||||
|     ctxt->state.aes.iv = g_new0(uint8_t, niv); | ||||
|     memcpy(ctxt->state.aes.iv, iv, niv); | ||||
|     ctxt->state.aes.niv = niv; | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| static int qcrypto_cipher_init_aes(QCryptoCipher *cipher, | ||||
|                                    const uint8_t *key, size_t nkey, | ||||
|                                    Error **errp) | ||||
| { | ||||
|     QCryptoCipherBuiltin *ctxt; | ||||
| 
 | ||||
|     if (cipher->mode != QCRYPTO_CIPHER_MODE_CBC && | ||||
|         cipher->mode != QCRYPTO_CIPHER_MODE_ECB) { | ||||
|         error_setg(errp, "Unsupported cipher mode %d", cipher->mode); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     ctxt = g_new0(QCryptoCipherBuiltin, 1); | ||||
| 
 | ||||
|     if (AES_set_encrypt_key(key, nkey * 8, &ctxt->state.aes.encrypt_key) != 0) { | ||||
|         error_setg(errp, "Failed to set encryption key"); | ||||
|         goto error; | ||||
|     } | ||||
| 
 | ||||
|     if (AES_set_decrypt_key(key, nkey * 8, &ctxt->state.aes.decrypt_key) != 0) { | ||||
|         error_setg(errp, "Failed to set decryption key"); | ||||
|         goto error; | ||||
|     } | ||||
| 
 | ||||
|     ctxt->free = qcrypto_cipher_free_aes; | ||||
|     ctxt->setiv = qcrypto_cipher_setiv_aes; | ||||
|     ctxt->encrypt = qcrypto_cipher_encrypt_aes; | ||||
|     ctxt->decrypt = qcrypto_cipher_decrypt_aes; | ||||
| 
 | ||||
|     cipher->opaque = ctxt; | ||||
| 
 | ||||
|     return 0; | ||||
| 
 | ||||
|  error: | ||||
|     g_free(ctxt); | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void qcrypto_cipher_free_des_rfb(QCryptoCipher *cipher) | ||||
| { | ||||
|     QCryptoCipherBuiltin *ctxt = cipher->opaque; | ||||
| 
 | ||||
|     g_free(ctxt->state.desrfb.key); | ||||
|     g_free(ctxt); | ||||
|     cipher->opaque = NULL; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int qcrypto_cipher_encrypt_des_rfb(QCryptoCipher *cipher, | ||||
|                                           const void *in, | ||||
|                                           void *out, | ||||
|                                           size_t len, | ||||
|                                           Error **errp) | ||||
| { | ||||
|     QCryptoCipherBuiltin *ctxt = cipher->opaque; | ||||
|     size_t i; | ||||
| 
 | ||||
|     if (len % 8) { | ||||
|         error_setg(errp, "Buffer size must be multiple of 8 not %zu", | ||||
|                    len); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     deskey(ctxt->state.desrfb.key, EN0); | ||||
| 
 | ||||
|     for (i = 0; i < len; i += 8) { | ||||
|         des((void *)in + i, out + i); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int qcrypto_cipher_decrypt_des_rfb(QCryptoCipher *cipher, | ||||
|                                           const void *in, | ||||
|                                           void *out, | ||||
|                                           size_t len, | ||||
|                                           Error **errp) | ||||
| { | ||||
|     QCryptoCipherBuiltin *ctxt = cipher->opaque; | ||||
|     size_t i; | ||||
| 
 | ||||
|     if (len % 8) { | ||||
|         error_setg(errp, "Buffer size must be multiple of 8 not %zu", | ||||
|                    len); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     deskey(ctxt->state.desrfb.key, DE1); | ||||
| 
 | ||||
|     for (i = 0; i < len; i += 8) { | ||||
|         des((void *)in + i, out + i); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int qcrypto_cipher_setiv_des_rfb(QCryptoCipher *cipher, | ||||
|                                         const uint8_t *iv, size_t niv, | ||||
|                                         Error **errp) | ||||
| { | ||||
|     error_setg(errp, "Setting IV is not supported"); | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int qcrypto_cipher_init_des_rfb(QCryptoCipher *cipher, | ||||
|                                        const uint8_t *key, size_t nkey, | ||||
|                                        Error **errp) | ||||
| { | ||||
|     QCryptoCipherBuiltin *ctxt; | ||||
| 
 | ||||
|     if (cipher->mode != QCRYPTO_CIPHER_MODE_ECB) { | ||||
|         error_setg(errp, "Unsupported cipher mode %d", cipher->mode); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     ctxt = g_new0(QCryptoCipherBuiltin, 1); | ||||
| 
 | ||||
|     ctxt->state.desrfb.key = g_new0(uint8_t, nkey); | ||||
|     memcpy(ctxt->state.desrfb.key, key, nkey); | ||||
|     ctxt->state.desrfb.nkey = nkey; | ||||
| 
 | ||||
|     ctxt->free = qcrypto_cipher_free_des_rfb; | ||||
|     ctxt->setiv = qcrypto_cipher_setiv_des_rfb; | ||||
|     ctxt->encrypt = qcrypto_cipher_encrypt_des_rfb; | ||||
|     ctxt->decrypt = qcrypto_cipher_decrypt_des_rfb; | ||||
| 
 | ||||
|     cipher->opaque = ctxt; | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg) | ||||
| { | ||||
|     switch (alg) { | ||||
|     case QCRYPTO_CIPHER_ALG_DES_RFB: | ||||
|     case QCRYPTO_CIPHER_ALG_AES_128: | ||||
|     case QCRYPTO_CIPHER_ALG_AES_192: | ||||
|     case QCRYPTO_CIPHER_ALG_AES_256: | ||||
|         return true; | ||||
|     default: | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, | ||||
|                                   QCryptoCipherMode mode, | ||||
|                                   const uint8_t *key, size_t nkey, | ||||
|                                   Error **errp) | ||||
| { | ||||
|     QCryptoCipher *cipher; | ||||
| 
 | ||||
|     cipher = g_new0(QCryptoCipher, 1); | ||||
|     cipher->alg = alg; | ||||
|     cipher->mode = mode; | ||||
| 
 | ||||
|     if (!qcrypto_cipher_validate_key_length(alg, nkey, errp)) { | ||||
|         goto error; | ||||
|     } | ||||
| 
 | ||||
|     switch (cipher->alg) { | ||||
|     case QCRYPTO_CIPHER_ALG_DES_RFB: | ||||
|         if (qcrypto_cipher_init_des_rfb(cipher, key, nkey, errp) < 0) { | ||||
|             goto error; | ||||
|         } | ||||
|         break; | ||||
|     case QCRYPTO_CIPHER_ALG_AES_128: | ||||
|     case QCRYPTO_CIPHER_ALG_AES_192: | ||||
|     case QCRYPTO_CIPHER_ALG_AES_256: | ||||
|         if (qcrypto_cipher_init_aes(cipher, key, nkey, errp) < 0) { | ||||
|             goto error; | ||||
|         } | ||||
|         break; | ||||
|     default: | ||||
|         error_setg(errp, | ||||
|                    "Unsupported cipher algorithm %d", cipher->alg); | ||||
|         goto error; | ||||
|     } | ||||
| 
 | ||||
|     return cipher; | ||||
| 
 | ||||
|  error: | ||||
|     g_free(cipher); | ||||
|     return NULL; | ||||
| } | ||||
| 
 | ||||
| void qcrypto_cipher_free(QCryptoCipher *cipher) | ||||
| { | ||||
|     QCryptoCipherBuiltin *ctxt = cipher->opaque; | ||||
|     if (!cipher) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     ctxt->free(cipher); | ||||
|     g_free(cipher); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int qcrypto_cipher_encrypt(QCryptoCipher *cipher, | ||||
|                            const void *in, | ||||
|                            void *out, | ||||
|                            size_t len, | ||||
|                            Error **errp) | ||||
| { | ||||
|     QCryptoCipherBuiltin *ctxt = cipher->opaque; | ||||
| 
 | ||||
|     return ctxt->encrypt(cipher, in, out, len, errp); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int qcrypto_cipher_decrypt(QCryptoCipher *cipher, | ||||
|                            const void *in, | ||||
|                            void *out, | ||||
|                            size_t len, | ||||
|                            Error **errp) | ||||
| { | ||||
|     QCryptoCipherBuiltin *ctxt = cipher->opaque; | ||||
| 
 | ||||
|     return ctxt->decrypt(cipher, in, out, len, errp); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int qcrypto_cipher_setiv(QCryptoCipher *cipher, | ||||
|                          const uint8_t *iv, size_t niv, | ||||
|                          Error **errp) | ||||
| { | ||||
|     QCryptoCipherBuiltin *ctxt = cipher->opaque; | ||||
| 
 | ||||
|     return ctxt->setiv(cipher, iv, niv, errp); | ||||
| } | ||||
							
								
								
									
										195
									
								
								crypto/cipher-gcrypt.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										195
									
								
								crypto/cipher-gcrypt.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,195 @@ | ||||
| /*
 | ||||
|  * QEMU Crypto cipher libgcrypt algorithms | ||||
|  * | ||||
|  * Copyright (c) 2015 Red Hat, Inc. | ||||
|  * | ||||
|  * 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 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 <gcrypt.h> | ||||
| 
 | ||||
| 
 | ||||
| bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg) | ||||
| { | ||||
|     switch (alg) { | ||||
|     case QCRYPTO_CIPHER_ALG_DES_RFB: | ||||
|     case QCRYPTO_CIPHER_ALG_AES_128: | ||||
|     case QCRYPTO_CIPHER_ALG_AES_192: | ||||
|     case QCRYPTO_CIPHER_ALG_AES_256: | ||||
|         return true; | ||||
|     default: | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, | ||||
|                                   QCryptoCipherMode mode, | ||||
|                                   const uint8_t *key, size_t nkey, | ||||
|                                   Error **errp) | ||||
| { | ||||
|     QCryptoCipher *cipher; | ||||
|     gcry_cipher_hd_t handle; | ||||
|     gcry_error_t err; | ||||
|     int gcryalg, gcrymode; | ||||
| 
 | ||||
|     switch (mode) { | ||||
|     case QCRYPTO_CIPHER_MODE_ECB: | ||||
|         gcrymode = GCRY_CIPHER_MODE_ECB; | ||||
|         break; | ||||
|     case QCRYPTO_CIPHER_MODE_CBC: | ||||
|         gcrymode = GCRY_CIPHER_MODE_CBC; | ||||
|         break; | ||||
|     default: | ||||
|         error_setg(errp, "Unsupported cipher mode %d", mode); | ||||
|         return NULL; | ||||
|     } | ||||
| 
 | ||||
|     if (!qcrypto_cipher_validate_key_length(alg, nkey, errp)) { | ||||
|         return NULL; | ||||
|     } | ||||
| 
 | ||||
|     switch (alg) { | ||||
|     case QCRYPTO_CIPHER_ALG_DES_RFB: | ||||
|         gcryalg = GCRY_CIPHER_DES; | ||||
|         break; | ||||
| 
 | ||||
|     case QCRYPTO_CIPHER_ALG_AES_128: | ||||
|         gcryalg = GCRY_CIPHER_AES128; | ||||
|         break; | ||||
| 
 | ||||
|     case QCRYPTO_CIPHER_ALG_AES_192: | ||||
|         gcryalg = GCRY_CIPHER_AES192; | ||||
|         break; | ||||
| 
 | ||||
|     case QCRYPTO_CIPHER_ALG_AES_256: | ||||
|         gcryalg = GCRY_CIPHER_AES256; | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         error_setg(errp, "Unsupported cipher algorithm %d", alg); | ||||
|         return NULL; | ||||
|     } | ||||
| 
 | ||||
|     cipher = g_new0(QCryptoCipher, 1); | ||||
|     cipher->alg = alg; | ||||
|     cipher->mode = mode; | ||||
| 
 | ||||
|     err = gcry_cipher_open(&handle, gcryalg, gcrymode, 0); | ||||
|     if (err != 0) { | ||||
|         error_setg(errp, "Cannot initialize cipher: %s", | ||||
|                    gcry_strerror(err)); | ||||
|         goto error; | ||||
|     } | ||||
| 
 | ||||
|     if (cipher->alg == QCRYPTO_CIPHER_ALG_DES_RFB) { | ||||
|         /* We're using standard DES cipher from gcrypt, so we need
 | ||||
|          * to munge the key so that the results are the same as the | ||||
|          * bizarre RFB variant of DES :-) | ||||
|          */ | ||||
|         uint8_t *rfbkey = qcrypto_cipher_munge_des_rfb_key(key, nkey); | ||||
|         err = gcry_cipher_setkey(handle, rfbkey, nkey); | ||||
|         g_free(rfbkey); | ||||
|     } else { | ||||
|         err = gcry_cipher_setkey(handle, key, nkey); | ||||
|     } | ||||
|     if (err != 0) { | ||||
|         error_setg(errp, "Cannot set key: %s", | ||||
|                    gcry_strerror(err)); | ||||
|         goto error; | ||||
|     } | ||||
| 
 | ||||
|     cipher->opaque = handle; | ||||
|     return cipher; | ||||
| 
 | ||||
|  error: | ||||
|     gcry_cipher_close(handle); | ||||
|     g_free(cipher); | ||||
|     return NULL; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void qcrypto_cipher_free(QCryptoCipher *cipher) | ||||
| { | ||||
|     gcry_cipher_hd_t handle; | ||||
|     if (!cipher) { | ||||
|         return; | ||||
|     } | ||||
|     handle = cipher->opaque; | ||||
|     gcry_cipher_close(handle); | ||||
|     g_free(cipher); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int qcrypto_cipher_encrypt(QCryptoCipher *cipher, | ||||
|                            const void *in, | ||||
|                            void *out, | ||||
|                            size_t len, | ||||
|                            Error **errp) | ||||
| { | ||||
|     gcry_cipher_hd_t handle = cipher->opaque; | ||||
|     gcry_error_t err; | ||||
| 
 | ||||
|     err = gcry_cipher_encrypt(handle, | ||||
|                               out, len, | ||||
|                               in, len); | ||||
|     if (err != 0) { | ||||
|         error_setg(errp, "Cannot encrypt data: %s", | ||||
|                    gcry_strerror(err)); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int qcrypto_cipher_decrypt(QCryptoCipher *cipher, | ||||
|                            const void *in, | ||||
|                            void *out, | ||||
|                            size_t len, | ||||
|                            Error **errp) | ||||
| { | ||||
|     gcry_cipher_hd_t handle = cipher->opaque; | ||||
|     gcry_error_t err; | ||||
| 
 | ||||
|     err = gcry_cipher_decrypt(handle, | ||||
|                               out, len, | ||||
|                               in, len); | ||||
|     if (err != 0) { | ||||
|         error_setg(errp, "Cannot decrypt data: %s", | ||||
|                    gcry_strerror(err)); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int qcrypto_cipher_setiv(QCryptoCipher *cipher, | ||||
|                          const uint8_t *iv, size_t niv, | ||||
|                          Error **errp) | ||||
| { | ||||
|     gcry_cipher_hd_t handle = cipher->opaque; | ||||
|     gcry_error_t err; | ||||
| 
 | ||||
|     gcry_cipher_reset(handle); | ||||
|     err = gcry_cipher_setiv(handle, iv, niv); | ||||
|     if (err != 0) { | ||||
|         error_setg(errp, "Cannot set IV: %s", | ||||
|                    gcry_strerror(err)); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										206
									
								
								crypto/cipher-nettle.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								crypto/cipher-nettle.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,206 @@ | ||||
| /*
 | ||||
|  * QEMU Crypto cipher nettle algorithms | ||||
|  * | ||||
|  * Copyright (c) 2015 Red Hat, Inc. | ||||
|  * | ||||
|  * 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 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 <nettle/nettle-types.h> | ||||
| #include <nettle/aes.h> | ||||
| #include <nettle/des.h> | ||||
| #include <nettle/cbc.h> | ||||
| 
 | ||||
| typedef struct QCryptoCipherNettle QCryptoCipherNettle; | ||||
| struct QCryptoCipherNettle { | ||||
|     void *ctx_encrypt; | ||||
|     void *ctx_decrypt; | ||||
|     nettle_crypt_func *alg_encrypt; | ||||
|     nettle_crypt_func *alg_decrypt; | ||||
|     uint8_t *iv; | ||||
|     size_t niv; | ||||
| }; | ||||
| 
 | ||||
| bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg) | ||||
| { | ||||
|     switch (alg) { | ||||
|     case QCRYPTO_CIPHER_ALG_DES_RFB: | ||||
|     case QCRYPTO_CIPHER_ALG_AES_128: | ||||
|     case QCRYPTO_CIPHER_ALG_AES_192: | ||||
|     case QCRYPTO_CIPHER_ALG_AES_256: | ||||
|         return true; | ||||
|     default: | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, | ||||
|                                   QCryptoCipherMode mode, | ||||
|                                   const uint8_t *key, size_t nkey, | ||||
|                                   Error **errp) | ||||
| { | ||||
|     QCryptoCipher *cipher; | ||||
|     QCryptoCipherNettle *ctx; | ||||
|     uint8_t *rfbkey; | ||||
| 
 | ||||
|     switch (mode) { | ||||
|     case QCRYPTO_CIPHER_MODE_ECB: | ||||
|     case QCRYPTO_CIPHER_MODE_CBC: | ||||
|         break; | ||||
|     default: | ||||
|         error_setg(errp, "Unsupported cipher mode %d", mode); | ||||
|         return NULL; | ||||
|     } | ||||
| 
 | ||||
|     if (!qcrypto_cipher_validate_key_length(alg, nkey, errp)) { | ||||
|         return NULL; | ||||
|     } | ||||
| 
 | ||||
|     cipher = g_new0(QCryptoCipher, 1); | ||||
|     cipher->alg = alg; | ||||
|     cipher->mode = mode; | ||||
| 
 | ||||
|     ctx = g_new0(QCryptoCipherNettle, 1); | ||||
| 
 | ||||
|     switch (alg) { | ||||
|     case QCRYPTO_CIPHER_ALG_DES_RFB: | ||||
|         ctx->ctx_encrypt = g_new0(struct des_ctx, 1); | ||||
|         ctx->ctx_decrypt = NULL; /* 1 ctx can do both */ | ||||
|         rfbkey = qcrypto_cipher_munge_des_rfb_key(key, nkey); | ||||
|         des_set_key(ctx->ctx_encrypt, rfbkey); | ||||
|         g_free(rfbkey); | ||||
| 
 | ||||
|         ctx->alg_encrypt = (nettle_crypt_func *)des_encrypt; | ||||
|         ctx->alg_decrypt = (nettle_crypt_func *)des_decrypt; | ||||
| 
 | ||||
|         ctx->niv = DES_BLOCK_SIZE; | ||||
|         break; | ||||
| 
 | ||||
|     case QCRYPTO_CIPHER_ALG_AES_128: | ||||
|     case QCRYPTO_CIPHER_ALG_AES_192: | ||||
|     case QCRYPTO_CIPHER_ALG_AES_256: | ||||
|         ctx->ctx_encrypt = g_new0(struct aes_ctx, 1); | ||||
|         ctx->ctx_decrypt = g_new0(struct aes_ctx, 1); | ||||
| 
 | ||||
|         aes_set_encrypt_key(ctx->ctx_encrypt, nkey, key); | ||||
|         aes_set_decrypt_key(ctx->ctx_decrypt, nkey, key); | ||||
| 
 | ||||
|         ctx->alg_encrypt = (nettle_crypt_func *)aes_encrypt; | ||||
|         ctx->alg_decrypt = (nettle_crypt_func *)aes_decrypt; | ||||
| 
 | ||||
|         ctx->niv = AES_BLOCK_SIZE; | ||||
|         break; | ||||
|     default: | ||||
|         error_setg(errp, "Unsupported cipher algorithm %d", alg); | ||||
|         goto error; | ||||
|     } | ||||
| 
 | ||||
|     ctx->iv = g_new0(uint8_t, ctx->niv); | ||||
|     cipher->opaque = ctx; | ||||
| 
 | ||||
|     return cipher; | ||||
| 
 | ||||
|  error: | ||||
|     g_free(cipher); | ||||
|     g_free(ctx); | ||||
|     return NULL; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void qcrypto_cipher_free(QCryptoCipher *cipher) | ||||
| { | ||||
|     QCryptoCipherNettle *ctx; | ||||
| 
 | ||||
|     if (!cipher) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     ctx = cipher->opaque; | ||||
|     g_free(ctx->iv); | ||||
|     g_free(ctx->ctx_encrypt); | ||||
|     g_free(ctx->ctx_decrypt); | ||||
|     g_free(ctx); | ||||
|     g_free(cipher); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int qcrypto_cipher_encrypt(QCryptoCipher *cipher, | ||||
|                            const void *in, | ||||
|                            void *out, | ||||
|                            size_t len, | ||||
|                            Error **errp) | ||||
| { | ||||
|     QCryptoCipherNettle *ctx = cipher->opaque; | ||||
| 
 | ||||
|     switch (cipher->mode) { | ||||
|     case QCRYPTO_CIPHER_MODE_ECB: | ||||
|         ctx->alg_encrypt(ctx->ctx_encrypt, len, out, in); | ||||
|         break; | ||||
| 
 | ||||
|     case QCRYPTO_CIPHER_MODE_CBC: | ||||
|         cbc_encrypt(ctx->ctx_encrypt, ctx->alg_encrypt, | ||||
|                     ctx->niv, ctx->iv, | ||||
|                     len, out, in); | ||||
|         break; | ||||
|     default: | ||||
|         error_setg(errp, "Unsupported cipher algorithm %d", | ||||
|                    cipher->alg); | ||||
|         return -1; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int qcrypto_cipher_decrypt(QCryptoCipher *cipher, | ||||
|                            const void *in, | ||||
|                            void *out, | ||||
|                            size_t len, | ||||
|                            Error **errp) | ||||
| { | ||||
|     QCryptoCipherNettle *ctx = cipher->opaque; | ||||
| 
 | ||||
|     switch (cipher->mode) { | ||||
|     case QCRYPTO_CIPHER_MODE_ECB: | ||||
|         ctx->alg_decrypt(ctx->ctx_decrypt ? ctx->ctx_decrypt : ctx->ctx_encrypt, | ||||
|                          len, out, in); | ||||
|         break; | ||||
| 
 | ||||
|     case QCRYPTO_CIPHER_MODE_CBC: | ||||
|         cbc_decrypt(ctx->ctx_decrypt ? ctx->ctx_decrypt : ctx->ctx_encrypt, | ||||
|                     ctx->alg_decrypt, ctx->niv, ctx->iv, | ||||
|                     len, out, in); | ||||
|         break; | ||||
|     default: | ||||
|         error_setg(errp, "Unsupported cipher algorithm %d", | ||||
|                    cipher->alg); | ||||
|         return -1; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int qcrypto_cipher_setiv(QCryptoCipher *cipher, | ||||
|                          const uint8_t *iv, size_t niv, | ||||
|                          Error **errp) | ||||
| { | ||||
|     QCryptoCipherNettle *ctx = cipher->opaque; | ||||
|     if (niv != ctx->niv) { | ||||
|         error_setg(errp, "Expected IV size %zu not %zu", | ||||
|                    ctx->niv, niv); | ||||
|         return -1; | ||||
|     } | ||||
|     memcpy(ctx->iv, iv, niv); | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										74
									
								
								crypto/cipher.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								crypto/cipher.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,74 @@ | ||||
| /*
 | ||||
|  * QEMU Crypto cipher algorithms | ||||
|  * | ||||
|  * Copyright (c) 2015 Red Hat, Inc. | ||||
|  * | ||||
|  * 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 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 "crypto/cipher.h" | ||||
| 
 | ||||
| 
 | ||||
| static size_t alg_key_len[QCRYPTO_CIPHER_ALG_LAST] = { | ||||
|     [QCRYPTO_CIPHER_ALG_AES_128] = 16, | ||||
|     [QCRYPTO_CIPHER_ALG_AES_192] = 24, | ||||
|     [QCRYPTO_CIPHER_ALG_AES_256] = 32, | ||||
|     [QCRYPTO_CIPHER_ALG_DES_RFB] = 8, | ||||
| }; | ||||
| 
 | ||||
| static bool | ||||
| qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg, | ||||
|                                    size_t nkey, | ||||
|                                    Error **errp) | ||||
| { | ||||
|     if ((unsigned)alg >= QCRYPTO_CIPHER_ALG_LAST) { | ||||
|         error_setg(errp, "Cipher algorithm %d out of range", | ||||
|                    alg); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if (alg_key_len[alg] != nkey) { | ||||
|         error_setg(errp, "Cipher key length %zu should be %zu", | ||||
|                    alg_key_len[alg], nkey); | ||||
|         return false; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| #if defined(CONFIG_GNUTLS_GCRYPT) || defined(CONFIG_GNUTLS_NETTLE) | ||||
| static uint8_t * | ||||
| qcrypto_cipher_munge_des_rfb_key(const uint8_t *key, | ||||
|                                  size_t nkey) | ||||
| { | ||||
|     uint8_t *ret = g_new0(uint8_t, nkey); | ||||
|     size_t i; | ||||
|     for (i = 0; i < nkey; i++) { | ||||
|         uint8_t r = key[i]; | ||||
|         r = (r & 0xf0) >> 4 | (r & 0x0f) << 4; | ||||
|         r = (r & 0xcc) >> 2 | (r & 0x33) << 2; | ||||
|         r = (r & 0xaa) >> 1 | (r & 0x55) << 1; | ||||
|         ret[i] = r; | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| #endif /* CONFIG_GNUTLS_GCRYPT || CONFIG_GNUTLS_NETTLE */ | ||||
| 
 | ||||
| #ifdef CONFIG_GNUTLS_GCRYPT | ||||
| #include "crypto/cipher-gcrypt.c" | ||||
| #elif defined CONFIG_GNUTLS_NETTLE | ||||
| #include "crypto/cipher-nettle.c" | ||||
| #else | ||||
| #include "crypto/cipher-builtin.c" | ||||
| #endif | ||||
| @ -26,7 +26,7 @@ | ||||
|  * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992. | ||||
|  */ | ||||
| 
 | ||||
| #include "d3des.h" | ||||
| #include "crypto/desrfb.h" | ||||
| 
 | ||||
| static void scrunch(unsigned char *, unsigned long *); | ||||
| static void unscrun(unsigned long *, unsigned char *); | ||||
							
								
								
									
										200
									
								
								crypto/hash.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								crypto/hash.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,200 @@ | ||||
| /*
 | ||||
|  * QEMU Crypto hash algorithms | ||||
|  * | ||||
|  * Copyright (c) 2015 Red Hat, Inc. | ||||
|  * | ||||
|  * 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 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 "crypto/hash.h" | ||||
| 
 | ||||
| #ifdef CONFIG_GNUTLS_HASH | ||||
| #include <gnutls/gnutls.h> | ||||
| #include <gnutls/crypto.h> | ||||
| 
 | ||||
| static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG_LAST] = { | ||||
|     [QCRYPTO_HASH_ALG_MD5] = GNUTLS_DIG_MD5, | ||||
|     [QCRYPTO_HASH_ALG_SHA1] = GNUTLS_DIG_SHA1, | ||||
|     [QCRYPTO_HASH_ALG_SHA256] = GNUTLS_DIG_SHA256, | ||||
| }; | ||||
| 
 | ||||
| gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg) | ||||
| { | ||||
|     if (alg < G_N_ELEMENTS(qcrypto_hash_alg_map)) { | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, | ||||
|                         const struct iovec *iov, | ||||
|                         size_t niov, | ||||
|                         uint8_t **result, | ||||
|                         size_t *resultlen, | ||||
|                         Error **errp) | ||||
| { | ||||
|     int i, ret; | ||||
|     gnutls_hash_hd_t dig; | ||||
| 
 | ||||
|     if (alg >= G_N_ELEMENTS(qcrypto_hash_alg_map)) { | ||||
|         error_setg(errp, | ||||
|                    "Unknown hash algorithm %d", | ||||
|                    alg); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     ret = gnutls_hash_init(&dig, qcrypto_hash_alg_map[alg]); | ||||
| 
 | ||||
|     if (ret < 0) { | ||||
|         error_setg(errp, | ||||
|                    "Unable to initialize hash algorithm: %s", | ||||
|                    gnutls_strerror(ret)); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     for (i = 0; i < niov; i++) { | ||||
|         ret = gnutls_hash(dig, iov[i].iov_base, iov[i].iov_len); | ||||
|         if (ret < 0) { | ||||
|             error_setg(errp, | ||||
|                        "Unable process hash data: %s", | ||||
|                        gnutls_strerror(ret)); | ||||
|             goto error; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ret = gnutls_hash_get_len(qcrypto_hash_alg_map[alg]); | ||||
|     if (ret <= 0) { | ||||
|         error_setg(errp, | ||||
|                    "Unable to get hash length: %s", | ||||
|                    gnutls_strerror(ret)); | ||||
|         goto error; | ||||
|     } | ||||
|     if (*resultlen == 0) { | ||||
|         *resultlen = ret; | ||||
|         *result = g_new0(uint8_t, *resultlen); | ||||
|     } else if (*resultlen != ret) { | ||||
|         error_setg(errp, | ||||
|                    "Result buffer size %zu is smaller than hash %d", | ||||
|                    *resultlen, ret); | ||||
|         goto error; | ||||
|     } | ||||
| 
 | ||||
|     gnutls_hash_deinit(dig, *result); | ||||
|     return 0; | ||||
| 
 | ||||
|  error: | ||||
|     gnutls_hash_deinit(dig, NULL); | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| #else /* ! CONFIG_GNUTLS_HASH */ | ||||
| 
 | ||||
| gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg G_GNUC_UNUSED) | ||||
| { | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, | ||||
|                         const struct iovec *iov G_GNUC_UNUSED, | ||||
|                         size_t niov G_GNUC_UNUSED, | ||||
|                         uint8_t **result G_GNUC_UNUSED, | ||||
|                         size_t *resultlen G_GNUC_UNUSED, | ||||
|                         Error **errp) | ||||
| { | ||||
|     error_setg(errp, | ||||
|                "Hash algorithm %d not supported without GNUTLS", | ||||
|                alg); | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| #endif /* ! CONFIG_GNUTLS_HASH */ | ||||
| 
 | ||||
| int qcrypto_hash_bytes(QCryptoHashAlgorithm alg, | ||||
|                        const char *buf, | ||||
|                        size_t len, | ||||
|                        uint8_t **result, | ||||
|                        size_t *resultlen, | ||||
|                        Error **errp) | ||||
| { | ||||
|     struct iovec iov = { .iov_base = (char *)buf, | ||||
|                          .iov_len = len }; | ||||
|     return qcrypto_hash_bytesv(alg, &iov, 1, result, resultlen, errp); | ||||
| } | ||||
| 
 | ||||
| static const char hex[] = "0123456789abcdef"; | ||||
| 
 | ||||
| int qcrypto_hash_digestv(QCryptoHashAlgorithm alg, | ||||
|                          const struct iovec *iov, | ||||
|                          size_t niov, | ||||
|                          char **digest, | ||||
|                          Error **errp) | ||||
| { | ||||
|     uint8_t *result = NULL; | ||||
|     size_t resultlen = 0; | ||||
|     size_t i; | ||||
| 
 | ||||
|     if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) { | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     *digest = g_new0(char, (resultlen * 2) + 1); | ||||
|     for (i = 0 ; i < resultlen ; i++) { | ||||
|         (*digest)[(i * 2)] = hex[(result[i] >> 4) & 0xf]; | ||||
|         (*digest)[(i * 2) + 1] = hex[result[i] & 0xf]; | ||||
|     } | ||||
|     (*digest)[resultlen * 2] = '\0'; | ||||
|     g_free(result); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int qcrypto_hash_digest(QCryptoHashAlgorithm alg, | ||||
|                         const char *buf, | ||||
|                         size_t len, | ||||
|                         char **digest, | ||||
|                         Error **errp) | ||||
| { | ||||
|     struct iovec iov = { .iov_base = (char *)buf, .iov_len = len }; | ||||
| 
 | ||||
|     return qcrypto_hash_digestv(alg, &iov, 1, digest, errp); | ||||
| } | ||||
| 
 | ||||
| int qcrypto_hash_base64v(QCryptoHashAlgorithm alg, | ||||
|                          const struct iovec *iov, | ||||
|                          size_t niov, | ||||
|                          char **base64, | ||||
|                          Error **errp) | ||||
| { | ||||
|     uint8_t *result = NULL; | ||||
|     size_t resultlen = 0; | ||||
| 
 | ||||
|     if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) { | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     *base64 = g_base64_encode(result, resultlen); | ||||
|     g_free(result); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int qcrypto_hash_base64(QCryptoHashAlgorithm alg, | ||||
|                         const char *buf, | ||||
|                         size_t len, | ||||
|                         char **base64, | ||||
|                         Error **errp) | ||||
| { | ||||
|     struct iovec iov = { .iov_base = (char *)buf, .iov_len = len }; | ||||
| 
 | ||||
|     return qcrypto_hash_base64v(alg, &iov, 1, base64, errp); | ||||
| } | ||||
							
								
								
									
										150
									
								
								crypto/init.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								crypto/init.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,150 @@ | ||||
| /*
 | ||||
|  * QEMU Crypto initialization | ||||
|  * | ||||
|  * Copyright (c) 2015 Red Hat, Inc. | ||||
|  * | ||||
|  * 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 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 "crypto/init.h" | ||||
| #include "qemu/thread.h" | ||||
| 
 | ||||
| #ifdef CONFIG_GNUTLS | ||||
| #include <gnutls/gnutls.h> | ||||
| #include <gnutls/crypto.h> | ||||
| 
 | ||||
| #ifdef CONFIG_GNUTLS_GCRYPT | ||||
| #include <gcrypt.h> | ||||
| #endif | ||||
| 
 | ||||
| /* #define DEBUG_GNUTLS */ | ||||
| 
 | ||||
| /*
 | ||||
|  * If GNUTLS is built against GCrypt then | ||||
|  * | ||||
|  *  - When GNUTLS >= 2.12, we must not initialize gcrypt threading | ||||
|  *    because GNUTLS will do that itself | ||||
|  *  - When GNUTLS < 2.12 we must always initialize gcrypt threading | ||||
|  * | ||||
|  * But.... | ||||
|  * | ||||
|  *    When gcrypt >= 1.6.0 we must not initialize gcrypt threading | ||||
|  *    because gcrypt will do that itself. | ||||
|  * | ||||
|  * So we need to init gcrypt threading if | ||||
|  * | ||||
|  *   - gcrypt < 1.6.0 | ||||
|  * AND | ||||
|  *   - gnutls < 2.12 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #if (defined(CONFIG_GNUTLS_GCRYPT) &&           \ | ||||
|      (!defined(GNUTLS_VERSION_NUMBER) ||        \ | ||||
|       (GNUTLS_VERSION_NUMBER < 0x020c00)) &&    \ | ||||
|      (!defined(GCRYPT_VERSION_NUMBER) ||        \ | ||||
|       (GCRYPT_VERSION_NUMBER < 0x010600))) | ||||
| #define QCRYPTO_INIT_GCRYPT_THREADS | ||||
| #else | ||||
| #undef QCRYPTO_INIT_GCRYPT_THREADS | ||||
| #endif | ||||
| 
 | ||||
| #ifdef DEBUG_GNUTLS | ||||
| static void qcrypto_gnutls_log(int level, const char *str) | ||||
| { | ||||
|     fprintf(stderr, "%d: %s", level, str); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #ifdef QCRYPTO_INIT_GCRYPT_THREADS | ||||
| static int qcrypto_gcrypt_mutex_init(void **priv) | ||||
| {                                                                             \ | ||||
|     QemuMutex *lock = NULL; | ||||
|     lock = g_new0(QemuMutex, 1); | ||||
|     qemu_mutex_init(lock); | ||||
|     *priv = lock; | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int qcrypto_gcrypt_mutex_destroy(void **priv) | ||||
| { | ||||
|     QemuMutex *lock = *priv; | ||||
|     qemu_mutex_destroy(lock); | ||||
|     g_free(lock); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int qcrypto_gcrypt_mutex_lock(void **priv) | ||||
| { | ||||
|     QemuMutex *lock = *priv; | ||||
|     qemu_mutex_lock(lock); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int qcrypto_gcrypt_mutex_unlock(void **priv) | ||||
| { | ||||
|     QemuMutex *lock = *priv; | ||||
|     qemu_mutex_unlock(lock); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static struct gcry_thread_cbs qcrypto_gcrypt_thread_impl = { | ||||
|     (GCRY_THREAD_OPTION_PTHREAD | (GCRY_THREAD_OPTION_VERSION << 8)), | ||||
|     NULL, | ||||
|     qcrypto_gcrypt_mutex_init, | ||||
|     qcrypto_gcrypt_mutex_destroy, | ||||
|     qcrypto_gcrypt_mutex_lock, | ||||
|     qcrypto_gcrypt_mutex_unlock, | ||||
|     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL | ||||
| }; | ||||
| #endif /* QCRYPTO_INIT_GCRYPT */ | ||||
| 
 | ||||
| int qcrypto_init(Error **errp) | ||||
| { | ||||
|     int ret; | ||||
|     ret = gnutls_global_init(); | ||||
|     if (ret < 0) { | ||||
|         error_setg(errp, | ||||
|                    "Unable to initialize GNUTLS library: %s", | ||||
|                    gnutls_strerror(ret)); | ||||
|         return -1; | ||||
|     } | ||||
| #ifdef DEBUG_GNUTLS | ||||
|     gnutls_global_set_log_level(10); | ||||
|     gnutls_global_set_log_function(qcrypto_gnutls_log); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_GNUTLS_GCRYPT | ||||
|     if (!gcry_check_version(GCRYPT_VERSION)) { | ||||
|         error_setg(errp, "Unable to initialize gcrypt"); | ||||
|         return -1; | ||||
|     } | ||||
| #ifdef QCRYPTO_INIT_GCRYPT_THREADS | ||||
|     gcry_control(GCRYCTL_SET_THREAD_CBS, &qcrypto_gcrypt_thread_impl); | ||||
| #endif /* QCRYPTO_INIT_GCRYPT_THREADS */ | ||||
|     gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); | ||||
| #endif | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| #else /* ! CONFIG_GNUTLS */ | ||||
| 
 | ||||
| int qcrypto_init(Error **errp G_GNUC_UNUSED) | ||||
| { | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| #endif /* ! CONFIG_GNUTLS */ | ||||
| @ -933,7 +933,7 @@ static void rom_reset(void *unused) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| int rom_load_all(void) | ||||
| int rom_check_and_register_reset(void) | ||||
| { | ||||
|     hwaddr addr = 0; | ||||
|     MemoryRegionSection section; | ||||
| @ -957,12 +957,8 @@ int rom_load_all(void) | ||||
|         memory_region_unref(section.mr); | ||||
|     } | ||||
|     qemu_register_reset(rom_reset, NULL); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| void rom_load_done(void) | ||||
| { | ||||
|     roms_loaded = 1; | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| void rom_set_fw(FWCfgState *f) | ||||
|  | ||||
							
								
								
									
										210
									
								
								include/crypto/cipher.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								include/crypto/cipher.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,210 @@ | ||||
| /*
 | ||||
|  * QEMU Crypto cipher algorithms | ||||
|  * | ||||
|  * Copyright (c) 2015 Red Hat, Inc. | ||||
|  * | ||||
|  * 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 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_CIPHER_H__ | ||||
| #define QCRYPTO_CIPHER_H__ | ||||
| 
 | ||||
| #include "qemu-common.h" | ||||
| #include "qapi/error.h" | ||||
| 
 | ||||
| typedef struct QCryptoCipher QCryptoCipher; | ||||
| 
 | ||||
| typedef enum { | ||||
|     QCRYPTO_CIPHER_ALG_AES_128, | ||||
|     QCRYPTO_CIPHER_ALG_AES_192, | ||||
|     QCRYPTO_CIPHER_ALG_AES_256, | ||||
|     QCRYPTO_CIPHER_ALG_DES_RFB, /* A stupid variant on DES for VNC */ | ||||
| 
 | ||||
|     QCRYPTO_CIPHER_ALG_LAST | ||||
| } QCryptoCipherAlgorithm; | ||||
| 
 | ||||
| typedef enum { | ||||
|     QCRYPTO_CIPHER_MODE_ECB, | ||||
|     QCRYPTO_CIPHER_MODE_CBC, | ||||
| 
 | ||||
|     QCRYPTO_CIPHER_MODE_LAST | ||||
| } QCryptoCipherMode; | ||||
| 
 | ||||
| /**
 | ||||
|  * QCryptoCipher: | ||||
|  * | ||||
|  * The QCryptoCipher object provides a way to perform encryption | ||||
|  * and decryption of data, with a standard API, regardless of the | ||||
|  * algorithm used. It further isolates the calling code from the | ||||
|  * details of the specific underlying implementation, whether | ||||
|  * built-in, libgcrypt or nettle. | ||||
|  * | ||||
|  * Each QCryptoCipher object is capable of performing both | ||||
|  * encryption and decryption, and can operate in a number | ||||
|  * or modes including ECB, CBC. | ||||
|  * | ||||
|  * <example> | ||||
|  *   <title>Encrypting data with AES-128 in CBC mode</title> | ||||
|  *   <programlisting> | ||||
|  * QCryptoCipher *cipher; | ||||
|  * uint8_t key = ....; | ||||
|  * size_t keylen = 16; | ||||
|  * uint8_t iv = ....; | ||||
|  * | ||||
|  * if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128)) { | ||||
|  *    error_report(errp, "Feature <blah> requires AES cipher support"); | ||||
|  *    return -1; | ||||
|  * } | ||||
|  * | ||||
|  * cipher = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_128, | ||||
|  *                             QCRYPTO_CIPHER_MODE_CBC, | ||||
|  *                             key, keylen, | ||||
|  *                             errp); | ||||
|  * if (!cipher) { | ||||
|  *    return -1; | ||||
|  * } | ||||
|  * | ||||
|  * if (qcrypto_cipher_set_iv(cipher, iv, keylen, errp) < 0) { | ||||
|  *    return -1; | ||||
|  * } | ||||
|  * | ||||
|  * if (qcrypto_cipher_encrypt(cipher, rawdata, encdata, datalen, errp) < 0) { | ||||
|  *    return -1; | ||||
|  * } | ||||
|  * | ||||
|  * qcrypto_cipher_free(cipher); | ||||
|  *   </programlisting> | ||||
|  * </example> | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| struct QCryptoCipher { | ||||
|     QCryptoCipherAlgorithm alg; | ||||
|     QCryptoCipherMode mode; | ||||
|     void *opaque; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * qcrypto_cipher_supports: | ||||
|  * @alg: the cipher algorithm | ||||
|  * | ||||
|  * Determine if @alg cipher algorithm is supported by the | ||||
|  * current configured build | ||||
|  * | ||||
|  * Returns: true if the algorithm is supported, false otherwise | ||||
|  */ | ||||
| bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg); | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * qcrypto_cipher_new: | ||||
|  * @alg: the cipher algorithm | ||||
|  * @mode: the cipher usage mode | ||||
|  * @key: the private key bytes | ||||
|  * @nkey: the length of @key | ||||
|  * @errp: pointer to an uninitialized error object | ||||
|  * | ||||
|  * Creates a new cipher object for encrypting/decrypting | ||||
|  * data with the algorithm @alg in the usage mode @mode. | ||||
|  * | ||||
|  * The @key parameter provides the bytes representing | ||||
|  * the encryption/decryption key to use. The @nkey parameter | ||||
|  * specifies the length of @key in bytes. Each algorithm has | ||||
|  * one or more valid key lengths, and it is an error to provide | ||||
|  * a key of the incorrect length. | ||||
|  * | ||||
|  * The returned cipher object must be released with | ||||
|  * qcrypto_cipher_free() when no longer required | ||||
|  * | ||||
|  * Returns: a new cipher object, or NULL on error | ||||
|  */ | ||||
| QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, | ||||
|                                   QCryptoCipherMode mode, | ||||
|                                   const uint8_t *key, size_t nkey, | ||||
|                                   Error **errp); | ||||
| 
 | ||||
| /**
 | ||||
|  * qcrypto_cipher_free: | ||||
|  * @cipher: the cipher object | ||||
|  * | ||||
|  * Release the memory associated with @cipher that | ||||
|  * was previously allocated by qcrypto_cipher_new() | ||||
|  */ | ||||
| void qcrypto_cipher_free(QCryptoCipher *cipher); | ||||
| 
 | ||||
| /**
 | ||||
|  * qcrypto_cipher_encrypt: | ||||
|  * @cipher: the cipher object | ||||
|  * @in: buffer holding the plain text input data | ||||
|  * @out: buffer to fill with the cipher text output data | ||||
|  * @len: the length of @in and @out buffers | ||||
|  * @errp: pointer to an uninitialized error object | ||||
|  * | ||||
|  * Encrypts the plain text stored in @in, filling | ||||
|  * @out with the resulting ciphered text. Both the | ||||
|  * @in and @out buffers must have the same size, | ||||
|  * given by @len. | ||||
|  * | ||||
|  * Returns: 0 on success, or -1 on error | ||||
|  */ | ||||
| int qcrypto_cipher_encrypt(QCryptoCipher *cipher, | ||||
|                            const void *in, | ||||
|                            void *out, | ||||
|                            size_t len, | ||||
|                            Error **errp); | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * qcrypto_cipher_decrypt: | ||||
|  * @cipher: the cipher object | ||||
|  * @in: buffer holding the cipher text input data | ||||
|  * @out: buffer to fill with the plain text output data | ||||
|  * @len: the length of @in and @out buffers | ||||
|  * @errp: pointer to an uninitialized error object | ||||
|  * | ||||
|  * Decrypts the cipher text stored in @in, filling | ||||
|  * @out with the resulting plain text. Both the | ||||
|  * @in and @out buffers must have the same size, | ||||
|  * given by @len. | ||||
|  * | ||||
|  * Returns: 0 on success, or -1 on error | ||||
|  */ | ||||
| int qcrypto_cipher_decrypt(QCryptoCipher *cipher, | ||||
|                            const void *in, | ||||
|                            void *out, | ||||
|                            size_t len, | ||||
|                            Error **errp); | ||||
| 
 | ||||
| /**
 | ||||
|  * qcrypto_cipher_setiv: | ||||
|  * @cipher: the cipher object | ||||
|  * @iv: the initialization vector bytes | ||||
|  * @niv: the length of @iv | ||||
|  * @errpr: pointer to an uninitialized error object | ||||
|  * | ||||
|  * If the @cipher object is setup to use a mode that requires | ||||
|  * initialization vectors, this sets the initialization vector | ||||
|  * bytes. The @iv data should have the same length as the | ||||
|  * cipher key used when originally constructing the cipher | ||||
|  * object. It is an error to set an initialization vector | ||||
|  * if the cipher mode does not require one. | ||||
|  * | ||||
|  * Returns: 0 on success, -1 on error | ||||
|  */ | ||||
| int qcrypto_cipher_setiv(QCryptoCipher *cipher, | ||||
|                          const uint8_t *iv, size_t niv, | ||||
|                          Error **errp); | ||||
| 
 | ||||
| #endif /* QCRYPTO_CIPHER_H__ */ | ||||
							
								
								
									
										189
									
								
								include/crypto/hash.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								include/crypto/hash.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,189 @@ | ||||
| /*
 | ||||
|  * QEMU Crypto hash algorithms | ||||
|  * | ||||
|  * Copyright (c) 2015 Red Hat, Inc. | ||||
|  * | ||||
|  * 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 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_HASH_H__ | ||||
| #define QCRYPTO_HASH_H__ | ||||
| 
 | ||||
| #include "qemu-common.h" | ||||
| #include "qapi/error.h" | ||||
| 
 | ||||
| typedef enum { | ||||
|     QCRYPTO_HASH_ALG_MD5, | ||||
|     QCRYPTO_HASH_ALG_SHA1, | ||||
|     QCRYPTO_HASH_ALG_SHA256, | ||||
| 
 | ||||
|     QCRYPTO_HASH_ALG_LAST | ||||
| } QCryptoHashAlgorithm; | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * qcrypto_hash_supports: | ||||
|  * @alg: the hash algorithm | ||||
|  * | ||||
|  * Determine if @alg hash algorithm is supported by the | ||||
|  * current configured build. | ||||
|  * | ||||
|  * Returns: true if the algorithm is supported, false otherwise | ||||
|  */ | ||||
| gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg); | ||||
| 
 | ||||
| /**
 | ||||
|  * qcrypto_hash_bytesv: | ||||
|  * @alg: the hash algorithm | ||||
|  * @iov: the array of memory regions to hash | ||||
|  * @niov: the length of @iov | ||||
|  * @result: pointer to hold output hash | ||||
|  * @resultlen: pointer to hold length of @result | ||||
|  * @errp: pointer to uninitialized error object | ||||
|  * | ||||
|  * Computes the hash across all the memory regions | ||||
|  * present in @iov. The @result pointer will be | ||||
|  * filled with raw bytes representing the computed | ||||
|  * hash, which will have length @resultlen. The | ||||
|  * memory pointer in @result must be released | ||||
|  * with a call to g_free() when no longer required. | ||||
|  * | ||||
|  * Returns: 0 on success, -1 on error | ||||
|  */ | ||||
| int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, | ||||
|                         const struct iovec *iov, | ||||
|                         size_t niov, | ||||
|                         uint8_t **result, | ||||
|                         size_t *resultlen, | ||||
|                         Error **errp); | ||||
| 
 | ||||
| /**
 | ||||
|  * qcrypto_hash_bytes: | ||||
|  * @alg: the hash algorithm | ||||
|  * @buf: the memory region to hash | ||||
|  * @len: the length of @buf | ||||
|  * @result: pointer to hold output hash | ||||
|  * @resultlen: pointer to hold length of @result | ||||
|  * @errp: pointer to uninitialized error object | ||||
|  * | ||||
|  * Computes the hash across all the memory region | ||||
|  * @buf of length @len. The @result pointer will be | ||||
|  * filled with raw bytes representing the computed | ||||
|  * hash, which will have length @resultlen. The | ||||
|  * memory pointer in @result must be released | ||||
|  * with a call to g_free() when no longer required. | ||||
|  * | ||||
|  * Returns: 0 on success, -1 on error | ||||
|  */ | ||||
| int qcrypto_hash_bytes(QCryptoHashAlgorithm alg, | ||||
|                        const char *buf, | ||||
|                        size_t len, | ||||
|                        uint8_t **result, | ||||
|                        size_t *resultlen, | ||||
|                        Error **errp); | ||||
| 
 | ||||
| /**
 | ||||
|  * qcrypto_hash_digestv: | ||||
|  * @alg: the hash algorithm | ||||
|  * @iov: the array of memory regions to hash | ||||
|  * @niov: the length of @iov | ||||
|  * @digest: pointer to hold output hash | ||||
|  * @errp: pointer to uninitialized error object | ||||
|  * | ||||
|  * Computes the hash across all the memory regions | ||||
|  * present in @iov. The @digest pointer will be | ||||
|  * filled with the printable hex digest of the computed | ||||
|  * hash, which will be terminated by '\0'. The | ||||
|  * memory pointer in @digest must be released | ||||
|  * with a call to g_free() when no longer required. | ||||
|  * | ||||
|  * Returns: 0 on success, -1 on error | ||||
|  */ | ||||
| int qcrypto_hash_digestv(QCryptoHashAlgorithm alg, | ||||
|                          const struct iovec *iov, | ||||
|                          size_t niov, | ||||
|                          char **digest, | ||||
|                          Error **errp); | ||||
| 
 | ||||
| /**
 | ||||
|  * qcrypto_hash_digest: | ||||
|  * @alg: the hash algorithm | ||||
|  * @buf: the memory region to hash | ||||
|  * @len: the length of @buf | ||||
|  * @digest: pointer to hold output hash | ||||
|  * @errp: pointer to uninitialized error object | ||||
|  * | ||||
|  * Computes the hash across all the memory region | ||||
|  * @buf of length @len. The @digest pointer will be | ||||
|  * filled with the printable hex digest of the computed | ||||
|  * hash, which will be terminated by '\0'. The | ||||
|  * memory pointer in @digest must be released | ||||
|  * with a call to g_free() when no longer required. | ||||
|  * | ||||
|  * Returns: 0 on success, -1 on error | ||||
|  */ | ||||
| int qcrypto_hash_digest(QCryptoHashAlgorithm alg, | ||||
|                         const char *buf, | ||||
|                         size_t len, | ||||
|                         char **digest, | ||||
|                         Error **errp); | ||||
| 
 | ||||
| /**
 | ||||
|  * qcrypto_hash_base64v: | ||||
|  * @alg: the hash algorithm | ||||
|  * @iov: the array of memory regions to hash | ||||
|  * @niov: the length of @iov | ||||
|  * @base64: pointer to hold output hash | ||||
|  * @errp: pointer to uninitialized error object | ||||
|  * | ||||
|  * Computes the hash across all the memory regions | ||||
|  * present in @iov. The @base64 pointer will be | ||||
|  * filled with the base64 encoding of the computed | ||||
|  * hash, which will be terminated by '\0'. The | ||||
|  * memory pointer in @base64 must be released | ||||
|  * with a call to g_free() when no longer required. | ||||
|  * | ||||
|  * Returns: 0 on success, -1 on error | ||||
|  */ | ||||
| int qcrypto_hash_base64v(QCryptoHashAlgorithm alg, | ||||
|                          const struct iovec *iov, | ||||
|                          size_t niov, | ||||
|                          char **base64, | ||||
|                          Error **errp); | ||||
| 
 | ||||
| /**
 | ||||
|  * qcrypto_hash_base64: | ||||
|  * @alg: the hash algorithm | ||||
|  * @buf: the memory region to hash | ||||
|  * @len: the length of @buf | ||||
|  * @base64: pointer to hold output hash | ||||
|  * @errp: pointer to uninitialized error object | ||||
|  * | ||||
|  * Computes the hash across all the memory region | ||||
|  * @buf of length @len. The @base64 pointer will be | ||||
|  * filled with the base64 encoding of the computed | ||||
|  * hash, which will be terminated by '\0'. The | ||||
|  * memory pointer in @base64 must be released | ||||
|  * with a call to g_free() when no longer required. | ||||
|  * | ||||
|  * Returns: 0 on success, -1 on error | ||||
|  */ | ||||
| int qcrypto_hash_base64(QCryptoHashAlgorithm alg, | ||||
|                         const char *buf, | ||||
|                         size_t len, | ||||
|                         char **base64, | ||||
|                         Error **errp); | ||||
| 
 | ||||
| #endif /* QCRYPTO_HASH_H__ */ | ||||
							
								
								
									
										29
									
								
								include/crypto/init.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								include/crypto/init.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| /*
 | ||||
|  * QEMU Crypto initialization | ||||
|  * | ||||
|  * Copyright (c) 2015 Red Hat, Inc. | ||||
|  * | ||||
|  * 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 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_INIT_H__ | ||||
| #define QCRYPTO_INIT_H__ | ||||
| 
 | ||||
| #include "qemu-common.h" | ||||
| #include "qapi/error.h" | ||||
| 
 | ||||
| int qcrypto_init(Error **errp); | ||||
| 
 | ||||
| #endif /* QCRYPTO_INIT_H__ */ | ||||
| @ -75,8 +75,7 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len, | ||||
|                            void *callback_opaque); | ||||
| int rom_add_elf_program(const char *name, void *data, size_t datasize, | ||||
|                         size_t romsize, hwaddr addr); | ||||
| int rom_load_all(void); | ||||
| void rom_load_done(void); | ||||
| int rom_check_and_register_reset(void); | ||||
| void rom_set_fw(FWCfgState *f); | ||||
| int rom_copy(uint8_t *dest, hwaddr addr, size_t size); | ||||
| void *rom_ptr(hwaddr addr); | ||||
|  | ||||
| @ -14,7 +14,7 @@ | ||||
| #include "cpu.h" | ||||
| #include "exec/exec-all.h" | ||||
| #include "exec/helper-proto.h" | ||||
| #include "qemu/aes.h" | ||||
| #include "crypto/aes.h" | ||||
| 
 | ||||
| union CRYPTO_STATE { | ||||
|     uint8_t    bytes[16]; | ||||
|  | ||||
| @ -20,7 +20,6 @@ | ||||
| #include <math.h> | ||||
| #include "cpu.h" | ||||
| #include "exec/helper-proto.h" | ||||
| #include "qemu/aes.h" | ||||
| #include "qemu/host-utils.h" | ||||
| #include "exec/cpu_ldst.h" | ||||
| 
 | ||||
|  | ||||
| @ -18,7 +18,7 @@ | ||||
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #include "qemu/aes.h" | ||||
| #include "crypto/aes.h" | ||||
| 
 | ||||
| #if SHIFT == 0 | ||||
| #define Reg MMXReg | ||||
|  | ||||
| @ -19,7 +19,7 @@ | ||||
| #include "cpu.h" | ||||
| #include "qemu/host-utils.h" | ||||
| #include "exec/helper-proto.h" | ||||
| #include "qemu/aes.h" | ||||
| #include "crypto/aes.h" | ||||
| 
 | ||||
| #include "helper_regs.h" | ||||
| /*****************************************************************************/ | ||||
|  | ||||
							
								
								
									
										2
									
								
								tests/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								tests/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -10,6 +10,8 @@ rcutorture | ||||
| test-aio | ||||
| test-bitops | ||||
| test-coroutine | ||||
| test-crypto-cipher | ||||
| test-crypto-hash | ||||
| test-cutils | ||||
| test-hbitmap | ||||
| test-int128 | ||||
|  | ||||
| @ -74,6 +74,8 @@ check-unit-y += tests/test-qemu-opts$(EXESUF) | ||||
| gcov-files-test-qemu-opts-y = qom/test-qemu-opts.c | ||||
| check-unit-y += tests/test-write-threshold$(EXESUF) | ||||
| gcov-files-test-write-threshold-y = block/write-threshold.c | ||||
| check-unit-$(CONFIG_GNUTLS_HASH) += tests/test-crypto-hash$(EXESUF) | ||||
| check-unit-y += tests/test-crypto-cipher$(EXESUF) | ||||
| 
 | ||||
| check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh | ||||
| 
 | ||||
| @ -343,6 +345,8 @@ tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y) l | ||||
| 
 | ||||
| tests/test-mul64$(EXESUF): tests/test-mul64.o libqemuutil.a | ||||
| tests/test-bitops$(EXESUF): tests/test-bitops.o libqemuutil.a | ||||
| tests/test-crypto-hash$(EXESUF): tests/test-crypto-hash.o libqemuutil.a libqemustub.a | ||||
| tests/test-crypto-cipher$(EXESUF): tests/test-crypto-cipher.o libqemuutil.a libqemustub.a | ||||
| 
 | ||||
| libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o | ||||
| libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o | ||||
|  | ||||
							
								
								
									
										290
									
								
								tests/test-crypto-cipher.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										290
									
								
								tests/test-crypto-cipher.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,290 @@ | ||||
| /*
 | ||||
|  * QEMU Crypto cipher algorithms | ||||
|  * | ||||
|  * Copyright (c) 2015 Red Hat, Inc. | ||||
|  * | ||||
|  * 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 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 <glib.h> | ||||
| 
 | ||||
| #include "crypto/init.h" | ||||
| #include "crypto/cipher.h" | ||||
| 
 | ||||
| typedef struct QCryptoCipherTestData QCryptoCipherTestData; | ||||
| struct QCryptoCipherTestData { | ||||
|     const char *path; | ||||
|     QCryptoCipherAlgorithm alg; | ||||
|     QCryptoCipherMode mode; | ||||
|     const char *key; | ||||
|     const char *plaintext; | ||||
|     const char *ciphertext; | ||||
|     const char *iv; | ||||
| }; | ||||
| 
 | ||||
| /* AES test data comes from appendix F of:
 | ||||
|  * | ||||
|  * http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
 | ||||
|  */ | ||||
| static QCryptoCipherTestData test_data[] = { | ||||
|     { | ||||
|         /* NIST F.1.1 ECB-AES128.Encrypt */ | ||||
|         .path = "/crypto/cipher/aes-ecb-128", | ||||
|         .alg = QCRYPTO_CIPHER_ALG_AES_128, | ||||
|         .mode = QCRYPTO_CIPHER_MODE_ECB, | ||||
|         .key = "2b7e151628aed2a6abf7158809cf4f3c", | ||||
|         .plaintext = | ||||
|             "6bc1bee22e409f96e93d7e117393172a" | ||||
|             "ae2d8a571e03ac9c9eb76fac45af8e51" | ||||
|             "30c81c46a35ce411e5fbc1191a0a52ef" | ||||
|             "f69f2445df4f9b17ad2b417be66c3710", | ||||
|         .ciphertext = | ||||
|             "3ad77bb40d7a3660a89ecaf32466ef97" | ||||
|             "f5d3d58503b9699de785895a96fdbaaf" | ||||
|             "43b1cd7f598ece23881b00e3ed030688" | ||||
|             "7b0c785e27e8ad3f8223207104725dd4" | ||||
|     }, | ||||
|     { | ||||
|         /* NIST F.1.3 ECB-AES192.Encrypt */ | ||||
|         .path = "/crypto/cipher/aes-ecb-192", | ||||
|         .alg = QCRYPTO_CIPHER_ALG_AES_192, | ||||
|         .mode = QCRYPTO_CIPHER_MODE_ECB, | ||||
|         .key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", | ||||
|         .plaintext  = | ||||
|             "6bc1bee22e409f96e93d7e117393172a" | ||||
|             "ae2d8a571e03ac9c9eb76fac45af8e51" | ||||
|             "30c81c46a35ce411e5fbc1191a0a52ef" | ||||
|             "f69f2445df4f9b17ad2b417be66c3710", | ||||
|         .ciphertext = | ||||
|             "bd334f1d6e45f25ff712a214571fa5cc" | ||||
|             "974104846d0ad3ad7734ecb3ecee4eef" | ||||
|             "ef7afd2270e2e60adce0ba2face6444e" | ||||
|             "9a4b41ba738d6c72fb16691603c18e0e" | ||||
|     }, | ||||
|     { | ||||
|         /* NIST F.1.5 ECB-AES256.Encrypt */ | ||||
|         .path = "/crypto/cipher/aes-ecb-256", | ||||
|         .alg = QCRYPTO_CIPHER_ALG_AES_256, | ||||
|         .mode = QCRYPTO_CIPHER_MODE_ECB, | ||||
|         .key = | ||||
|             "603deb1015ca71be2b73aef0857d7781" | ||||
|             "1f352c073b6108d72d9810a30914dff4", | ||||
|         .plaintext  = | ||||
|             "6bc1bee22e409f96e93d7e117393172a" | ||||
|             "ae2d8a571e03ac9c9eb76fac45af8e51" | ||||
|             "30c81c46a35ce411e5fbc1191a0a52ef" | ||||
|             "f69f2445df4f9b17ad2b417be66c3710", | ||||
|         .ciphertext = | ||||
|             "f3eed1bdb5d2a03c064b5a7e3db181f8" | ||||
|             "591ccb10d410ed26dc5ba74a31362870" | ||||
|             "b6ed21b99ca6f4f9f153e7b1beafed1d" | ||||
|             "23304b7a39f9f3ff067d8d8f9e24ecc7", | ||||
|     }, | ||||
|     { | ||||
|         /* NIST F.2.1 CBC-AES128.Encrypt */ | ||||
|         .path = "/crypto/cipher/aes-cbc-128", | ||||
|         .alg = QCRYPTO_CIPHER_ALG_AES_128, | ||||
|         .mode = QCRYPTO_CIPHER_MODE_CBC, | ||||
|         .key = "2b7e151628aed2a6abf7158809cf4f3c", | ||||
|         .iv = "000102030405060708090a0b0c0d0e0f", | ||||
|         .plaintext  = | ||||
|             "6bc1bee22e409f96e93d7e117393172a" | ||||
|             "ae2d8a571e03ac9c9eb76fac45af8e51" | ||||
|             "30c81c46a35ce411e5fbc1191a0a52ef" | ||||
|             "f69f2445df4f9b17ad2b417be66c3710", | ||||
|         .ciphertext = | ||||
|             "7649abac8119b246cee98e9b12e9197d" | ||||
|             "5086cb9b507219ee95db113a917678b2" | ||||
|             "73bed6b8e3c1743b7116e69e22229516" | ||||
|             "3ff1caa1681fac09120eca307586e1a7", | ||||
|     }, | ||||
|     { | ||||
|         /* NIST F.2.3 CBC-AES128.Encrypt */ | ||||
|         .path = "/crypto/cipher/aes-cbc-192", | ||||
|         .alg = QCRYPTO_CIPHER_ALG_AES_192, | ||||
|         .mode = QCRYPTO_CIPHER_MODE_CBC, | ||||
|         .key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", | ||||
|         .iv = "000102030405060708090a0b0c0d0e0f", | ||||
|         .plaintext  = | ||||
|             "6bc1bee22e409f96e93d7e117393172a" | ||||
|             "ae2d8a571e03ac9c9eb76fac45af8e51" | ||||
|             "30c81c46a35ce411e5fbc1191a0a52ef" | ||||
|             "f69f2445df4f9b17ad2b417be66c3710", | ||||
|         .ciphertext = | ||||
|             "4f021db243bc633d7178183a9fa071e8" | ||||
|             "b4d9ada9ad7dedf4e5e738763f69145a" | ||||
|             "571b242012fb7ae07fa9baac3df102e0" | ||||
|             "08b0e27988598881d920a9e64f5615cd", | ||||
|     }, | ||||
|     { | ||||
|         /* NIST F.2.5 CBC-AES128.Encrypt */ | ||||
|         .path = "/crypto/cipher/aes-cbc-256", | ||||
|         .alg = QCRYPTO_CIPHER_ALG_AES_256, | ||||
|         .mode = QCRYPTO_CIPHER_MODE_CBC, | ||||
|         .key = | ||||
|             "603deb1015ca71be2b73aef0857d7781" | ||||
|             "1f352c073b6108d72d9810a30914dff4", | ||||
|         .iv = "000102030405060708090a0b0c0d0e0f", | ||||
|         .plaintext  = | ||||
|             "6bc1bee22e409f96e93d7e117393172a" | ||||
|             "ae2d8a571e03ac9c9eb76fac45af8e51" | ||||
|             "30c81c46a35ce411e5fbc1191a0a52ef" | ||||
|             "f69f2445df4f9b17ad2b417be66c3710", | ||||
|         .ciphertext = | ||||
|             "f58c4c04d6e5f1ba779eabfb5f7bfbd6" | ||||
|             "9cfc4e967edb808d679f777bc6702c7d" | ||||
|             "39f23369a9d9bacfa530e26304231461" | ||||
|             "b2eb05e2c39be9fcda6c19078c6a9d1b", | ||||
|     }, | ||||
|     { | ||||
|         .path = "/crypto/cipher/des-rfb-ecb-56", | ||||
|         .alg = QCRYPTO_CIPHER_ALG_DES_RFB, | ||||
|         .mode = QCRYPTO_CIPHER_MODE_ECB, | ||||
|         .key = "0123456789abcdef", | ||||
|         .plaintext = | ||||
|             "6bc1bee22e409f96e93d7e117393172a" | ||||
|             "ae2d8a571e03ac9c9eb76fac45af8e51" | ||||
|             "30c81c46a35ce411e5fbc1191a0a52ef" | ||||
|             "f69f2445df4f9b17ad2b417be66c3710", | ||||
|         .ciphertext = | ||||
|             "8f346aaf64eaf24040720d80648c52e7" | ||||
|             "aefc616be53ab1a3d301e69d91e01838" | ||||
|             "ffd29f1bb5596ad94ea2d8e6196b7f09" | ||||
|             "30d8ed0bf2773af36dd82a6280c20926", | ||||
|     }, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static inline int unhex(char c) | ||||
| { | ||||
|     if (c >= 'a' && c <= 'f') { | ||||
|         return 10 + (c - 'a'); | ||||
|     } | ||||
|     if (c >= 'A' && c <= 'F') { | ||||
|         return 10 + (c - 'A'); | ||||
|     } | ||||
|     return c - '0'; | ||||
| } | ||||
| 
 | ||||
| static inline char hex(int i) | ||||
| { | ||||
|     if (i < 10) { | ||||
|         return '0' + i; | ||||
|     } | ||||
|     return 'a' + (i - 10); | ||||
| } | ||||
| 
 | ||||
| static size_t unhex_string(const char *hexstr, | ||||
|                            uint8_t **data) | ||||
| { | ||||
|     size_t len; | ||||
|     size_t i; | ||||
| 
 | ||||
|     if (!hexstr) { | ||||
|         *data = NULL; | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     len = strlen(hexstr); | ||||
|     *data = g_new0(uint8_t, len / 2); | ||||
| 
 | ||||
|     for (i = 0; i < len; i += 2) { | ||||
|         (*data)[i/2] = (unhex(hexstr[i]) << 4) | unhex(hexstr[i+1]); | ||||
|     } | ||||
|     return len / 2; | ||||
| } | ||||
| 
 | ||||
| static char *hex_string(const uint8_t *bytes, | ||||
|                         size_t len) | ||||
| { | ||||
|     char *hexstr = g_new0(char, len * 2 + 1); | ||||
|     size_t i; | ||||
| 
 | ||||
|     for (i = 0; i < len; i++) { | ||||
|         hexstr[i*2] = hex((bytes[i] >> 4) & 0xf); | ||||
|         hexstr[i*2+1] = hex(bytes[i] & 0xf); | ||||
|     } | ||||
|     hexstr[len*2] = '\0'; | ||||
| 
 | ||||
|     return hexstr; | ||||
| } | ||||
| 
 | ||||
| static void test_cipher(const void *opaque) | ||||
| { | ||||
|     const QCryptoCipherTestData *data = opaque; | ||||
| 
 | ||||
|     QCryptoCipher *cipher; | ||||
|     Error *err = NULL; | ||||
|     uint8_t *key, *iv, *ciphertext, *plaintext, *outtext; | ||||
|     size_t nkey, niv, nciphertext, nplaintext; | ||||
|     char *outtexthex; | ||||
| 
 | ||||
|     g_test_message("foo"); | ||||
|     nkey = unhex_string(data->key, &key); | ||||
|     niv = unhex_string(data->iv, &iv); | ||||
|     nciphertext = unhex_string(data->ciphertext, &ciphertext); | ||||
|     nplaintext = unhex_string(data->plaintext, &plaintext); | ||||
| 
 | ||||
|     g_assert(nciphertext == nplaintext); | ||||
| 
 | ||||
|     outtext = g_new0(uint8_t, nciphertext); | ||||
| 
 | ||||
|     cipher = qcrypto_cipher_new( | ||||
|         data->alg, data->mode, | ||||
|         key, nkey, | ||||
|         &err); | ||||
|     g_assert(cipher != NULL); | ||||
|     g_assert(err == NULL); | ||||
| 
 | ||||
| 
 | ||||
|     if (iv) { | ||||
|         g_assert(qcrypto_cipher_setiv(cipher, | ||||
|                                       iv, niv, | ||||
|                                       &err) == 0); | ||||
|         g_assert(err == NULL); | ||||
|     } | ||||
|     g_assert(qcrypto_cipher_encrypt(cipher, | ||||
|                                     plaintext, | ||||
|                                     outtext, | ||||
|                                     nplaintext, | ||||
|                                     &err) == 0); | ||||
|     g_assert(err == NULL); | ||||
| 
 | ||||
|     outtexthex = hex_string(outtext, nciphertext); | ||||
| 
 | ||||
|     g_assert_cmpstr(outtexthex, ==, data->ciphertext); | ||||
| 
 | ||||
|     g_free(outtext); | ||||
|     g_free(outtexthex); | ||||
|     g_free(key); | ||||
|     g_free(iv); | ||||
|     g_free(ciphertext); | ||||
|     g_free(plaintext); | ||||
|     qcrypto_cipher_free(cipher); | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
|     size_t i; | ||||
| 
 | ||||
|     g_test_init(&argc, &argv, NULL); | ||||
| 
 | ||||
|     g_assert(qcrypto_init(NULL) == 0); | ||||
| 
 | ||||
|     for (i = 0; i < G_N_ELEMENTS(test_data); i++) { | ||||
|         g_test_add_data_func(test_data[i].path, &test_data[i], test_cipher); | ||||
|     } | ||||
|     return g_test_run(); | ||||
| } | ||||
							
								
								
									
										209
									
								
								tests/test-crypto-hash.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								tests/test-crypto-hash.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,209 @@ | ||||
| /*
 | ||||
|  * QEMU Crypto hash algorithms | ||||
|  * | ||||
|  * Copyright (c) 2015 Red Hat, Inc. | ||||
|  * | ||||
|  * 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 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 <glib.h> | ||||
| 
 | ||||
| #include "crypto/init.h" | ||||
| #include "crypto/hash.h" | ||||
| 
 | ||||
| #define INPUT_TEXT "Hiss hisss Hissss hiss Hiss hisss Hiss hiss" | ||||
| #define INPUT_TEXT1 "Hiss hisss " | ||||
| #define INPUT_TEXT2 "Hissss hiss " | ||||
| #define INPUT_TEXT3 "Hiss hisss Hiss hiss" | ||||
| 
 | ||||
| #define OUTPUT_MD5 "628d206371563035ab8ef62f492bdec9" | ||||
| #define OUTPUT_SHA1 "b2e74f26758a3a421e509cee045244b78753cc02" | ||||
| #define OUTPUT_SHA256 "bc757abb0436586f392b437e5dd24096" \ | ||||
|                       "f7f224de6b74d4d86e2abc6121b160d0" | ||||
| 
 | ||||
| #define OUTPUT_MD5_B64 "Yo0gY3FWMDWrjvYvSSveyQ==" | ||||
| #define OUTPUT_SHA1_B64 "sudPJnWKOkIeUJzuBFJEt4dTzAI=" | ||||
| #define OUTPUT_SHA256_B64 "vHV6uwQ2WG85K0N+XdJAlvfyJN5rdNTYbiq8YSGxYNA=" | ||||
| 
 | ||||
| static const char *expected_outputs[] = { | ||||
|     [QCRYPTO_HASH_ALG_MD5] = OUTPUT_MD5, | ||||
|     [QCRYPTO_HASH_ALG_SHA1] = OUTPUT_SHA1, | ||||
|     [QCRYPTO_HASH_ALG_SHA256] = OUTPUT_SHA256, | ||||
| }; | ||||
| static const char *expected_outputs_b64[] = { | ||||
|     [QCRYPTO_HASH_ALG_MD5] = OUTPUT_MD5_B64, | ||||
|     [QCRYPTO_HASH_ALG_SHA1] = OUTPUT_SHA1_B64, | ||||
|     [QCRYPTO_HASH_ALG_SHA256] = OUTPUT_SHA256_B64, | ||||
| }; | ||||
| static const int expected_lens[] = { | ||||
|     [QCRYPTO_HASH_ALG_MD5] = 16, | ||||
|     [QCRYPTO_HASH_ALG_SHA1] = 20, | ||||
|     [QCRYPTO_HASH_ALG_SHA256] = 32, | ||||
| }; | ||||
| 
 | ||||
| static const char hex[] = "0123456789abcdef"; | ||||
| 
 | ||||
| /* Test with dynamic allocation */ | ||||
| static void test_hash_alloc(void) | ||||
| { | ||||
|     size_t i; | ||||
| 
 | ||||
|     g_assert(qcrypto_init(NULL) == 0); | ||||
| 
 | ||||
|     for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { | ||||
|         uint8_t *result = NULL; | ||||
|         size_t resultlen = 0; | ||||
|         int ret; | ||||
|         size_t j; | ||||
| 
 | ||||
|         ret = qcrypto_hash_bytes(i, | ||||
|                                  INPUT_TEXT, | ||||
|                                  strlen(INPUT_TEXT), | ||||
|                                  &result, | ||||
|                                  &resultlen, | ||||
|                                  NULL); | ||||
|         g_assert(ret == 0); | ||||
|         g_assert(resultlen == expected_lens[i]); | ||||
| 
 | ||||
|         for (j = 0; j < resultlen; j++) { | ||||
|             g_assert(expected_outputs[i][j * 2] == hex[(result[j] >> 4) & 0xf]); | ||||
|             g_assert(expected_outputs[i][j * 2 + 1] == hex[result[j] & 0xf]); | ||||
|         } | ||||
|         g_free(result); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* Test with caller preallocating */ | ||||
| static void test_hash_prealloc(void) | ||||
| { | ||||
|     size_t i; | ||||
| 
 | ||||
|     g_assert(qcrypto_init(NULL) == 0); | ||||
| 
 | ||||
|     for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { | ||||
|         uint8_t *result; | ||||
|         size_t resultlen; | ||||
|         int ret; | ||||
|         size_t j; | ||||
| 
 | ||||
|         resultlen = expected_lens[i]; | ||||
|         result = g_new0(uint8_t, resultlen); | ||||
| 
 | ||||
|         ret = qcrypto_hash_bytes(i, | ||||
|                                  INPUT_TEXT, | ||||
|                                  strlen(INPUT_TEXT), | ||||
|                                  &result, | ||||
|                                  &resultlen, | ||||
|                                  NULL); | ||||
|         g_assert(ret == 0); | ||||
| 
 | ||||
|         g_assert(resultlen == expected_lens[i]); | ||||
|         for (j = 0; j < resultlen; j++) { | ||||
|             g_assert(expected_outputs[i][j * 2] == hex[(result[j] >> 4) & 0xf]); | ||||
|             g_assert(expected_outputs[i][j * 2 + 1] == hex[result[j] & 0xf]); | ||||
|         } | ||||
|         g_free(result); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Test with dynamic allocation */ | ||||
| static void test_hash_iov(void) | ||||
| { | ||||
|     size_t i; | ||||
| 
 | ||||
|     g_assert(qcrypto_init(NULL) == 0); | ||||
| 
 | ||||
|     for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { | ||||
|         struct iovec iov[3] = { | ||||
|             { .iov_base = (char *)INPUT_TEXT1, .iov_len = strlen(INPUT_TEXT1) }, | ||||
|             { .iov_base = (char *)INPUT_TEXT2, .iov_len = strlen(INPUT_TEXT2) }, | ||||
|             { .iov_base = (char *)INPUT_TEXT3, .iov_len = strlen(INPUT_TEXT3) }, | ||||
|         }; | ||||
|         uint8_t *result = NULL; | ||||
|         size_t resultlen = 0; | ||||
|         int ret; | ||||
|         size_t j; | ||||
| 
 | ||||
|         ret = qcrypto_hash_bytesv(i, | ||||
|                                   iov, 3, | ||||
|                                   &result, | ||||
|                                   &resultlen, | ||||
|                                   NULL); | ||||
|         g_assert(ret == 0); | ||||
|         g_assert(resultlen == expected_lens[i]); | ||||
|         for (j = 0; j < resultlen; j++) { | ||||
|             g_assert(expected_outputs[i][j * 2] == hex[(result[j] >> 4) & 0xf]); | ||||
|             g_assert(expected_outputs[i][j * 2 + 1] == hex[result[j] & 0xf]); | ||||
|         } | ||||
|         g_free(result); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Test with printable hashing */ | ||||
| static void test_hash_digest(void) | ||||
| { | ||||
|     size_t i; | ||||
| 
 | ||||
|     g_assert(qcrypto_init(NULL) == 0); | ||||
| 
 | ||||
|     for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { | ||||
|         int ret; | ||||
|         char *digest; | ||||
| 
 | ||||
|         ret = qcrypto_hash_digest(i, | ||||
|                                   INPUT_TEXT, | ||||
|                                   strlen(INPUT_TEXT), | ||||
|                                   &digest, | ||||
|                                   NULL); | ||||
|         g_assert(ret == 0); | ||||
|         g_assert(g_str_equal(digest, expected_outputs[i])); | ||||
|         g_free(digest); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* Test with base64 encoding */ | ||||
| static void test_hash_base64(void) | ||||
| { | ||||
|     size_t i; | ||||
| 
 | ||||
|     g_assert(qcrypto_init(NULL) == 0); | ||||
| 
 | ||||
|     for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { | ||||
|         int ret; | ||||
|         char *digest; | ||||
| 
 | ||||
|         ret = qcrypto_hash_base64(i, | ||||
|                                   INPUT_TEXT, | ||||
|                                   strlen(INPUT_TEXT), | ||||
|                                   &digest, | ||||
|                                   NULL); | ||||
|         g_assert(ret == 0); | ||||
|         g_assert(g_str_equal(digest, expected_outputs_b64[i])); | ||||
|         g_free(digest); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char **argv) | ||||
| { | ||||
|     g_test_init(&argc, &argv, NULL); | ||||
|     g_test_add_func("/crypto/hash/iov", test_hash_iov); | ||||
|     g_test_add_func("/crypto/hash/alloc", test_hash_alloc); | ||||
|     g_test_add_func("/crypto/hash/prealloc", test_hash_prealloc); | ||||
|     g_test_add_func("/crypto/hash/digest", test_hash_digest); | ||||
|     g_test_add_func("/crypto/hash/base64", test_hash_base64); | ||||
|     return g_test_run(); | ||||
| } | ||||
| @ -1,10 +1,10 @@ | ||||
| vnc-obj-y += vnc.o d3des.o | ||||
| vnc-obj-y += vnc.o | ||||
| vnc-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o | ||||
| vnc-obj-y += vnc-enc-tight.o vnc-palette.o | ||||
| vnc-obj-y += vnc-enc-zrle.o | ||||
| vnc-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o | ||||
| vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o | ||||
| vnc-obj-$(CONFIG_VNC_WS) += vnc-ws.o | ||||
| vnc-obj-y += vnc-ws.o | ||||
| vnc-obj-y += vnc-jobs.o | ||||
| 
 | ||||
| common-obj-y += keymaps.o console.o cursor.o qemu-pixman.o | ||||
|  | ||||
							
								
								
									
										22
									
								
								ui/vnc-ws.c
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								ui/vnc-ws.c
									
									
									
									
									
								
							| @ -20,6 +20,7 @@ | ||||
| 
 | ||||
| #include "vnc.h" | ||||
| #include "qemu/main-loop.h" | ||||
| #include "crypto/hash.h" | ||||
| 
 | ||||
| #ifdef CONFIG_VNC_TLS | ||||
| #include "qemu/sockets.h" | ||||
| @ -203,24 +204,21 @@ static char *vncws_extract_handshake_entry(const char *handshake, | ||||
| static void vncws_send_handshake_response(VncState *vs, const char* key) | ||||
| { | ||||
|     char combined_key[WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1]; | ||||
|     unsigned char hash[SHA1_DIGEST_LEN]; | ||||
|     size_t hash_size = sizeof(hash); | ||||
|     char *accept = NULL, *response = NULL; | ||||
|     gnutls_datum_t in; | ||||
|     int ret; | ||||
|     Error *err = NULL; | ||||
| 
 | ||||
|     g_strlcpy(combined_key, key, WS_CLIENT_KEY_LEN + 1); | ||||
|     g_strlcat(combined_key, WS_GUID, WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1); | ||||
| 
 | ||||
|     /* hash and encode it */ | ||||
|     in.data = (void *)combined_key; | ||||
|     in.size = WS_CLIENT_KEY_LEN + WS_GUID_LEN; | ||||
|     ret = gnutls_fingerprint(GNUTLS_DIG_SHA1, &in, hash, &hash_size); | ||||
|     if (ret == GNUTLS_E_SUCCESS && hash_size <= SHA1_DIGEST_LEN) { | ||||
|         accept = g_base64_encode(hash, hash_size); | ||||
|     } | ||||
|     if (accept == NULL) { | ||||
|         VNC_DEBUG("Hashing Websocket combined key failed\n"); | ||||
|     if (qcrypto_hash_base64(QCRYPTO_HASH_ALG_SHA1, | ||||
|                             combined_key, | ||||
|                             WS_CLIENT_KEY_LEN + WS_GUID_LEN, | ||||
|                             &accept, | ||||
|                             &err) < 0) { | ||||
|         VNC_DEBUG("Hashing Websocket combined key failed %s\n", | ||||
|                   error_get_pretty(err)); | ||||
|         error_free(err); | ||||
|         vnc_client_error(vs); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @ -21,8 +21,6 @@ | ||||
| #ifndef __QEMU_UI_VNC_WS_H | ||||
| #define __QEMU_UI_VNC_WS_H | ||||
| 
 | ||||
| #include <gnutls/gnutls.h> | ||||
| 
 | ||||
| #define B64LEN(__x) (((__x + 2) / 3) * 12 / 3) | ||||
| #define SHA1_DIGEST_LEN 20 | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										119
									
								
								ui/vnc.c
									
									
									
									
									
								
							
							
						
						
									
										119
									
								
								ui/vnc.c
									
									
									
									
									
								
							| @ -40,6 +40,7 @@ | ||||
| #include "qemu/osdep.h" | ||||
| #include "ui/input.h" | ||||
| #include "qapi-event.h" | ||||
| #include "crypto/hash.h" | ||||
| 
 | ||||
| #define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT | ||||
| #define VNC_REFRESH_INTERVAL_INC  50 | ||||
| @ -48,7 +49,7 @@ static const struct timeval VNC_REFRESH_STATS = { 0, 500000 }; | ||||
| static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 }; | ||||
| 
 | ||||
| #include "vnc_keysym.h" | ||||
| #include "d3des.h" | ||||
| #include "crypto/cipher.h" | ||||
| 
 | ||||
| static QTAILQ_HEAD(, VncDisplay) vnc_displays = | ||||
|     QTAILQ_HEAD_INITIALIZER(vnc_displays); | ||||
| @ -355,9 +356,7 @@ static VncClientInfo *qmp_query_vnc_client(const VncState *client) | ||||
|     info->base->host = g_strdup(host); | ||||
|     info->base->service = g_strdup(serv); | ||||
|     info->base->family = inet_netfamily(sa.ss_family); | ||||
| #ifdef CONFIG_VNC_WS | ||||
|     info->base->websocket = client->websocket; | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_VNC_TLS | ||||
|     if (client->tls.session && client->tls.dname) { | ||||
| @ -582,12 +581,10 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp) | ||||
|             info->server = qmp_query_server_entry(vd->lsock, false, | ||||
|                                                   info->server); | ||||
|         } | ||||
| #ifdef CONFIG_VNC_WS | ||||
|         if (vd->lwebsock != -1) { | ||||
|             info->server = qmp_query_server_entry(vd->lwebsock, true, | ||||
|                                                   info->server); | ||||
|         } | ||||
| #endif | ||||
| 
 | ||||
|         item = g_new0(VncInfo2List, 1); | ||||
|         item->value = info; | ||||
| @ -1231,10 +1228,8 @@ void vnc_disconnect_finish(VncState *vs) | ||||
| 
 | ||||
|     buffer_free(&vs->input); | ||||
|     buffer_free(&vs->output); | ||||
| #ifdef CONFIG_VNC_WS | ||||
|     buffer_free(&vs->ws_input); | ||||
|     buffer_free(&vs->ws_output); | ||||
| #endif /* CONFIG_VNC_WS */ | ||||
| 
 | ||||
|     qapi_free_VncClientInfo(vs->info); | ||||
| 
 | ||||
| @ -1413,12 +1408,9 @@ static void vnc_client_write_locked(void *opaque) | ||||
|     } else | ||||
| #endif /* CONFIG_VNC_SASL */ | ||||
|     { | ||||
| #ifdef CONFIG_VNC_WS | ||||
|         if (vs->encode_ws) { | ||||
|             vnc_client_write_ws(vs); | ||||
|         } else | ||||
| #endif /* CONFIG_VNC_WS */ | ||||
|         { | ||||
|         } else { | ||||
|             vnc_client_write_plain(vs); | ||||
|         } | ||||
|     } | ||||
| @ -1429,11 +1421,7 @@ void vnc_client_write(void *opaque) | ||||
|     VncState *vs = opaque; | ||||
| 
 | ||||
|     vnc_lock_output(vs); | ||||
|     if (vs->output.offset | ||||
| #ifdef CONFIG_VNC_WS | ||||
|             || vs->ws_output.offset | ||||
| #endif | ||||
|             ) { | ||||
|     if (vs->output.offset || vs->ws_output.offset) { | ||||
|         vnc_client_write_locked(opaque); | ||||
|     } else if (vs->csock != -1) { | ||||
|         qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); | ||||
| @ -1539,7 +1527,6 @@ void vnc_client_read(void *opaque) | ||||
|         ret = vnc_client_read_sasl(vs); | ||||
|     else | ||||
| #endif /* CONFIG_VNC_SASL */ | ||||
| #ifdef CONFIG_VNC_WS | ||||
|         if (vs->encode_ws) { | ||||
|             ret = vnc_client_read_ws(vs); | ||||
|             if (ret == -1) { | ||||
| @ -1549,10 +1536,8 @@ void vnc_client_read(void *opaque) | ||||
|                 vnc_client_error(vs); | ||||
|                 return; | ||||
|             } | ||||
|         } else | ||||
| #endif /* CONFIG_VNC_WS */ | ||||
|         { | ||||
|         ret = vnc_client_read_plain(vs); | ||||
|         } else { | ||||
|             ret = vnc_client_read_plain(vs); | ||||
|         } | ||||
|     if (!ret) { | ||||
|         if (vs->csock == -1) | ||||
| @ -1624,11 +1609,8 @@ void vnc_write_u8(VncState *vs, uint8_t value) | ||||
| void vnc_flush(VncState *vs) | ||||
| { | ||||
|     vnc_lock_output(vs); | ||||
|     if (vs->csock != -1 && (vs->output.offset | ||||
| #ifdef CONFIG_VNC_WS | ||||
|                 || vs->ws_output.offset | ||||
| #endif | ||||
|                 )) { | ||||
|     if (vs->csock != -1 && (vs->output.offset || | ||||
|                             vs->ws_output.offset)) { | ||||
|         vnc_client_write_locked(vs); | ||||
|     } | ||||
|     vnc_unlock_output(vs); | ||||
| @ -2535,9 +2517,11 @@ static void make_challenge(VncState *vs) | ||||
| static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len) | ||||
| { | ||||
|     unsigned char response[VNC_AUTH_CHALLENGE_SIZE]; | ||||
|     int i, j, pwlen; | ||||
|     size_t i, pwlen; | ||||
|     unsigned char key[8]; | ||||
|     time_t now = time(NULL); | ||||
|     QCryptoCipher *cipher; | ||||
|     Error *err = NULL; | ||||
| 
 | ||||
|     if (!vs->vd->password) { | ||||
|         VNC_DEBUG("No password configured on server"); | ||||
| @ -2554,9 +2538,29 @@ static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len) | ||||
|     pwlen = strlen(vs->vd->password); | ||||
|     for (i=0; i<sizeof(key); i++) | ||||
|         key[i] = i<pwlen ? vs->vd->password[i] : 0; | ||||
|     deskey(key, EN0); | ||||
|     for (j = 0; j < VNC_AUTH_CHALLENGE_SIZE; j += 8) | ||||
|         des(response+j, response+j); | ||||
| 
 | ||||
|     cipher = qcrypto_cipher_new( | ||||
|         QCRYPTO_CIPHER_ALG_DES_RFB, | ||||
|         QCRYPTO_CIPHER_MODE_ECB, | ||||
|         key, G_N_ELEMENTS(key), | ||||
|         &err); | ||||
|     if (!cipher) { | ||||
|         VNC_DEBUG("Cannot initialize cipher %s", | ||||
|                   error_get_pretty(err)); | ||||
|         error_free(err); | ||||
|         goto reject; | ||||
|     } | ||||
| 
 | ||||
|     if (qcrypto_cipher_decrypt(cipher, | ||||
|                                vs->challenge, | ||||
|                                response, | ||||
|                                VNC_AUTH_CHALLENGE_SIZE, | ||||
|                                &err) < 0) { | ||||
|         VNC_DEBUG("Cannot encrypt challenge %s", | ||||
|                   error_get_pretty(err)); | ||||
|         error_free(err); | ||||
|         goto reject; | ||||
|     } | ||||
| 
 | ||||
|     /* Compare expected vs actual challenge response */ | ||||
|     if (memcmp(response, data, VNC_AUTH_CHALLENGE_SIZE) != 0) { | ||||
| @ -3019,7 +3023,6 @@ static void vnc_connect(VncDisplay *vd, int csock, | ||||
|     VNC_DEBUG("New client on socket %d\n", csock); | ||||
|     update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE); | ||||
|     qemu_set_nonblock(vs->csock); | ||||
| #ifdef CONFIG_VNC_WS | ||||
|     if (websocket) { | ||||
|         vs->websocket = 1; | ||||
| #ifdef CONFIG_VNC_TLS | ||||
| @ -3031,7 +3034,6 @@ static void vnc_connect(VncDisplay *vd, int csock, | ||||
|             qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs); | ||||
|         } | ||||
|     } else | ||||
| #endif /* CONFIG_VNC_WS */ | ||||
|     { | ||||
|         qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); | ||||
|     } | ||||
| @ -3040,10 +3042,7 @@ static void vnc_connect(VncDisplay *vd, int csock, | ||||
|     vnc_qmp_event(vs, QAPI_EVENT_VNC_CONNECTED); | ||||
|     vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING); | ||||
| 
 | ||||
| #ifdef CONFIG_VNC_WS | ||||
|     if (!vs->websocket) | ||||
| #endif | ||||
|     { | ||||
|     if (!vs->websocket) { | ||||
|         vnc_init_state(vs); | ||||
|     } | ||||
| 
 | ||||
| @ -3099,12 +3098,9 @@ static void vnc_listen_read(void *opaque, bool websocket) | ||||
| 
 | ||||
|     /* Catch-up */ | ||||
|     graphic_hw_update(vs->dcl.con); | ||||
| #ifdef CONFIG_VNC_WS | ||||
|     if (websocket) { | ||||
|         csock = qemu_accept(vs->lwebsock, (struct sockaddr *)&addr, &addrlen); | ||||
|     } else | ||||
| #endif /* CONFIG_VNC_WS */ | ||||
|     { | ||||
|     } else { | ||||
|         csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); | ||||
|     } | ||||
| 
 | ||||
| @ -3119,12 +3115,10 @@ static void vnc_listen_regular_read(void *opaque) | ||||
|     vnc_listen_read(opaque, false); | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_VNC_WS | ||||
| static void vnc_listen_websocket_read(void *opaque) | ||||
| { | ||||
|     vnc_listen_read(opaque, true); | ||||
| } | ||||
| #endif /* CONFIG_VNC_WS */ | ||||
| 
 | ||||
| static const DisplayChangeListenerOps dcl_ops = { | ||||
|     .dpy_name             = "vnc", | ||||
| @ -3150,9 +3144,7 @@ void vnc_display_init(const char *id) | ||||
|     QTAILQ_INSERT_TAIL(&vnc_displays, vs, next); | ||||
| 
 | ||||
|     vs->lsock = -1; | ||||
| #ifdef CONFIG_VNC_WS | ||||
|     vs->lwebsock = -1; | ||||
| #endif | ||||
| 
 | ||||
|     QTAILQ_INIT(&vs->clients); | ||||
|     vs->expires = TIME_MAX; | ||||
| @ -3186,14 +3178,12 @@ static void vnc_display_close(VncDisplay *vs) | ||||
|         close(vs->lsock); | ||||
|         vs->lsock = -1; | ||||
|     } | ||||
| #ifdef CONFIG_VNC_WS | ||||
|     vs->ws_enabled = false; | ||||
|     if (vs->lwebsock != -1) { | ||||
|         qemu_set_fd_handler(vs->lwebsock, NULL, NULL, NULL); | ||||
|         close(vs->lwebsock); | ||||
|         vs->lwebsock = -1; | ||||
|     } | ||||
| #endif /* CONFIG_VNC_WS */ | ||||
|     vs->auth = VNC_AUTH_INVALID; | ||||
|     vs->subauth = VNC_AUTH_INVALID; | ||||
| #ifdef CONFIG_VNC_TLS | ||||
| @ -3516,12 +3506,20 @@ void vnc_display_open(const char *id, Error **errp) | ||||
|     } | ||||
| 
 | ||||
|     password = qemu_opt_get_bool(opts, "password", false); | ||||
|     if (password && fips_get_state()) { | ||||
|         error_setg(errp, | ||||
|                    "VNC password auth disabled due to FIPS mode, " | ||||
|                    "consider using the VeNCrypt or SASL authentication " | ||||
|                    "methods as an alternative"); | ||||
|         goto fail; | ||||
|     if (password) { | ||||
|         if (fips_get_state()) { | ||||
|             error_setg(errp, | ||||
|                        "VNC password auth disabled due to FIPS mode, " | ||||
|                        "consider using the VeNCrypt or SASL authentication " | ||||
|                        "methods as an alternative"); | ||||
|             goto fail; | ||||
|         } | ||||
|         if (!qcrypto_cipher_supports( | ||||
|                 QCRYPTO_CIPHER_ALG_DES_RFB)) { | ||||
|             error_setg(errp, | ||||
|                        "Cipher backend does not support DES RFB algorithm"); | ||||
|             goto fail; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     reverse = qemu_opt_get_bool(opts, "reverse", false); | ||||
| @ -3579,13 +3577,12 @@ void vnc_display_open(const char *id, Error **errp) | ||||
| 
 | ||||
|     websocket = qemu_opt_get(opts, "websocket"); | ||||
|     if (websocket) { | ||||
| #ifdef CONFIG_VNC_WS | ||||
|         vs->ws_enabled = true; | ||||
|         qemu_opt_set(wsopts, "port", websocket, &error_abort); | ||||
| #else /* ! CONFIG_VNC_WS */ | ||||
|         error_setg(errp, "Websockets protocol requires gnutls support"); | ||||
|         goto fail; | ||||
| #endif /* ! CONFIG_VNC_WS */ | ||||
|         if (!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) { | ||||
|             error_setg(errp, "SHA1 hash support is required for websockets"); | ||||
|             goto fail; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| #ifdef CONFIG_VNC_JPEG | ||||
| @ -3668,9 +3665,7 @@ void vnc_display_open(const char *id, Error **errp) | ||||
|         /* connect to viewer */ | ||||
|         int csock; | ||||
|         vs->lsock = -1; | ||||
| #ifdef CONFIG_VNC_WS | ||||
|         vs->lwebsock = -1; | ||||
| #endif | ||||
|         if (strncmp(vnc, "unix:", 5) == 0) { | ||||
|             csock = unix_connect(vnc+5, errp); | ||||
|         } else { | ||||
| @ -3693,7 +3688,6 @@ void vnc_display_open(const char *id, Error **errp) | ||||
|             if (vs->lsock < 0) { | ||||
|                 goto fail; | ||||
|             } | ||||
| #ifdef CONFIG_VNC_WS | ||||
|             if (vs->ws_enabled) { | ||||
|                 vs->lwebsock = inet_listen_opts(wsopts, 0, errp); | ||||
|                 if (vs->lwebsock < 0) { | ||||
| @ -3704,16 +3698,13 @@ void vnc_display_open(const char *id, Error **errp) | ||||
|                     goto fail; | ||||
|                 } | ||||
|             } | ||||
| #endif /* CONFIG_VNC_WS */ | ||||
|         } | ||||
|         vs->enabled = true; | ||||
|         qemu_set_fd_handler(vs->lsock, vnc_listen_regular_read, NULL, vs); | ||||
| #ifdef CONFIG_VNC_WS | ||||
|         if (vs->ws_enabled) { | ||||
|             qemu_set_fd_handler(vs->lwebsock, vnc_listen_websocket_read, | ||||
|                                 NULL, vs); | ||||
|         } | ||||
| #endif /* CONFIG_VNC_WS */ | ||||
|     } | ||||
|     qemu_opts_del(sopts); | ||||
|     qemu_opts_del(wsopts); | ||||
| @ -3723,9 +3714,7 @@ fail: | ||||
|     qemu_opts_del(sopts); | ||||
|     qemu_opts_del(wsopts); | ||||
|     vs->enabled = false; | ||||
| #ifdef CONFIG_VNC_WS | ||||
|     vs->ws_enabled = false; | ||||
| #endif /* CONFIG_VNC_WS */ | ||||
| } | ||||
| 
 | ||||
| void vnc_display_add_client(const char *id, int csock, bool skipauth) | ||||
|  | ||||
							
								
								
									
										8
									
								
								ui/vnc.h
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								ui/vnc.h
									
									
									
									
									
								
							| @ -108,9 +108,7 @@ typedef struct VncDisplay VncDisplay; | ||||
| #ifdef CONFIG_VNC_SASL | ||||
| #include "vnc-auth-sasl.h" | ||||
| #endif | ||||
| #ifdef CONFIG_VNC_WS | ||||
| #include "vnc-ws.h" | ||||
| #endif | ||||
| 
 | ||||
| struct VncRectStat | ||||
| { | ||||
| @ -156,10 +154,8 @@ struct VncDisplay | ||||
|     int connections_limit; | ||||
|     VncSharePolicy share_policy; | ||||
|     int lsock; | ||||
| #ifdef CONFIG_VNC_WS | ||||
|     int lwebsock; | ||||
|     bool ws_enabled; | ||||
| #endif | ||||
|     DisplaySurface *ds; | ||||
|     DisplayChangeListener dcl; | ||||
|     kbd_layout_t *kbd_layout; | ||||
| @ -294,21 +290,17 @@ struct VncState | ||||
| #ifdef CONFIG_VNC_SASL | ||||
|     VncStateSASL sasl; | ||||
| #endif | ||||
| #ifdef CONFIG_VNC_WS | ||||
|     bool encode_ws; | ||||
|     bool websocket; | ||||
| #endif /* CONFIG_VNC_WS */ | ||||
| 
 | ||||
|     VncClientInfo *info; | ||||
| 
 | ||||
|     Buffer output; | ||||
|     Buffer input; | ||||
| #ifdef CONFIG_VNC_WS | ||||
|     Buffer ws_input; | ||||
|     Buffer ws_output; | ||||
|     size_t ws_payload_remain; | ||||
|     WsMask ws_payload_mask; | ||||
| #endif | ||||
|     /* current output mode information */ | ||||
|     VncWritePixels *write_pixels; | ||||
|     PixelFormat client_pf; | ||||
|  | ||||
| @ -9,7 +9,7 @@ util-obj-y += acl.o | ||||
| util-obj-y += error.o qemu-error.o | ||||
| util-obj-$(CONFIG_POSIX) += compatfd.o | ||||
| util-obj-y += id.o | ||||
| util-obj-y += iov.o aes.o qemu-config.o qemu-sockets.o uri.o notify.o | ||||
| util-obj-y += iov.o qemu-config.o qemu-sockets.o uri.o notify.o | ||||
| util-obj-y += qemu-option.o qemu-progress.o | ||||
| util-obj-y += hexdump.o | ||||
| util-obj-y += crc32c.o | ||||
|  | ||||
							
								
								
									
										18
									
								
								vl.c
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								vl.c
									
									
									
									
									
								
							| @ -121,6 +121,7 @@ int main(int argc, char **argv) | ||||
| #include "qom/object_interfaces.h" | ||||
| #include "qapi-event.h" | ||||
| #include "exec/semihost.h" | ||||
| #include "crypto/init.h" | ||||
| 
 | ||||
| #define MAX_VIRTIO_CONSOLES 1 | ||||
| #define MAX_SCLP_CONSOLES 1 | ||||
| @ -2976,6 +2977,7 @@ int main(int argc, char **argv, char **envp) | ||||
|     uint64_t ram_slots = 0; | ||||
|     FILE *vmstate_dump_file = NULL; | ||||
|     Error *main_loop_err = NULL; | ||||
|     Error *err = NULL; | ||||
| 
 | ||||
|     qemu_init_cpu_loop(); | ||||
|     qemu_mutex_lock_iothread(); | ||||
| @ -3019,6 +3021,11 @@ int main(int argc, char **argv, char **envp) | ||||
| 
 | ||||
|     runstate_init(); | ||||
| 
 | ||||
|     if (qcrypto_init(&err) < 0) { | ||||
|         fprintf(stderr, "Cannot initialize crypto: %s\n", | ||||
|                 error_get_pretty(err)); | ||||
|         exit(1); | ||||
|     } | ||||
|     rtc_clock = QEMU_CLOCK_HOST; | ||||
| 
 | ||||
|     QLIST_INIT (&vm_change_state_head); | ||||
| @ -4597,18 +4604,15 @@ int main(int argc, char **argv, char **envp) | ||||
| 
 | ||||
|     qdev_machine_creation_done(); | ||||
| 
 | ||||
|     if (rom_load_all() != 0) { | ||||
|         fprintf(stderr, "rom loading failed\n"); | ||||
|         exit(1); | ||||
|     } | ||||
| 
 | ||||
|     /* TODO: once all bus devices are qdevified, this should be done
 | ||||
|      * when bus is created by qdev.c */ | ||||
|     qemu_register_reset(qbus_reset_all_fn, sysbus_get_default()); | ||||
|     qemu_run_machine_init_done_notifiers(); | ||||
| 
 | ||||
|     /* Done notifiers can load ROMs */ | ||||
|     rom_load_done(); | ||||
|     if (rom_check_and_register_reset() != 0) { | ||||
|         fprintf(stderr, "rom check and register reset failed\n"); | ||||
|         exit(1); | ||||
|     } | ||||
| 
 | ||||
|     qemu_system_reset(VMRESET_SILENT); | ||||
|     if (loadvm) { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Peter Maydell
						Peter Maydell