Blame src/chachapoly.c

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