cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

u2f-emulated.c (11644B)


      1/*
      2 * U2F USB Emulated device.
      3 *
      4 * Copyright (c) 2020 César Belley <cesar.belley@lse.epita.fr>
      5 * Written by César Belley <cesar.belley@lse.epita.fr>
      6 *
      7 * Permission is hereby granted, free of charge, to any person obtaining a copy
      8 * of this software and associated documentation files (the "Software"), to deal
      9 * in the Software without restriction, including without limitation the rights
     10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     11 * copies of the Software, and to permit persons to whom the Software is
     12 * furnished to do so, subject to the following conditions:
     13 *
     14 * The above copyright notice and this permission notice shall be included in
     15 * all copies or substantial portions of the Software.
     16 *
     17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     23 * THE SOFTWARE.
     24 */
     25
     26#include "qemu/osdep.h"
     27#include "qemu/module.h"
     28#include "qemu/thread.h"
     29#include "qemu/main-loop.h"
     30#include "qapi/error.h"
     31#include "hw/usb.h"
     32#include "hw/qdev-properties.h"
     33
     34#include <u2f-emu/u2f-emu.h>
     35
     36#include "u2f.h"
     37
     38/* Counter which sync with a file */
     39struct synced_counter {
     40    /* Emulated device counter */
     41    struct u2f_emu_vdev_counter vdev_counter;
     42
     43    /* Private attributes */
     44    uint32_t value;
     45    FILE *fp;
     46};
     47
     48static void counter_increment(struct u2f_emu_vdev_counter *vdev_counter)
     49{
     50    struct synced_counter *counter = (struct synced_counter *)vdev_counter;
     51    ++counter->value;
     52
     53    /* Write back */
     54    if (fseek(counter->fp, 0, SEEK_SET) == -1) {
     55        return;
     56    }
     57    fprintf(counter->fp, "%u\n", counter->value);
     58}
     59
     60static uint32_t counter_read(struct u2f_emu_vdev_counter *vdev_counter)
     61{
     62    struct synced_counter *counter = (struct synced_counter *)vdev_counter;
     63    return counter->value;
     64}
     65
     66typedef struct U2FEmulatedState U2FEmulatedState;
     67
     68#define PENDING_OUT_NUM 32
     69
     70struct U2FEmulatedState {
     71    U2FKeyState base;
     72
     73    /* U2F virtual emulated device */
     74    u2f_emu_vdev *vdev;
     75    QemuMutex vdev_mutex;
     76
     77    /* Properties */
     78    char *dir;
     79    char *cert;
     80    char *privkey;
     81    char *entropy;
     82    char *counter;
     83    struct synced_counter synced_counter;
     84
     85    /* Pending packets received from the guest */
     86    uint8_t pending_out[PENDING_OUT_NUM][U2FHID_PACKET_SIZE];
     87    uint8_t pending_out_start;
     88    uint8_t pending_out_end;
     89    uint8_t pending_out_num;
     90    QemuMutex pending_out_mutex;
     91
     92    /* Emulation thread and sync */
     93    QemuCond key_cond;
     94    QemuMutex key_mutex;
     95    QemuThread key_thread;
     96    bool stop_thread;
     97    EventNotifier notifier;
     98};
     99
    100#define TYPE_U2F_EMULATED "u2f-emulated"
    101#define EMULATED_U2F_KEY(obj) \
    102    OBJECT_CHECK(U2FEmulatedState, (obj), TYPE_U2F_EMULATED)
    103
    104static void u2f_emulated_reset(U2FEmulatedState *key)
    105{
    106    key->pending_out_start = 0;
    107    key->pending_out_end = 0;
    108    key->pending_out_num = 0;
    109}
    110
    111static void u2f_pending_out_add(U2FEmulatedState *key,
    112                                const uint8_t packet[U2FHID_PACKET_SIZE])
    113{
    114    int index;
    115
    116    if (key->pending_out_num >= PENDING_OUT_NUM) {
    117        return;
    118    }
    119
    120    index = key->pending_out_end;
    121    key->pending_out_end = (index + 1) % PENDING_OUT_NUM;
    122    ++key->pending_out_num;
    123
    124    memcpy(&key->pending_out[index], packet, U2FHID_PACKET_SIZE);
    125}
    126
    127static uint8_t *u2f_pending_out_get(U2FEmulatedState *key)
    128{
    129    int index;
    130
    131    if (key->pending_out_num == 0) {
    132        return NULL;
    133    }
    134
    135    index  = key->pending_out_start;
    136    key->pending_out_start = (index + 1) % PENDING_OUT_NUM;
    137    --key->pending_out_num;
    138
    139    return key->pending_out[index];
    140}
    141
    142static void u2f_emulated_recv_from_guest(U2FKeyState *base,
    143                                    const uint8_t packet[U2FHID_PACKET_SIZE])
    144{
    145    U2FEmulatedState *key = EMULATED_U2F_KEY(base);
    146
    147    qemu_mutex_lock(&key->pending_out_mutex);
    148    u2f_pending_out_add(key, packet);
    149    qemu_mutex_unlock(&key->pending_out_mutex);
    150
    151    qemu_mutex_lock(&key->key_mutex);
    152    qemu_cond_signal(&key->key_cond);
    153    qemu_mutex_unlock(&key->key_mutex);
    154}
    155
    156static void *u2f_emulated_thread(void* arg)
    157{
    158    U2FEmulatedState *key = arg;
    159    uint8_t packet[U2FHID_PACKET_SIZE];
    160    uint8_t *packet_out = NULL;
    161
    162
    163    while (true) {
    164        /* Wait signal */
    165        qemu_mutex_lock(&key->key_mutex);
    166        qemu_cond_wait(&key->key_cond, &key->key_mutex);
    167        qemu_mutex_unlock(&key->key_mutex);
    168
    169        /* Exit thread check */
    170        if (key->stop_thread) {
    171            key->stop_thread = false;
    172            break;
    173        }
    174
    175        qemu_mutex_lock(&key->pending_out_mutex);
    176        packet_out = u2f_pending_out_get(key);
    177        if (packet_out == NULL) {
    178            qemu_mutex_unlock(&key->pending_out_mutex);
    179            continue;
    180        }
    181        memcpy(packet, packet_out, U2FHID_PACKET_SIZE);
    182        qemu_mutex_unlock(&key->pending_out_mutex);
    183
    184        qemu_mutex_lock(&key->vdev_mutex);
    185        u2f_emu_vdev_send(key->vdev, U2F_EMU_USB, packet,
    186                          U2FHID_PACKET_SIZE);
    187
    188        /* Notify response */
    189        if (u2f_emu_vdev_has_response(key->vdev, U2F_EMU_USB)) {
    190            event_notifier_set(&key->notifier);
    191        }
    192        qemu_mutex_unlock(&key->vdev_mutex);
    193    }
    194    return NULL;
    195}
    196
    197static ssize_t u2f_emulated_read(const char *path, char *buffer,
    198                                 size_t buffer_len)
    199{
    200    int fd;
    201    ssize_t ret;
    202
    203    fd = qemu_open_old(path, O_RDONLY);
    204    if (fd < 0) {
    205        return -1;
    206    }
    207
    208    ret = read(fd, buffer, buffer_len);
    209    close(fd);
    210
    211    return ret;
    212}
    213
    214static bool u2f_emulated_setup_counter(const char *path,
    215                                       struct synced_counter *counter)
    216{
    217    int fd, ret;
    218    FILE *fp;
    219
    220    fd = qemu_open_old(path, O_RDWR);
    221    if (fd < 0) {
    222        return false;
    223    }
    224    fp = fdopen(fd, "r+");
    225    if (fp == NULL) {
    226        close(fd);
    227        return false;
    228    }
    229    ret = fscanf(fp, "%u", &counter->value);
    230    if (ret == EOF) {
    231        fclose(fp);
    232        return false;
    233    }
    234    counter->fp = fp;
    235    counter->vdev_counter.counter_increment = counter_increment;
    236    counter->vdev_counter.counter_read = counter_read;
    237
    238    return true;
    239}
    240
    241static u2f_emu_rc u2f_emulated_setup_vdev_manualy(U2FEmulatedState *key)
    242{
    243    ssize_t ret;
    244    char cert_pem[4096], privkey_pem[2048];
    245    struct u2f_emu_vdev_setup setup_info;
    246
    247    /* Certificate */
    248    ret = u2f_emulated_read(key->cert, cert_pem, sizeof(cert_pem));
    249    if (ret < 0) {
    250        return -1;
    251    }
    252
    253    /* Private key */
    254    ret = u2f_emulated_read(key->privkey, privkey_pem, sizeof(privkey_pem));
    255    if (ret < 0) {
    256        return -1;
    257    }
    258
    259    /* Entropy */
    260    ret = u2f_emulated_read(key->entropy, (char *)&setup_info.entropy,
    261                            sizeof(setup_info.entropy));
    262    if (ret < 0) {
    263        return -1;
    264    }
    265
    266    /* Counter */
    267    if (!u2f_emulated_setup_counter(key->counter, &key->synced_counter)) {
    268        return -1;
    269    }
    270
    271    /* Setup */
    272    setup_info.certificate = cert_pem;
    273    setup_info.private_key = privkey_pem;
    274    setup_info.counter = (struct u2f_emu_vdev_counter *)&key->synced_counter;
    275
    276    return u2f_emu_vdev_new(&key->vdev, &setup_info);
    277}
    278
    279static void u2f_emulated_event_handler(EventNotifier *notifier)
    280{
    281    U2FEmulatedState *key = container_of(notifier, U2FEmulatedState, notifier);
    282    size_t packet_size;
    283    uint8_t *packet_in = NULL;
    284
    285    event_notifier_test_and_clear(&key->notifier);
    286    qemu_mutex_lock(&key->vdev_mutex);
    287    while (u2f_emu_vdev_has_response(key->vdev, U2F_EMU_USB)) {
    288        packet_size = u2f_emu_vdev_get_response(key->vdev, U2F_EMU_USB,
    289                                                &packet_in);
    290        if (packet_size == U2FHID_PACKET_SIZE) {
    291            u2f_send_to_guest(&key->base, packet_in);
    292        }
    293        u2f_emu_vdev_free_response(packet_in);
    294    }
    295    qemu_mutex_unlock(&key->vdev_mutex);
    296}
    297
    298static void u2f_emulated_realize(U2FKeyState *base, Error **errp)
    299{
    300    U2FEmulatedState *key = EMULATED_U2F_KEY(base);
    301    u2f_emu_rc rc;
    302
    303    if (key->cert != NULL || key->privkey != NULL || key->entropy != NULL
    304        || key->counter != NULL) {
    305        if (key->cert != NULL && key->privkey != NULL
    306            && key->entropy != NULL && key->counter != NULL) {
    307            rc = u2f_emulated_setup_vdev_manualy(key);
    308        } else {
    309            error_setg(errp, "%s: cert, priv, entropy and counter "
    310                       "parameters must be provided to manually configure "
    311                       "the emulated device", TYPE_U2F_EMULATED);
    312            return;
    313        }
    314    } else if (key->dir != NULL) {
    315        rc = u2f_emu_vdev_new_from_dir(&key->vdev, key->dir);
    316    } else {
    317        rc = u2f_emu_vdev_new_ephemeral(&key->vdev);
    318    }
    319
    320    if (rc != U2F_EMU_OK) {
    321        error_setg(errp, "%s: Failed to setup the key", TYPE_U2F_EMULATED);
    322        return;
    323    }
    324
    325    if (event_notifier_init(&key->notifier, false) < 0) {
    326        error_setg(errp, "%s: Failed to initialize notifier",
    327                   TYPE_U2F_EMULATED);
    328        return;
    329    }
    330    /* Notifier */
    331    event_notifier_set_handler(&key->notifier, u2f_emulated_event_handler);
    332
    333    /* Synchronization */
    334    qemu_cond_init(&key->key_cond);
    335    qemu_mutex_init(&key->vdev_mutex);
    336    qemu_mutex_init(&key->pending_out_mutex);
    337    qemu_mutex_init(&key->key_mutex);
    338    u2f_emulated_reset(key);
    339
    340    /* Thread */
    341    key->stop_thread = false;
    342    qemu_thread_create(&key->key_thread, "u2f-key", u2f_emulated_thread,
    343                       key, QEMU_THREAD_JOINABLE);
    344}
    345
    346static void u2f_emulated_unrealize(U2FKeyState *base)
    347{
    348    U2FEmulatedState *key = EMULATED_U2F_KEY(base);
    349
    350    /* Thread */
    351    key->stop_thread = true;
    352    qemu_cond_signal(&key->key_cond);
    353    qemu_thread_join(&key->key_thread);
    354
    355    /* Notifier */
    356    event_notifier_set_handler(&key->notifier, NULL);
    357    event_notifier_cleanup(&key->notifier);
    358
    359    /* Synchronization */
    360    qemu_cond_destroy(&key->key_cond);
    361    qemu_mutex_destroy(&key->vdev_mutex);
    362    qemu_mutex_destroy(&key->key_mutex);
    363    qemu_mutex_destroy(&key->pending_out_mutex);
    364
    365    /* Vdev */
    366    u2f_emu_vdev_free(key->vdev);
    367    if (key->synced_counter.fp != NULL) {
    368        fclose(key->synced_counter.fp);
    369    }
    370}
    371
    372static Property u2f_emulated_properties[] = {
    373    DEFINE_PROP_STRING("dir", U2FEmulatedState, dir),
    374    DEFINE_PROP_STRING("cert", U2FEmulatedState, cert),
    375    DEFINE_PROP_STRING("privkey", U2FEmulatedState, privkey),
    376    DEFINE_PROP_STRING("entropy", U2FEmulatedState, entropy),
    377    DEFINE_PROP_STRING("counter", U2FEmulatedState, counter),
    378    DEFINE_PROP_END_OF_LIST(),
    379};
    380
    381static void u2f_emulated_class_init(ObjectClass *klass, void *data)
    382{
    383    DeviceClass *dc = DEVICE_CLASS(klass);
    384    U2FKeyClass *kc = U2F_KEY_CLASS(klass);
    385
    386    kc->realize = u2f_emulated_realize;
    387    kc->unrealize = u2f_emulated_unrealize;
    388    kc->recv_from_guest = u2f_emulated_recv_from_guest;
    389    dc->desc = "QEMU U2F emulated key";
    390    device_class_set_props(dc, u2f_emulated_properties);
    391}
    392
    393static const TypeInfo u2f_key_emulated_info = {
    394    .name = TYPE_U2F_EMULATED,
    395    .parent = TYPE_U2F_KEY,
    396    .instance_size = sizeof(U2FEmulatedState),
    397    .class_init = u2f_emulated_class_init
    398};
    399
    400static void u2f_key_emulated_register_types(void)
    401{
    402    type_register_static(&u2f_key_emulated_info);
    403}
    404
    405type_init(u2f_key_emulated_register_types)