|
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 |
}
|