Welcome to mirror list, hosted at ThFree Co, Russian Federation.

aesgcm-footer.h « crypto - github.com/mRemoteNG/PuTTYNG.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 981905da397a928d6532240414ba0e5d280aa991 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
/*
 * Common footer included by every implementation of the AES-GCM MAC.
 *
 * The difficult part of AES-GCM, which is done differently depending
 * on what hardware acceleration is available, is the actual
 * evaluation of a polynomial over GF(2^128) whose coefficients are
 * 128-bit chunks of data. But preparing those chunks in the first
 * place (out of the ciphertext, associated data, and an
 * administrative block containing the lengths of both) is done in the
 * same way no matter what technique is used for the evaluation, so
 * that's centralised into this file, along with as much of the other
 * functionality as posible.
 *
 * This footer file is #included by each implementation, but each one
 * will define its own struct type for the state, so that each alloc
 * function will test sizeof() a different structure, and similarly
 * for free when it zeroes out the state on cleanup.
 *
 * The functions in the source file may be defined as 'inline' so that
 * the functions in here can inline them. The 'coeff' function in
 * particular probably should be, because that's called once per
 * 16-byte block, so eliminating function call overheads is especially
 * useful there.
 *
 * This footer has the following expectations from the source file
 * that #includes it:
 *
 *  - define AESGCM_FLAVOUR to be a fragment of a C identifier that
 *    will be included in all the function names (both the ones
 *    defined in the implementation source file and those in here).
 *    For example purposes below I'll suppose that this is 'foo'.
 *
 *  - define AESGCM_NAME to be a string literal that will be included
 *    in the display name of the implementation.
 *
 *  - define a typedef 'aesgcm_foo' to be the state structure for the
 *    implementation, and inside that structure, expand the macro
 *    AESGCM_COMMON_FIELDS defined in aesgcm.h
 *
 *  - define the following functions:
 *
 *    // Determine whether this implementation is available at run time
 *    static bool aesgcm_foo_available(void);
 *
 *    // Set up the 'key' of the polynomial part of the MAC, that is,
 *    // the value at which the polynomial will be evaluated. 'var' is
 *    // a 16-byte data block in the byte order it comes out of AES.
 *    static void aesgcm_foo_setkey_impl(aesgcm_foo *ctx,
 *                                       const unsigned char *var);
 *
 *    // Set up at the start of evaluating an individual polynomial.
 *    // 'mask' is the 16-byte data block that will be XORed into the
 *    // output value of the polynomial, also in AES byte order. This
 *    // function should store 'mask' in whatever form is most
 *    // convenient, and initialise an accumulator to zero.
 *    static void aesgcm_foo_setup(aesgcm_foo *ctx,
 *                                 const unsigned char *mask);
 *
 *    // Fold in a coefficient of the polynomial, by means of XORing
 *    // it into the accumulator and then multiplying the accumulator
 *    // by the variable passed to setkey_impl() above.
 *    //
 *    // 'coeff' points to the 16-byte block of data that the
 *    // polynomial coefficient will be made out of.
 *    //
 *    // You probably want to mark this function 'inline'.
 *    static void aesgcm_foo_coeff(aesgcm_foo *ctx,
 *                                 const unsigned char *coeff);
 *
 *    // Generate the output MAC, by XORing the accumulator's final
 *    // value with the mask passed to setup() above.
 *    //
 *    // 'output' points to a 16-byte region of memory to write the
 *    // result to.
 *    static void aesgcm_foo_output(aesgcm_foo *ctx,
 *                                  unsigned char *output);
 *
 *  - if allocation of the state structure must be done in a
 *    non-standard way (e.g. x86 needs this to force greater alignment
 *    than standard malloc provides), then #define SPECIAL_ALLOC and
 *    define this additional function:
 *
 *    // Allocate a state structure, zero out its contents, and return it.
 *    static aesgcm_foo *aesgcm_foo_alloc(void);
 *
 *  - if freeing must also be done in an unusual way, #define
 *    SPECIAL_FREE and define this function:
 *
 *    // Zero out the state structure to avoid information leaks if the
 *    // memory is reused, and then free it.
 *    static void aesgcm_foo_free(aesgcm_foo *ctx);
 */

#ifndef AESGCM_FLAVOUR
#error AESGCM_FLAVOUR must be defined by any module including this footer
#endif
#ifndef AESGCM_NAME
#error AESGCM_NAME must be defined by any module including this footer
#endif

#define CONTEXT CAT(aesgcm_, AESGCM_FLAVOUR)
#define PREFIX(name) CAT(CAT(aesgcm_, AESGCM_FLAVOUR), CAT(_, name))

#include "aes.h" // for aes_encrypt_ecb_block

static const char *PREFIX(mac_text_name)(ssh2_mac *mac)
{
    return "AES-GCM (" AESGCM_NAME ")";
}

static void PREFIX(mac_next_message)(ssh2_mac *mac)
{
    CONTEXT *ctx = container_of(mac, CONTEXT, mac);

    /*
     * Make the mask value for a single MAC instance, by encrypting
     * the all-zeroes word using the associated AES instance in its
     * ordinary GCM fashion. This consumes the first block of
     * keystream (with per-block counter equal to 1), leaving the
     * second block of keystream ready to be used on the first block
     * of plaintext.
     */
    unsigned char buf[16];
    memset(buf, 0, 16);
    ssh_cipher_encrypt(ctx->cipher, buf, 16);
    PREFIX(setup)(ctx, buf); /* give it to the implementation to store */
    smemclr(buf, sizeof(buf));
}

static void PREFIX(mac_setkey)(ssh2_mac *mac, ptrlen key)
{
    CONTEXT *ctx = container_of(mac, CONTEXT, mac);

    /*
     * Make the value of the polynomial variable, by encrypting the
     * all-zeroes word using the associated AES instance in the
     * special ECB mode. This is done via the special AES-specific API
     * function encrypt_ecb_block, which doesn't touch the counter
     * state at all.
     */
    unsigned char var[16];
    memset(var, 0, 16);
    aes_encrypt_ecb_block(ctx->cipher, var);
    PREFIX(setkey_impl)(ctx, var);
    smemclr(var, sizeof(var));

    PREFIX(mac_next_message)(mac);     /* set up mask */
}

static void PREFIX(mac_start)(ssh2_mac *mac)
{
    CONTEXT *ctx = container_of(mac, CONTEXT, mac);

    ctx->skipgot = ctx->aadgot = ctx->ciphertextlen = ctx->partlen = 0;
}

/*
 * Handle receiving data via the BinarySink API and turning it into a
 * collection of 16-byte blocks to use as polynomial coefficients.
 *
 * This code is written in a fully general way, which is able to
 * handle an arbitrary number of bytes at the start of the data to
 * ignore completely (necessary for PuTTY integration), and an
 * arbitrary number to treat as associated data, and the rest will be
 * regarded as ciphertext. The stream can be interrupted at any byte
 * position and resumed later; a partial block will be stored as
 * necessary.
 *
 * At the time of writing this comment, in live use most of that
 * generality isn't required: the full data is passed to this function
 * in just one call. But there's no guarantee of that staying true in
 * future, so we do the full deal here just in case, and the test
 * vectors in cryptsuite.py will test it. (And they'll use
 * set_prefix_lengths to set up different configurations from the SSH
 * usage.)
 */
static void PREFIX(mac_BinarySink_write)(
    BinarySink *bs, const void *blkv, size_t len)
{
    CONTEXT *ctx = BinarySink_DOWNCAST(bs, CONTEXT);
    const unsigned char *blk = (const unsigned char *)blkv;

    /*
     * Skip the prefix sequence number used as implicit extra data in
     * SSH MACs. This is not included in the associated data field for
     * GCM, because the IV incrementation policy provides its own
     * sequence numbering.
     */
    if (ctx->skipgot < ctx->skiplen) {
        size_t n = ctx->skiplen - ctx->skipgot;
        if (n > len)
            n = len;
        blk += n;
        len -= n;
        ctx->skipgot += n;

        if (len == 0)
            return;
    }

    /*
     * Read additional authenticated data and fold it in to the MAC.
     */
    while (ctx->aadgot < ctx->aadlen) {
        size_t n = ctx->aadlen - ctx->aadgot;
        if (n > len)
            n = len;

        if (ctx->partlen || n < 16) {
            /*
             * Fold data into the partial block.
             */
            if (n > 16 - ctx->partlen)
                n = 16 - ctx->partlen;
            memcpy(ctx->partblk + ctx->partlen, blk, n);
            ctx->partlen += n;
        } else if (n >= 16) {
            /*
             * Consume a whole block of AAD.
             */
            PREFIX(coeff)(ctx, blk);
            n = 16;
        }
        blk += n;
        len -= n;
        ctx->aadgot += n;

        if (ctx->partlen == 16) {
            PREFIX(coeff)(ctx, ctx->partblk);
            ctx->partlen = 0;
        }

        if (ctx->aadgot == ctx->aadlen && ctx->partlen) {
            memset(ctx->partblk + ctx->partlen, 0, 16 - ctx->partlen);
            PREFIX(coeff)(ctx, ctx->partblk);
            ctx->partlen = 0;
        }

        if (len == 0)
            return;
    }

    /*
     * Read the main ciphertext and fold it in to the MAC.
     */
    while (len > 0) {
        size_t n = len;

        if (ctx->partlen || n < 16) {
            /*
             * Fold data into the partial block.
             */
            if (n > 16 - ctx->partlen)
                n = 16 - ctx->partlen;
            memcpy(ctx->partblk + ctx->partlen, blk, n);
            ctx->partlen += n;
        } else if (n >= 16) {
            /*
             * Consume a whole block of ciphertext.
             */
            PREFIX(coeff)(ctx, blk);
            n = 16;
        }
        blk += n;
        len -= n;
        ctx->ciphertextlen += n;

        if (ctx->partlen == 16) {
            PREFIX(coeff)(ctx, ctx->partblk);
            ctx->partlen = 0;
        }
    }
}

static void PREFIX(mac_genresult)(ssh2_mac *mac, unsigned char *output)
{
    CONTEXT *ctx = container_of(mac, CONTEXT, mac);

    /*
     * Consume any partial block of ciphertext remaining.
     */
    if (ctx->partlen) {
        memset(ctx->partblk + ctx->partlen, 0, 16 - ctx->partlen);
        PREFIX(coeff)(ctx, ctx->partblk);
    }

    /*
     * Consume the final block giving the lengths of the AAD and ciphertext.
     */
    unsigned char blk[16];
    memset(blk, 0, 16);
    PUT_64BIT_MSB_FIRST(blk, ctx->aadlen * 8);
    PUT_64BIT_MSB_FIRST(blk + 8, ctx->ciphertextlen * 8);
    PREFIX(coeff)(ctx, blk);

    /*
     * And call the implementation's output function.
     */
    PREFIX(output)(ctx, output);

    smemclr(blk, sizeof(blk));
    smemclr(ctx->partblk, 16);
}

static ssh2_mac *PREFIX(mac_new)(const ssh2_macalg *alg, ssh_cipher *cipher)
{
    const struct aesgcm_extra *extra = alg->extra;
    if (!check_aesgcm_availability(extra))
        return NULL;

#ifdef SPECIAL_ALLOC
    CONTEXT *ctx = PREFIX(alloc)();
#else
    CONTEXT *ctx = snew(CONTEXT);
    memset(ctx, 0, sizeof(CONTEXT));
#endif

    ctx->mac.vt = alg;
    ctx->cipher = cipher;
    /* Default values for SSH-2, overridable by set_prefix_lengths for
     * testcrypt purposes */
    ctx->skiplen = 4;
    ctx->aadlen = 4;
    BinarySink_INIT(ctx, PREFIX(mac_BinarySink_write));
    BinarySink_DELEGATE_INIT(&ctx->mac, ctx);
    return &ctx->mac;
}

static void PREFIX(set_prefix_lengths)(ssh2_mac *mac, size_t skip, size_t aad)
{
    CONTEXT *ctx = container_of(mac, CONTEXT, mac);
    ctx->skiplen = skip;
    ctx->aadlen = aad;
}

static void PREFIX(mac_free)(ssh2_mac *mac)
{
    CONTEXT *ctx = container_of(mac, CONTEXT, mac);
#ifdef SPECIAL_FREE
    PREFIX(free)(ctx);
#else
    smemclr(ctx, sizeof(*ctx));
    sfree(ctx);
#endif
}

static struct aesgcm_extra_mutable PREFIX(extra_mut);

static const struct aesgcm_extra PREFIX(extra) = {
    .check_available = PREFIX(available),
    .mut = &PREFIX(extra_mut),
    .set_prefix_lengths = PREFIX(set_prefix_lengths),
};

const ssh2_macalg CAT(ssh2_aesgcm_mac_, AESGCM_FLAVOUR) = {
    .new = PREFIX(mac_new),
    .free = PREFIX(mac_free),
    .setkey = PREFIX(mac_setkey),
    .start = PREFIX(mac_start),
    .genresult = PREFIX(mac_genresult),
    .next_message = PREFIX(mac_next_message),
    .text_name = PREFIX(mac_text_name),
    .name = "",
    .etm_name = "", /* Not selectable independently */
    .len = 16,
    .keylen = 0,
    .extra = &PREFIX(extra),
};