| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 | /* * Implementation of HMAC (RFC 2104) for PuTTY, in a general form that * can wrap any underlying hash function. */#include "ssh.h"struct hmac {    const ssh_hashalg *hashalg;    ssh_hash *h_outer, *h_inner, *h_live;    uint8_t *digest;    strbuf *text_name;    ssh2_mac mac;};struct hmac_extra {    const ssh_hashalg *hashalg_base;    const char *suffix, *annotation;};/* Most of hmac_new(). Takes the actual 'struct hmac' as a parameter, * because sometimes it will have been allocated in a special way. */static ssh2_mac *hmac_new_inner(struct hmac *ctx, const ssh2_macalg *alg){    const struct hmac_extra *extra = (const struct hmac_extra *)alg->extra;    ctx->h_outer = ssh_hash_new(extra->hashalg_base);    /* In case that hashalg was a selector vtable, we'll now switch to     * using whatever real one it selected, for all future purposes. */    ctx->hashalg = ssh_hash_alg(ctx->h_outer);    ctx->h_inner = ssh_hash_new(ctx->hashalg);    ctx->h_live = ssh_hash_new(ctx->hashalg);    /*     * HMAC is not well defined as a wrapper on an absolutely general     * hash function; it expects that the function it's wrapping will     * consume data in fixed-size blocks, and it's partially defined     * in terms of that block size. So we insist that the hash we're     * given must have defined a meaningful block size.     */    assert(ctx->hashalg->blocklen);    ctx->digest = snewn(ctx->hashalg->hlen, uint8_t);    ctx->text_name = strbuf_new();    put_fmt(ctx->text_name, "HMAC-%s%s",            ctx->hashalg->text_basename, extra->suffix);    if (extra->annotation || ctx->hashalg->annotation) {        put_fmt(ctx->text_name, " (");        { // WINSCP        const char *sep = "";        if (extra->annotation) {            put_fmt(ctx->text_name, "%s%s", sep, extra->annotation);            sep = ", ";        }        if (ctx->hashalg->annotation) {            put_fmt(ctx->text_name, "%s%s", sep, ctx->hashalg->annotation);            sep = ", ";        }        put_fmt(ctx->text_name, ")");        } // WINSCP    }    ctx->mac.vt = alg;    BinarySink_DELEGATE_INIT(&ctx->mac, ctx->h_live);    return &ctx->mac;}static ssh2_mac *hmac_new(const ssh2_macalg *alg, ssh_cipher *cipher){    return hmac_new_inner(snew(struct hmac), alg); /* cipher isn't needed */}static void hmac_free(ssh2_mac *mac){    struct hmac *ctx = container_of(mac, struct hmac, mac);    ssh_hash_free(ctx->h_outer);    ssh_hash_free(ctx->h_inner);    ssh_hash_free(ctx->h_live);    smemclr(ctx->digest, ctx->hashalg->hlen);    sfree(ctx->digest);    strbuf_free(ctx->text_name);    smemclr(ctx, sizeof(*ctx));    sfree(ctx);}#define PAD_OUTER 0x5C#define PAD_INNER 0x36static void hmac_key(ssh2_mac *mac, ptrlen key){    struct hmac *ctx = container_of(mac, struct hmac, mac);    const uint8_t *kp;    size_t klen;    strbuf *sb = NULL;    if (key.len > ctx->hashalg->blocklen) {        /*         * RFC 2104 section 2: if the key exceeds the block length of         * the underlying hash, then we start by hashing the key, and         * use that hash as the 'true' key for the HMAC construction.         */        sb = strbuf_new_nm();        strbuf_append(sb, ctx->hashalg->hlen);        hash_simple(ctx->hashalg, key, sb->u);        { // WINSCP        kp = sb->u;        klen = sb->len;        } // WINSCP    } else {        /*         * A short enough key is used as is.         */        kp = (const uint8_t *)key.ptr;        klen = key.len;    }    ssh_hash_reset(ctx->h_outer);    { // WINSCP    size_t i; // WINSCP    for (i = 0; i < klen; i++)        put_byte(ctx->h_outer, PAD_OUTER ^ kp[i]);    for (i = klen; i < ctx->hashalg->blocklen; i++)        put_byte(ctx->h_outer, PAD_OUTER);    ssh_hash_reset(ctx->h_inner);    for (i = 0; i < klen; i++)        put_byte(ctx->h_inner, PAD_INNER ^ kp[i]);    for (i = klen; i < ctx->hashalg->blocklen; i++)        put_byte(ctx->h_inner, PAD_INNER);    if (sb)        strbuf_free(sb);    } // WINSCP}static void hmac_start(ssh2_mac *mac){    struct hmac *ctx = container_of(mac, struct hmac, mac);    ssh_hash_copyfrom(ctx->h_live, ctx->h_inner);}static void hmac_genresult(ssh2_mac *mac, unsigned char *output){    struct hmac *ctx = container_of(mac, struct hmac, mac);    ssh_hash *htmp;    /* Leave h_live and h_outer in place, so that the SSH-2 BPP can     * continue regenerating test results from different-length     * prefixes of the packet */    ssh_hash_digest_nondestructive(ctx->h_live, ctx->digest);    htmp = ssh_hash_copy(ctx->h_outer);    put_data(htmp, ctx->digest, ctx->hashalg->hlen);    ssh_hash_final(htmp, ctx->digest);    /*     * Some instances of HMAC truncate the output hash, so instead of     * writing it directly to 'output' we wrote it to our own     * full-length buffer, and now we copy the required amount.     */    memcpy(output, ctx->digest, mac->vt->len);    smemclr(ctx->digest, ctx->hashalg->hlen);}static const char *hmac_text_name(ssh2_mac *mac){    struct hmac *ctx = container_of(mac, struct hmac, mac);    return ctx->text_name->s;}static const struct hmac_extra ssh_hmac_sha512_extra = { &ssh_sha512, "" };const ssh2_macalg ssh_hmac_sha512 = {    /*.new =*/ hmac_new,    /*.free =*/ hmac_free,    /*.setkey =*/ hmac_key,    /*.start =*/ hmac_start,    /*.genresult =*/ hmac_genresult,    /*.next_message =*/ nullmac_next_message,    /*.text_name =*/ hmac_text_name,    /*.name =*/ "hmac-sha2-512",    /*.etm_name =*/ "[email protected]",    /*.len =*/ 64,    /*.keylen =*/ 64,    /*.extra =*/ &ssh_hmac_sha512_extra,};static const struct hmac_extra ssh_hmac_sha256_extra = { &ssh_sha256, "" };const ssh2_macalg ssh_hmac_sha256 = {    // WINSCP    /*.new =*/ hmac_new,    /*.free =*/ hmac_free,    /*.setkey =*/ hmac_key,    /*.start =*/ hmac_start,    /*.genresult =*/ hmac_genresult,    /*.next_message =*/ nullmac_next_message,    /*.text_name =*/ hmac_text_name,    /*.name =*/ "hmac-sha2-256",    /*.etm_name =*/ "[email protected]",    /*.len =*/ 32,    /*.keylen =*/ 32,    /*.extra =*/ &ssh_hmac_sha256_extra,};static const struct hmac_extra ssh_hmac_md5_extra = { &ssh_md5, "" };const ssh2_macalg ssh_hmac_md5 = {    // WINSCP    /*.new =*/ hmac_new,    /*.free =*/ hmac_free,    /*.setkey =*/ hmac_key,    /*.start =*/ hmac_start,    /*.genresult =*/ hmac_genresult,    /*.next_message =*/ nullmac_next_message,    /*.text_name =*/ hmac_text_name,    /*.name =*/ "hmac-md5",    /*.etm_name =*/ "[email protected]",    /*.len =*/ 16,    /*.keylen =*/ 16,    /*.extra =*/ &ssh_hmac_md5_extra,};static const struct hmac_extra ssh_hmac_sha1_extra = { &ssh_sha1, "" };const ssh2_macalg ssh_hmac_sha1 = {    // WINSCP    /*.new =*/ hmac_new,    /*.free =*/ hmac_free,    /*.setkey =*/ hmac_key,    /*.start =*/ hmac_start,    /*.genresult =*/ hmac_genresult,    /*.next_message =*/ nullmac_next_message,    /*.text_name =*/ hmac_text_name,    /*.name =*/ "hmac-sha1",    /*.etm_name =*/ "[email protected]",    /*.len =*/ 20,    /*.keylen =*/ 20,    /*.extra =*/ &ssh_hmac_sha1_extra,};static const struct hmac_extra ssh_hmac_sha1_96_extra = { &ssh_sha1, "-96" };const ssh2_macalg ssh_hmac_sha1_96 = {    // WINSCP    /*.new =*/ hmac_new,    /*.free =*/ hmac_free,    /*.setkey =*/ hmac_key,    /*.start =*/ hmac_start,    /*.genresult =*/ hmac_genresult,    /*.next_message =*/ nullmac_next_message,    /*.text_name =*/ hmac_text_name,    /*.name =*/ "hmac-sha1-96",    /*.etm_name =*/ "[email protected]",    /*.len =*/ 12,    /*.keylen =*/ 20,    /*.extra =*/ &ssh_hmac_sha1_96_extra,};static const struct hmac_extra ssh_hmac_sha1_buggy_extra = {    &ssh_sha1, "", "bug-compatible"};const ssh2_macalg ssh_hmac_sha1_buggy = {    // WINSCP    /*.new =*/ hmac_new,    /*.free =*/ hmac_free,    /*.setkey =*/ hmac_key,    /*.start =*/ hmac_start,    /*.genresult =*/ hmac_genresult,    /*.next_message =*/ nullmac_next_message,    /*.text_name =*/ hmac_text_name,    /*.name =*/ "hmac-sha1",    NULL, // WINSCP    /*.len =*/ 20,    /*.keylen =*/ 16,    /*.extra =*/ &ssh_hmac_sha1_buggy_extra,};static const struct hmac_extra ssh_hmac_sha1_96_buggy_extra = {    &ssh_sha1, "-96", "bug-compatible"};const ssh2_macalg ssh_hmac_sha1_96_buggy = {    // WINSCP    /*.new =*/ hmac_new,    /*.free =*/ hmac_free,    /*.setkey =*/ hmac_key,    /*.start =*/ hmac_start,    /*.genresult =*/ hmac_genresult,    /*.next_message =*/ nullmac_next_message,    /*.text_name =*/ hmac_text_name,    /*.name =*/ "hmac-sha1-96",    NULL, // WINSCP    /*.len =*/ 12,    /*.keylen =*/ 16,    /*.extra =*/ &ssh_hmac_sha1_96_buggy_extra,};ssh2_mac *hmac_new_from_hash(const ssh_hashalg *hash){    /*     * Construct a custom ssh2_macalg, derived directly from the     * provided hash vtable. It's included in the same memory     * allocation as the struct hmac, so that it all gets freed     * together.     */    struct alloc {        struct hmac hmac;        ssh2_macalg alg;        struct hmac_extra extra;    };    struct alloc *alloc = snew(struct alloc);    alloc->alg.new = hmac_new;    alloc->alg.free = hmac_free;    alloc->alg.setkey = hmac_key;    alloc->alg.start = hmac_start;    alloc->alg.genresult = hmac_genresult;    alloc->alg.next_message = nullmac_next_message;    alloc->alg.text_name = hmac_text_name;    alloc->alg.name = NULL;    alloc->alg.etm_name = NULL;    alloc->alg.len = hash->hlen;    alloc->alg.keylen = hash->hlen;    alloc->alg.extra = &alloc->extra;    alloc->extra.hashalg_base = hash;    alloc->extra.suffix = "";    alloc->extra.annotation = NULL;    return hmac_new_inner(&alloc->hmac, &alloc->alg);}
 |