Blame src/chachapoly.c

Packit Service 31306d
/*
Packit Service 31306d
 * This file is part of the SSH Library
Packit Service 31306d
 *
Packit Service 31306d
 * Copyright (c) 2015 by Aris Adamantiadis
Packit Service 31306d
 *
Packit Service 31306d
 * The SSH Library is free software; you can redistribute it and/or modify
Packit Service 31306d
 * it under the terms of the GNU Lesser General Public License as published by
Packit Service 31306d
 * the Free Software Foundation; either version 2.1 of the License, or (at your
Packit Service 31306d
 * option) any later version.
Packit Service 31306d
 *
Packit Service 31306d
 * The SSH Library is distributed in the hope that it will be useful, but
Packit Service 31306d
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit Service 31306d
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
Packit Service 31306d
 * License for more details.
Packit Service 31306d
 *
Packit Service 31306d
 * You should have received a copy of the GNU Lesser General Public License
Packit Service 31306d
 * along with the SSH Library; see the file COPYING.  If not, write to
Packit Service 31306d
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
Packit Service 31306d
 * MA 02111-1307, USA.
Packit Service 31306d
 */
Packit Service 31306d
Packit Service 31306d
#include "config.h"
Packit Service 31306d
Packit Service 31306d
#include "libssh/libssh.h"
Packit Service 31306d
#include "libssh/crypto.h"
Packit Service 31306d
#include "libssh/chacha.h"
Packit Service 31306d
#include "libssh/poly1305.h"
Packit Service 31306d
#include "libssh/misc.h"
Packit Service 31306d
Packit Service 31306d
/* size of the keys k1 and k2 as defined in specs */
Packit Service 31306d
#define CHACHA20_KEYLEN 32
Packit Service 31306d
struct chacha20_poly1305_keysched {
Packit Service 31306d
    /* key used for encrypting the length field*/
Packit Service 31306d
    struct chacha_ctx k1;
Packit Service 31306d
    /* key used for encrypting the packets */
Packit Service 31306d
    struct chacha_ctx k2;
Packit Service 31306d
};
Packit Service 31306d
Packit Service 31306d
#pragma pack(push, 1)
Packit Service 31306d
struct ssh_packet_header {
Packit Service 31306d
    uint32_t length;
Packit Service 31306d
    uint8_t payload[];
Packit Service 31306d
};
Packit Service 31306d
#pragma pack(pop)
Packit Service 31306d
Packit Service 31306d
static const uint8_t zero_block_counter[8] = {0, 0, 0, 0, 0, 0, 0, 0};
Packit Service 31306d
static const uint8_t payload_block_counter[8] = {1, 0, 0, 0, 0, 0, 0, 0};
Packit Service 31306d
Packit Service 31306d
static int chacha20_set_encrypt_key(struct ssh_cipher_struct *cipher,
Packit Service 31306d
                                    void *key,
Packit Service 31306d
                                    void *IV)
Packit Service 31306d
{
Packit Service 31306d
    struct chacha20_poly1305_keysched *sched;
Packit Service 31306d
    uint8_t *u8key = key;
Packit Service 31306d
    (void)IV;
Packit Service 31306d
Packit Service 31306d
    if (cipher->chacha20_schedule == NULL) {
Packit Service 31306d
        sched = malloc(sizeof *sched);
Packit Service 31306d
        if (sched == NULL){
Packit Service 31306d
            return -1;
Packit Service 31306d
        }
Packit Service 31306d
    } else {
Packit Service 31306d
        sched = cipher->chacha20_schedule;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    chacha_keysetup(&sched->k2, u8key, CHACHA20_KEYLEN * 8);
Packit Service 31306d
    chacha_keysetup(&sched->k1, u8key + CHACHA20_KEYLEN, CHACHA20_KEYLEN * 8);
Packit Service 31306d
    cipher->chacha20_schedule = sched;
Packit Service 31306d
Packit Service 31306d
    return 0;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
/**
Packit Service 31306d
 * @internal
Packit Service 31306d
 *
Packit Service 31306d
 * @brief encrypts an outgoing packet with chacha20 and authenticate it
Packit Service 31306d
 * with poly1305.
Packit Service 31306d
 */
Packit Service 31306d
static void chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
Packit Service 31306d
                                           void *in,
Packit Service 31306d
                                           void *out,
Packit Service 31306d
                                           size_t len,
Packit Service 31306d
                                           uint8_t *tag,
Packit Service 31306d
                                           uint64_t seq)
Packit Service 31306d
{
Packit Service 31306d
    struct ssh_packet_header *in_packet = in, *out_packet = out;
Packit Service 31306d
    uint8_t poly1305_ctx[POLY1305_KEYLEN] = {0};
Packit Service 31306d
    struct chacha20_poly1305_keysched *keys = cipher->chacha20_schedule;
Packit Service 31306d
Packit Service 31306d
    seq = htonll(seq);
Packit Service 31306d
    /* step 1, prepare the poly1305 key */
Packit Service 31306d
    chacha_ivsetup(&keys->k2, (uint8_t *)&seq, zero_block_counter);
Packit Service 31306d
    chacha_encrypt_bytes(&keys->k2,
Packit Service 31306d
                         poly1305_ctx,
Packit Service 31306d
                         poly1305_ctx,
Packit Service 31306d
                         POLY1305_KEYLEN);
Packit Service 31306d
Packit Service 31306d
    /* step 2, encrypt length field */
Packit Service 31306d
    chacha_ivsetup(&keys->k1, (uint8_t *)&seq, zero_block_counter);
Packit Service 31306d
    chacha_encrypt_bytes(&keys->k1,
Packit Service 31306d
                         (uint8_t *)&in_packet->length,
Packit Service 31306d
                         (uint8_t *)&out_packet->length,
Packit Service 31306d
                         sizeof(uint32_t));
Packit Service 31306d
Packit Service 31306d
    /* step 3, encrypt packet payload */
Packit Service 31306d
    chacha_ivsetup(&keys->k2, (uint8_t *)&seq, payload_block_counter);
Packit Service 31306d
    chacha_encrypt_bytes(&keys->k2,
Packit Service 31306d
                         in_packet->payload,
Packit Service 31306d
                         out_packet->payload,
Packit Service 31306d
                         len - sizeof(uint32_t));
Packit Service 31306d
Packit Service 31306d
    /* ssh_log_hexdump("poly1305_ctx", poly1305_ctx, sizeof(poly1305_ctx)); */
Packit Service 31306d
    /* step 4, compute the MAC */
Packit Service 31306d
    poly1305_auth(tag, (uint8_t *)out_packet, len, poly1305_ctx);
Packit Service 31306d
    /* ssh_log_hexdump("poly1305 src", (uint8_t *)out_packet, len);
Packit Service 31306d
    ssh_log_hexdump("poly1305 tag", tag, POLY1305_TAGLEN); */
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
static int chacha20_poly1305_aead_decrypt_length(
Packit Service 31306d
        struct ssh_cipher_struct *cipher,
Packit Service 31306d
        void *in,
Packit Service 31306d
        uint8_t *out,
Packit Service 31306d
        size_t len,
Packit Service 31306d
        uint64_t seq)
Packit Service 31306d
{
Packit Service 31306d
    struct chacha20_poly1305_keysched *keys = cipher->chacha20_schedule;
Packit Service 31306d
Packit Service 31306d
    if (len < sizeof(uint32_t)) {
Packit Service 31306d
        return SSH_ERROR;
Packit Service 31306d
    }
Packit Service 31306d
    seq = htonll(seq);
Packit Service 31306d
Packit Service 31306d
    chacha_ivsetup(&keys->k1, (uint8_t *)&seq, zero_block_counter);
Packit Service 31306d
    chacha_encrypt_bytes(&keys->k1,
Packit Service 31306d
                         in,
Packit Service 31306d
                         (uint8_t *)out,
Packit Service 31306d
                         sizeof(uint32_t));
Packit Service 31306d
    return SSH_OK;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
static int chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
Packit Service 31306d
                                          void *complete_packet,
Packit Service 31306d
                                          uint8_t *out,
Packit Service 31306d
                                          size_t encrypted_size,
Packit Service 31306d
                                          uint64_t seq)
Packit Service 31306d
{
Packit Service 31306d
    uint8_t poly1305_ctx[POLY1305_KEYLEN] = {0};
Packit Service 31306d
    uint8_t tag[POLY1305_TAGLEN] = {0};
Packit Service 31306d
    struct chacha20_poly1305_keysched *keys = cipher->chacha20_schedule;
Packit Service 31306d
    uint8_t *mac = (uint8_t *)complete_packet + sizeof(uint32_t) + encrypted_size;
Packit Service 31306d
    int cmp;
Packit Service 31306d
Packit Service 31306d
    seq = htonll(seq);
Packit Service 31306d
Packit Service 31306d
    ZERO_STRUCT(poly1305_ctx);
Packit Service 31306d
    chacha_ivsetup(&keys->k2, (uint8_t *)&seq, zero_block_counter);
Packit Service 31306d
    chacha_encrypt_bytes(&keys->k2,
Packit Service 31306d
                         poly1305_ctx,
Packit Service 31306d
                         poly1305_ctx,
Packit Service 31306d
                         POLY1305_KEYLEN);
Packit Service 31306d
#if 0
Packit Service 31306d
    ssh_log_hexdump("poly1305_ctx", poly1305_ctx, sizeof(poly1305_ctx));
Packit Service 31306d
#endif
Packit Service 31306d
Packit Service 31306d
    poly1305_auth(tag, (uint8_t *)complete_packet, encrypted_size +
Packit Service 31306d
            sizeof(uint32_t), poly1305_ctx);
Packit Service 31306d
#if 0
Packit Service 31306d
    ssh_log_hexdump("poly1305 src",
Packit Service 31306d
                   (uint8_t*)complete_packet,
Packit Service 31306d
                   encrypted_size + 4);
Packit Service 31306d
    ssh_log_hexdump("poly1305 tag", tag, POLY1305_TAGLEN);
Packit Service 31306d
    ssh_log_hexdump("received tag", mac, POLY1305_TAGLEN);
Packit Service 31306d
#endif
Packit Service 31306d
Packit Service 31306d
    cmp = memcmp(tag, mac, POLY1305_TAGLEN);
Packit Service 31306d
    if(cmp != 0) {
Packit Service 31306d
        /* mac error */
Packit Service 31306d
        SSH_LOG(SSH_LOG_PACKET,"poly1305 verify error");
Packit Service 31306d
        return SSH_ERROR;
Packit Service 31306d
    }
Packit Service 31306d
    chacha_ivsetup(&keys->k2, (uint8_t *)&seq, payload_block_counter);
Packit Service 31306d
    chacha_encrypt_bytes(&keys->k2,
Packit Service 31306d
                         (uint8_t *)complete_packet + sizeof(uint32_t),
Packit Service 31306d
                         out,
Packit Service 31306d
                         encrypted_size);
Packit Service 31306d
Packit Service 31306d
    return SSH_OK;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
static void chacha20_cleanup(struct ssh_cipher_struct *cipher) {
Packit Service 31306d
    SAFE_FREE(cipher->chacha20_schedule);
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
const struct ssh_cipher_struct chacha20poly1305_cipher = {
Packit Service 31306d
    .ciphertype = SSH_AEAD_CHACHA20_POLY1305,
Packit Service 31306d
    .name = "chacha20-poly1305@openssh.com",
Packit Service 31306d
    .blocksize = 8,
Packit Service 31306d
    .lenfield_blocksize = 4,
Packit Service 31306d
    .keylen = sizeof(struct chacha20_poly1305_keysched),
Packit Service 31306d
    .keysize = 512,
Packit Service 31306d
    .tag_size = POLY1305_TAGLEN,
Packit Service 31306d
    .set_encrypt_key = chacha20_set_encrypt_key,
Packit Service 31306d
    .set_decrypt_key = chacha20_set_encrypt_key,
Packit Service 31306d
    .aead_encrypt = chacha20_poly1305_aead_encrypt,
Packit Service 31306d
    .aead_decrypt_length = chacha20_poly1305_aead_decrypt_length,
Packit Service 31306d
    .aead_decrypt = chacha20_poly1305_aead_decrypt,
Packit Service 31306d
    .cleanup = chacha20_cleanup
Packit Service 31306d
};
Packit Service 31306d
Packit Service 31306d
const struct ssh_cipher_struct *ssh_get_chacha20poly1305_cipher(void)
Packit Service 31306d
{
Packit Service 31306d
    return &chacha20poly1305_cipher;
Packit Service 31306d
}