Exemple #1
0
/* Measure the performance of a DH primitive when deriving keys */
static void perf_dh_derive(int id)
{
    char name[64];
    NoiseDHState *dh;
    uint8_t private_key[56];
    size_t key_len;
    timestamp_t start, end;
    int count;
    double elapsed;

    if (noise_dhstate_new_by_id(&dh, id) != NOISE_ERROR_NONE)
        return;
    key_len = noise_dhstate_get_private_key_length(dh);

    memset(private_key, 0xAA, sizeof(private_key));
    start = current_timestamp();
    for (count = 0; count < DH_COUNT; ++count)
        noise_dhstate_set_keypair_private(dh, private_key, key_len);
    end = current_timestamp();

    elapsed = elapsed_to_seconds(start, end) / (double)DH_COUNT;
    snprintf(name, sizeof(name), "%s derive key",
             noise_id_to_name(NOISE_DH_CATEGORY, id));
    printf("%-20s%8.2f          %8.2f\n", name, 1.0 / elapsed, units / elapsed);

    noise_dhstate_free(dh);
}
/* Derive the public key values for testing purposes */
static void handshakestate_derive_keys(void)
{
    NoiseDHState *dh;

    /* Curve25519 keys */
    compare(noise_dhstate_new_by_id(&dh, NOISE_DH_CURVE25519),
            NOISE_ERROR_NONE);
    compare(noise_dhstate_set_keypair_private
                (dh, init_private_25519, sizeof(init_private_25519)),
            NOISE_ERROR_NONE);
    compare(noise_dhstate_get_public_key
                (dh, init_public_25519, sizeof(init_public_25519)),
            NOISE_ERROR_NONE);
    compare(noise_dhstate_set_keypair_private
                (dh, resp_private_25519, sizeof(resp_private_25519)),
            NOISE_ERROR_NONE);
    compare(noise_dhstate_get_public_key
                (dh, resp_public_25519, sizeof(resp_public_25519)),
            NOISE_ERROR_NONE);
    compare(noise_dhstate_set_keypair_private
                (dh, resp_private_25519_alt, sizeof(resp_private_25519_alt)),
            NOISE_ERROR_NONE);
    compare(noise_dhstate_get_public_key
                (dh, resp_public_25519_alt, sizeof(resp_public_25519_alt)),
            NOISE_ERROR_NONE);
    compare(noise_dhstate_free(dh), NOISE_ERROR_NONE);

    /* Curve448 keys */
    compare(noise_dhstate_new_by_id(&dh, NOISE_DH_CURVE448),
            NOISE_ERROR_NONE);
    compare(noise_dhstate_set_keypair_private
                (dh, init_private_448, sizeof(init_private_448)),
            NOISE_ERROR_NONE);
    compare(noise_dhstate_get_public_key
                (dh, init_public_448, sizeof(init_public_448)),
            NOISE_ERROR_NONE);
    compare(noise_dhstate_set_keypair_private
                (dh, resp_private_448, sizeof(resp_private_448)),
            NOISE_ERROR_NONE);
    compare(noise_dhstate_get_public_key
                (dh, resp_public_448, sizeof(resp_public_448)),
            NOISE_ERROR_NONE);
    compare(noise_dhstate_set_keypair_private
                (dh, resp_private_448_alt, sizeof(resp_private_448_alt)),
            NOISE_ERROR_NONE);
    compare(noise_dhstate_get_public_key
                (dh, resp_public_448_alt, sizeof(resp_public_448_alt)),
            NOISE_ERROR_NONE);
    compare(noise_dhstate_free(dh), NOISE_ERROR_NONE);
}
Exemple #3
0
/* Initialize's the handshake with all necessary keys */
static int initialize_handshake
    (NoiseHandshakeState *handshake, const NoiseProtocolId *nid,
     const void *prologue, size_t prologue_len)
{
    NoiseDHState *dh;
    int dh_id;
    int err;

    /* Set the prologue first */
    err = noise_handshakestate_set_prologue(handshake, prologue, prologue_len);
    if (err != NOISE_ERROR_NONE) {
        noise_perror("prologue", err);
        return 0;
    }

    /* Set the PSK if one is needed */
    if (nid->prefix_id == NOISE_PREFIX_PSK) {
        err = noise_handshakestate_set_pre_shared_key
            (handshake, psk, sizeof(psk));
        if (err != NOISE_ERROR_NONE) {
            noise_perror("psk", err);
            return 0;
        }
    }

    /* Set the local keypair for the server based on the DH algorithm */
    if (noise_handshakestate_needs_local_keypair(handshake)) {
        dh = noise_handshakestate_get_local_keypair_dh(handshake);
        dh_id = noise_dhstate_get_dh_id(dh);
        if (dh_id == NOISE_DH_CURVE25519) {
            err = noise_dhstate_set_keypair_private
                (dh, server_key_25519, sizeof(server_key_25519));
        } else if (dh_id == NOISE_DH_CURVE448) {
            err = noise_dhstate_set_keypair_private
                (dh, server_key_448, sizeof(server_key_448));
        } else {
            err = NOISE_ERROR_UNKNOWN_ID;
        }
        if (err != NOISE_ERROR_NONE) {
            noise_perror("set server private key", err);
            return 0;
        }
    }

    /* Set the remote public key for the client */
    if (noise_handshakestate_needs_remote_public_key(handshake)) {
        dh = noise_handshakestate_get_remote_public_key_dh(handshake);
        dh_id = noise_dhstate_get_dh_id(dh);
        if (dh_id == NOISE_DH_CURVE25519) {
            err = noise_dhstate_set_public_key
                (dh, client_key_25519, sizeof(client_key_25519));
        } else if (dh_id == NOISE_DH_CURVE25519) {
            err = noise_dhstate_set_public_key
                (dh, client_key_448, sizeof(client_key_448));
        } else {
            err = NOISE_ERROR_UNKNOWN_ID;
        }
        if (err != NOISE_ERROR_NONE) {
            noise_perror("set client public key", err);
            return 0;
        }
    }

    /* Set the fixed local ephemeral value if necessary */
    if (fixed_ephemeral) {
        dh = noise_handshakestate_get_fixed_ephemeral_dh(handshake);
        if (noise_dhstate_get_dh_id(dh) == NOISE_DH_CURVE25519) {
            err = noise_dhstate_set_keypair_private
                (dh, fixed_ephemeral_25519, sizeof(fixed_ephemeral_25519));
        } else {
            err = noise_dhstate_set_keypair_private
                (dh, fixed_ephemeral_448, sizeof(fixed_ephemeral_448));
        }
        if (err != NOISE_ERROR_NONE) {
            noise_perror("fixed ephemeral value", err);
            return 0;
        }
    }

    /* Ready to go */
    return 1;
}
/* Check that "IK" correctly falls back to "XXfallback" */
static void check_fallback_protocol
    (const char *name, int fallback_anyway, int trial_initiator_decrypt)
{
    NoiseHandshakeState *initiator;
    NoiseHandshakeState *responder;
    NoiseDHState *dh;
    uint8_t message[4096];
    uint8_t message2[4096];
    uint8_t payload[23];
    NoiseBuffer mbuf;
    NoiseBuffer pbuf;

    /* Set the name of this test for error reporting */
    data_name = name;

    /* Create the two objects for an initial "IK" handshake */
    compare(noise_handshakestate_new_by_name
                (&initiator, name, NOISE_ROLE_INITIATOR),
            NOISE_ERROR_NONE);
    compare(noise_handshakestate_new_by_name
                (&responder, name, NOISE_ROLE_RESPONDER),
            NOISE_ERROR_NONE);

    /* Set up the keys.  The responder uses an alternate key that is
       different from the one expected by the initiator */
    compare(noise_handshakestate_set_prologue(initiator, "Hello", 5),
            NOISE_ERROR_NONE);
    compare(noise_handshakestate_set_prologue(responder, "Hello", 5),
            NOISE_ERROR_NONE);
    dh = noise_handshakestate_get_local_keypair_dh(initiator);
    if (noise_dhstate_get_dh_id(dh) == NOISE_DH_CURVE25519) {
        compare(noise_dhstate_set_keypair_private
                    (dh, init_private_25519, sizeof(init_private_25519)),
                NOISE_ERROR_NONE);
    } else {
        compare(noise_dhstate_set_keypair_private
                    (dh, init_private_448, sizeof(init_private_448)),
                NOISE_ERROR_NONE);
    }
    dh = noise_handshakestate_get_remote_public_key_dh(initiator);
    if (noise_dhstate_get_dh_id(dh) == NOISE_DH_CURVE25519) {
        compare(noise_dhstate_set_public_key
                    (dh, resp_public_25519, sizeof(resp_public_25519)),
                NOISE_ERROR_NONE);
    } else {
        compare(noise_dhstate_set_public_key
                    (dh, resp_public_448, sizeof(resp_public_448)),
                NOISE_ERROR_NONE);
    }
    dh = noise_handshakestate_get_local_keypair_dh(responder);
    if (!fallback_anyway) {
        if (noise_dhstate_get_dh_id(dh) == NOISE_DH_CURVE25519) {
            compare(noise_dhstate_set_keypair_private
                        (dh, resp_private_25519_alt, sizeof(resp_private_25519_alt)),
                    NOISE_ERROR_NONE);
        } else {
            compare(noise_dhstate_set_keypair_private
                        (dh, resp_private_448_alt, sizeof(resp_private_448_alt)),
                    NOISE_ERROR_NONE);
        }
    } else {
        /* Matching keys, but the responder will fallback anyway */
        if (noise_dhstate_get_dh_id(dh) == NOISE_DH_CURVE25519) {
            compare(noise_dhstate_set_keypair_private
                        (dh, resp_private_25519, sizeof(resp_private_25519)),
                    NOISE_ERROR_NONE);
        } else {
            compare(noise_dhstate_set_keypair_private
                        (dh, resp_private_448, sizeof(resp_private_448)),
                    NOISE_ERROR_NONE);
        }
    }
    if (noise_handshakestate_needs_pre_shared_key(initiator)) {
        compare(noise_handshakestate_set_pre_shared_key
                    (initiator, psk, sizeof(psk)),
                NOISE_ERROR_NONE);
        compare(noise_handshakestate_set_pre_shared_key
                    (responder, psk, sizeof(psk)),
                NOISE_ERROR_NONE);
    }

    /* Start the handshakes running */
    compare(noise_handshakestate_start(initiator), NOISE_ERROR_NONE);
    compare(noise_handshakestate_start(responder), NOISE_ERROR_NONE);

    /* Create the first outgoing "IK" packet from the initiator */
    memset(message, 0, sizeof(message));
    memset(payload, 0x66, sizeof(payload));
    noise_buffer_set_output(mbuf, message, sizeof(message));
    noise_buffer_set_input(pbuf, payload, sizeof(payload));
    compare(noise_handshakestate_write_message(initiator, &mbuf, &pbuf),
            NOISE_ERROR_NONE);

    /* Read the message on the responder side */
    if (!fallback_anyway) {
        noise_buffer_set_output(pbuf, payload, sizeof(payload));
        compare(noise_handshakestate_read_message(responder, &mbuf, &pbuf),
                NOISE_ERROR_MAC_FAILURE);
    } else {
        /* The "IK" handshake was successful, but we decide to ignore that
           and change to "XXfallback" anyway */
        noise_buffer_set_output(pbuf, payload, sizeof(payload));
        compare(noise_handshakestate_read_message(responder, &mbuf, &pbuf),
                NOISE_ERROR_NONE);
    }

    /* Fallback on the responder side */
    compare(noise_handshakestate_fallback(responder), NOISE_ERROR_NONE);
    compare(noise_handshakestate_get_role(initiator), NOISE_ROLE_INITIATOR);
    compare(noise_handshakestate_get_role(responder), NOISE_ROLE_INITIATOR);

    /* Supply the prologue and the PSK again to the responder */
    compare(noise_handshakestate_set_prologue(responder, "Hello", 5),
            NOISE_ERROR_NONE);
    if (noise_handshakestate_needs_pre_shared_key(responder)) {
        compare(noise_handshakestate_set_pre_shared_key
                    (responder, psk, sizeof(psk)),
                NOISE_ERROR_NONE);
    }
    compare(noise_handshakestate_start(responder), NOISE_ERROR_NONE);

    /* Write a new message back to the initiator */
    memset(payload, 0xAA, sizeof(payload));
    noise_buffer_set_output(mbuf, message, sizeof(message));
    noise_buffer_set_input(pbuf, payload, sizeof(payload));
    compare(noise_handshakestate_write_message(responder, &mbuf, &pbuf),
            NOISE_ERROR_NONE);

    /* Optionally perform a trial decryption first, which will fail */
    if (trial_initiator_decrypt) {
        memcpy(message2, message, sizeof(message));
        noise_buffer_set_output(pbuf, payload, sizeof(payload));
        compare(noise_handshakestate_read_message(initiator, &mbuf, &pbuf),
                NOISE_ERROR_MAC_FAILURE);
        memcpy(message, message2, sizeof(message));
    }

    /* Fallback on the initiator side */
    compare(noise_handshakestate_fallback(initiator), NOISE_ERROR_NONE);
    compare(noise_handshakestate_get_role(initiator), NOISE_ROLE_RESPONDER);
    compare(noise_handshakestate_get_role(responder), NOISE_ROLE_INITIATOR);

    /* Supply the prologue and the PSK again to the initiator */
    compare(noise_handshakestate_set_prologue(initiator, "Hello", 5),
            NOISE_ERROR_NONE);
    if (noise_handshakestate_needs_pre_shared_key(initiator)) {
        compare(noise_handshakestate_set_pre_shared_key
                    (initiator, psk, sizeof(psk)),
                NOISE_ERROR_NONE);
    }
    compare(noise_handshakestate_start(initiator), NOISE_ERROR_NONE);

    /* Read the message on the initiator side */
    noise_buffer_set_output(pbuf, payload, sizeof(payload));
    compare(noise_handshakestate_read_message(initiator, &mbuf, &pbuf),
            NOISE_ERROR_NONE);

    /* Send the next "XXfallback" message from the initiator side */
    memset(payload, 0x66, sizeof(payload));
    noise_buffer_set_output(mbuf, message, sizeof(message));
    noise_buffer_set_input(pbuf, payload, sizeof(payload));
    compare(noise_handshakestate_write_message(initiator, &mbuf, &pbuf),
            NOISE_ERROR_NONE);

    /* Receive the message on the responder side */
    noise_buffer_set_output(pbuf, payload, sizeof(payload));
    compare(noise_handshakestate_read_message(responder, &mbuf, &pbuf),
            NOISE_ERROR_NONE);

    /* Both sides should now be in the "split" condition */
    compare(noise_handshakestate_get_action(initiator), NOISE_ACTION_SPLIT);
    compare(noise_handshakestate_get_action(responder), NOISE_ACTION_SPLIT);

    /* Check that the handshake hashes are identical */
    compare(noise_handshakestate_get_handshake_hash(initiator, message, 64),
            NOISE_ERROR_NONE);
    compare(noise_handshakestate_get_handshake_hash(responder, message + 64, 64),
            NOISE_ERROR_NONE);
    verify(!memcmp(message, message + 64, 64));

    /* Clean up */
    compare(noise_handshakestate_free(initiator), NOISE_ERROR_NONE);
    compare(noise_handshakestate_free(responder), NOISE_ERROR_NONE);
}
/* Check the behaviour of a specific handshake protocol.  These tests check
   whether the initiator and responder can talk to each other via the
   protocol but they do not check for correct bytes on the wire.  Wire checks
   are done by the separate vector tests. */
static void check_handshake_protocol(const char *name)
{
    NoiseHandshakeState *initiator;
    NoiseHandshakeState *responder;
    NoiseHandshakeState *send;
    NoiseHandshakeState *recv;
    NoiseProtocolId id, id2;
    NoiseDHState *dh;
    const uint8_t *pattern;
    NoisePatternFlags_t init_flags;
    NoisePatternFlags_t resp_flags;
    uint8_t message[4096];
    uint8_t payload[23];
    NoiseBuffer mbuf;
    NoiseBuffer pbuf;
    int action;
    int index;

    /* Set the name of this test for error reporting */
    data_name = name;

    /* Convert the name into a protocol identifier and look up the pattern */
    compare(noise_protocol_name_to_id(&id, name, strlen(name)),
            NOISE_ERROR_NONE);
    pattern = noise_pattern_lookup(id.pattern_id);
    verify(pattern != 0);
    init_flags = ((NoisePatternFlags_t)(pattern[0])) |
                (((NoisePatternFlags_t)(pattern[1])) << 8);
    resp_flags = noise_pattern_reverse_flags(init_flags);

    /* Create two objects for the initiator and responder,
       one by name and the other by identifier. */
    compare(noise_handshakestate_new_by_name
                (&initiator, name, NOISE_ROLE_INITIATOR),
            NOISE_ERROR_NONE);
    compare(noise_handshakestate_new_by_id
                (&responder, &id, NOISE_ROLE_RESPONDER),
            NOISE_ERROR_NONE);

    /* Check that the "needs" functions report results consistent
       with the requirements of the protocol */
    compare(noise_handshakestate_needs_local_keypair(initiator),
            (init_flags & NOISE_PAT_FLAG_LOCAL_STATIC) != 0);
    compare(noise_handshakestate_needs_remote_public_key(initiator),
            (init_flags & NOISE_PAT_FLAG_REMOTE_REQUIRED) != 0);
    compare(noise_handshakestate_needs_pre_shared_key(initiator),
            id.prefix_id == NOISE_PREFIX_PSK);
    compare(noise_handshakestate_needs_local_keypair(responder),
            (resp_flags & NOISE_PAT_FLAG_LOCAL_STATIC) != 0);
    compare(noise_handshakestate_needs_remote_public_key(responder),
            (resp_flags & NOISE_PAT_FLAG_REMOTE_REQUIRED) != 0);
    compare(noise_handshakestate_needs_pre_shared_key(responder),
            id.prefix_id == NOISE_PREFIX_PSK);

    /* Check other properties */
    compare(noise_handshakestate_get_role(initiator), NOISE_ROLE_INITIATOR);
    compare(noise_handshakestate_get_role(responder), NOISE_ROLE_RESPONDER);
    memset(&id2, 0xAA, sizeof(id2));
    compare(noise_handshakestate_get_protocol_id(initiator, &id2),
            NOISE_ERROR_NONE);
    verify(!memcmp(&id, &id2, sizeof(id)));
    memset(&id2, 0x66, sizeof(id2));
    compare(noise_handshakestate_get_protocol_id(responder, &id2),
            NOISE_ERROR_NONE);
    verify(!memcmp(&id, &id2, sizeof(id)));

    /* Specify a fixed prologue on each end */
    compare(noise_handshakestate_set_prologue(initiator, "Hello", 5),
            NOISE_ERROR_NONE);
    compare(noise_handshakestate_set_prologue(responder, "Hello", 5),
            NOISE_ERROR_NONE);

    /* Set the keys that are needed on each end */
    compare(noise_handshakestate_has_local_keypair(initiator), 0);
    if (noise_handshakestate_needs_local_keypair(initiator)) {
        compare(noise_handshakestate_start(initiator),
                NOISE_ERROR_LOCAL_KEY_REQUIRED);
        dh = noise_handshakestate_get_local_keypair_dh(initiator);
        if (noise_dhstate_get_dh_id(dh) == NOISE_DH_CURVE25519) {
            compare(noise_dhstate_set_keypair_private
                        (dh, init_private_25519, sizeof(init_private_25519)),
                    NOISE_ERROR_NONE);
        } else {
            compare(noise_dhstate_set_keypair_private
                        (dh, init_private_448, sizeof(init_private_448)),
                    NOISE_ERROR_NONE);
        }
        compare(noise_handshakestate_has_local_keypair(initiator), 1);
    } else {
        dh = noise_handshakestate_get_local_keypair_dh(initiator);
        verify(dh == 0);
    }
    compare(noise_handshakestate_has_remote_public_key(initiator), 0);
    if (noise_handshakestate_needs_remote_public_key(initiator)) {
        compare(noise_handshakestate_start(initiator),
                NOISE_ERROR_REMOTE_KEY_REQUIRED);
        dh = noise_handshakestate_get_remote_public_key_dh(initiator);
        if (noise_dhstate_get_dh_id(dh) == NOISE_DH_CURVE25519) {
            compare(noise_dhstate_set_public_key
                        (dh, resp_public_25519, sizeof(resp_public_25519)),
                    NOISE_ERROR_NONE);
        } else {
            compare(noise_dhstate_set_public_key
                        (dh, resp_public_448, sizeof(resp_public_448)),
                    NOISE_ERROR_NONE);
        }
        compare(noise_handshakestate_has_remote_public_key(initiator), 1);
    } else {
        dh = noise_handshakestate_get_remote_public_key_dh(initiator);
        if ((init_flags & NOISE_PAT_FLAG_REMOTE_STATIC) != 0)
            verify(dh != 0);
        else
            verify(dh == 0);
    }
    compare(noise_handshakestate_has_local_keypair(responder), 0);
    if (noise_handshakestate_needs_local_keypair(responder)) {
        compare(noise_handshakestate_start(responder),
                NOISE_ERROR_LOCAL_KEY_REQUIRED);
        dh = noise_handshakestate_get_local_keypair_dh(responder);
        if (noise_dhstate_get_dh_id(dh) == NOISE_DH_CURVE25519) {
            compare(noise_dhstate_set_keypair_private
                        (dh, resp_private_25519, sizeof(resp_private_25519)),
                    NOISE_ERROR_NONE);
        } else {
            compare(noise_dhstate_set_keypair_private
                        (dh, resp_private_448, sizeof(resp_private_448)),
                    NOISE_ERROR_NONE);
        }
        compare(noise_handshakestate_has_local_keypair(responder), 1);
    } else {
        dh = noise_handshakestate_get_local_keypair_dh(responder);
        verify(dh == 0);
    }
    compare(noise_handshakestate_has_remote_public_key(responder), 0);
    if (noise_handshakestate_needs_remote_public_key(responder)) {
        compare(noise_handshakestate_start(responder),
                NOISE_ERROR_REMOTE_KEY_REQUIRED);
        dh = noise_handshakestate_get_remote_public_key_dh(responder);
        if (noise_dhstate_get_dh_id(dh) == NOISE_DH_CURVE25519) {
            compare(noise_dhstate_set_public_key
                        (dh, init_public_25519, sizeof(init_public_25519)),
                    NOISE_ERROR_NONE);
        } else {
            compare(noise_dhstate_set_public_key
                        (dh, init_public_448, sizeof(init_public_448)),
                    NOISE_ERROR_NONE);
        }
        compare(noise_handshakestate_has_remote_public_key(responder), 1);
    } else {
        dh = noise_handshakestate_get_remote_public_key_dh(responder);
        if ((init_flags & NOISE_PAT_FLAG_LOCAL_STATIC) != 0)
            verify(dh != 0);
        else
            verify(dh == 0);
    }
    compare(noise_handshakestate_has_pre_shared_key(initiator), 0);
    if (noise_handshakestate_needs_pre_shared_key(initiator)) {
        compare(noise_handshakestate_start(initiator),
                NOISE_ERROR_PSK_REQUIRED);
        compare(noise_handshakestate_set_pre_shared_key
                    (initiator, psk, sizeof(psk)),
                NOISE_ERROR_NONE);
        compare(noise_handshakestate_has_pre_shared_key(initiator), 1);
    } else {
        compare(noise_handshakestate_set_pre_shared_key
                    (initiator, psk, sizeof(psk)),
                NOISE_ERROR_NOT_APPLICABLE);
    }
    compare(noise_handshakestate_has_pre_shared_key(responder), 0);
    if (noise_handshakestate_needs_pre_shared_key(responder)) {
        compare(noise_handshakestate_start(responder),
                NOISE_ERROR_PSK_REQUIRED);
        compare(noise_handshakestate_set_pre_shared_key
                    (responder, psk, sizeof(psk)),
                NOISE_ERROR_NONE);
        compare(noise_handshakestate_has_pre_shared_key(responder), 1);
    } else {
        compare(noise_handshakestate_set_pre_shared_key
                    (initiator, psk, sizeof(psk)),
                NOISE_ERROR_NOT_APPLICABLE);
    }

    /* Start the handshake running */
    compare(noise_handshakestate_start(initiator), NOISE_ERROR_NONE);
    compare(noise_handshakestate_start(responder), NOISE_ERROR_NONE);

    /* Starting the handshake again should fail (already running) */
    compare(noise_handshakestate_start(initiator), NOISE_ERROR_INVALID_STATE);
    compare(noise_handshakestate_start(responder), NOISE_ERROR_INVALID_STATE);

    /* Run the two handshakes in parallel while something to read/write */
    memset(payload, 0xAA, sizeof(payload));
    for (;;) {
        /* Which direction for this message? */
        action = noise_handshakestate_get_action(initiator);
        if (action == NOISE_ACTION_WRITE_MESSAGE) {
            send = initiator;
            recv = responder;
        } else if (action == NOISE_ACTION_READ_MESSAGE) {
            send = responder;
            recv = initiator;
        } else {
            break;
        }

        /* Check that the objects have the right action for this step */
        compare(noise_handshakestate_get_action(send),
                NOISE_ACTION_WRITE_MESSAGE);
        compare(noise_handshakestate_get_action(recv),
                NOISE_ACTION_READ_MESSAGE);

        /* Cannot perform the wrong operation on the sender or receiver */
        noise_buffer_set_output(mbuf, message, sizeof(message));
        noise_buffer_set_output(pbuf, payload, sizeof(payload));
        compare(noise_handshakestate_read_message(send, &mbuf, &pbuf),
                NOISE_ERROR_INVALID_STATE);
        noise_buffer_set_output(mbuf, message, sizeof(message));
        noise_buffer_set_input(pbuf, payload, sizeof(payload));
        compare(noise_handshakestate_write_message(recv, &mbuf, &pbuf),
                NOISE_ERROR_INVALID_STATE);

        /* Parameter errors */
        compare(noise_handshakestate_write_message(0, &mbuf, &pbuf),
                NOISE_ERROR_INVALID_PARAM);
        compare(noise_handshakestate_write_message(send, 0, &pbuf),
                NOISE_ERROR_INVALID_PARAM);
        mbuf.data = 0;
        compare(noise_handshakestate_write_message(send, &mbuf, &pbuf),
                NOISE_ERROR_INVALID_PARAM);
        noise_buffer_set_output(mbuf, message, sizeof(message));
        pbuf.data = 0;
        compare(noise_handshakestate_write_message(send, &mbuf, &pbuf),
                NOISE_ERROR_INVALID_PARAM);
        noise_buffer_set_output(mbuf, message, sizeof(message));
        noise_buffer_set_output(pbuf, payload, sizeof(payload));
        compare(noise_handshakestate_read_message(0, &mbuf, &pbuf),
                NOISE_ERROR_INVALID_PARAM);
        compare(noise_handshakestate_read_message(recv, 0, &pbuf),
                NOISE_ERROR_INVALID_PARAM);
        mbuf.data = 0;
        compare(noise_handshakestate_read_message(recv, &mbuf, &pbuf),
                NOISE_ERROR_INVALID_PARAM);
        noise_buffer_set_output(mbuf, message, sizeof(message));
        pbuf.data = 0;
        compare(noise_handshakestate_read_message(recv, &mbuf, &pbuf),
                NOISE_ERROR_INVALID_PARAM);

        /* Transfer the message to the other side properly */
        noise_buffer_set_output(mbuf, message, sizeof(message));
        noise_buffer_set_input(pbuf, payload, sizeof(payload));
        compare(noise_handshakestate_write_message(send, &mbuf, &pbuf),
                NOISE_ERROR_NONE);
        noise_buffer_set_output(pbuf, payload, sizeof(payload));
        compare(noise_handshakestate_read_message(recv, &mbuf, &pbuf),
                NOISE_ERROR_NONE);
    }

    /* Both handshakes should now have "split" */
    compare(noise_handshakestate_get_action(initiator), NOISE_ACTION_SPLIT);
    compare(noise_handshakestate_get_action(responder), NOISE_ACTION_SPLIT);

    /* Check that the handshake hashes are identical */
    compare(noise_handshakestate_get_handshake_hash(initiator, message, 64),
            NOISE_ERROR_NONE);
    compare(noise_handshakestate_get_handshake_hash(responder, message + 64, 64),
            NOISE_ERROR_NONE);
    verify(!memcmp(message, message + 64, 64));

    /* Check handshake hash truncation */
    memset(message, 0xAA, sizeof(message));
    compare(noise_handshakestate_get_handshake_hash(initiator, message, 16),
            NOISE_ERROR_NONE);
    compare(noise_handshakestate_get_handshake_hash(responder, message + 64, 16),
            NOISE_ERROR_NONE);
    verify(!memcmp(message, message + 64, 16));
    for (index = 16; index < 64; ++index)
        compare(message[index], 0xAA);

    /* Check for various error conditions */
    compare(noise_handshakestate_get_protocol_id(0, &id2),
            NOISE_ERROR_INVALID_PARAM);
    compare(noise_handshakestate_get_protocol_id(initiator, 0),
            NOISE_ERROR_INVALID_PARAM);
    compare(noise_handshakestate_get_handshake_hash(0, message, 64),
            NOISE_ERROR_INVALID_PARAM);
    compare(noise_handshakestate_get_handshake_hash(initiator, 0, 64),
            NOISE_ERROR_INVALID_PARAM);

    /* Clean up */
    compare(noise_handshakestate_free(initiator), NOISE_ERROR_NONE);
    compare(noise_handshakestate_free(responder), NOISE_ERROR_NONE);
}