1 1.10 pgoyette /* $NetBSD: cipher-ctr-mt.c,v 1.10 2019/01/27 02:08:33 pgoyette Exp $ */ 2 1.1 christos /* 3 1.1 christos * OpenSSH Multi-threaded AES-CTR Cipher 4 1.1 christos * 5 1.1 christos * Author: Benjamin Bennett <ben (at) psc.edu> 6 1.1 christos * Copyright (c) 2008 Pittsburgh Supercomputing Center. All rights reserved. 7 1.1 christos * 8 1.1 christos * Based on original OpenSSH AES-CTR cipher. Small portions remain unchanged, 9 1.1 christos * Copyright (c) 2003 Markus Friedl <markus (at) openbsd.org> 10 1.1 christos * 11 1.1 christos * Permission to use, copy, modify, and distribute this software for any 12 1.1 christos * purpose with or without fee is hereby granted, provided that the above 13 1.1 christos * copyright notice and this permission notice appear in all copies. 14 1.1 christos * 15 1.1 christos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16 1.1 christos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 1.1 christos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 18 1.1 christos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 1.1 christos * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 1.1 christos * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 21 1.1 christos * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 1.1 christos */ 23 1.1 christos #include "includes.h" 24 1.9 christos __RCSID("$NetBSD: cipher-ctr-mt.c,v 1.10 2019/01/27 02:08:33 pgoyette Exp $"); 25 1.1 christos 26 1.1 christos #include <sys/types.h> 27 1.1 christos 28 1.1 christos #include <stdarg.h> 29 1.1 christos #include <string.h> 30 1.1 christos 31 1.1 christos #include <openssl/evp.h> 32 1.1 christos 33 1.1 christos #include "xmalloc.h" 34 1.1 christos #include "log.h" 35 1.1 christos 36 1.1 christos #ifndef USE_BUILTIN_RIJNDAEL 37 1.1 christos #include <openssl/aes.h> 38 1.1 christos #endif 39 1.1 christos 40 1.1 christos #include <pthread.h> 41 1.1 christos 42 1.1 christos /*-------------------- TUNABLES --------------------*/ 43 1.1 christos /* Number of pregen threads to use */ 44 1.1 christos #define CIPHER_THREADS 2 45 1.1 christos 46 1.1 christos /* Number of keystream queues */ 47 1.1 christos #define NUMKQ (CIPHER_THREADS + 2) 48 1.1 christos 49 1.1 christos /* Length of a keystream queue */ 50 1.1 christos #define KQLEN 4096 51 1.1 christos 52 1.1 christos /* Processor cacheline length */ 53 1.1 christos #define CACHELINE_LEN 64 54 1.1 christos 55 1.1 christos /* Collect thread stats and print at cancellation when in debug mode */ 56 1.1 christos /* #define CIPHER_THREAD_STATS */ 57 1.1 christos 58 1.1 christos /* Use single-byte XOR instead of 8-byte XOR */ 59 1.1 christos /* #define CIPHER_BYTE_XOR */ 60 1.1 christos /*-------------------- END TUNABLES --------------------*/ 61 1.1 christos 62 1.1 christos #ifdef AES_CTR_MT 63 1.1 christos 64 1.1 christos 65 1.1 christos const EVP_CIPHER *evp_aes_ctr_mt(void); 66 1.1 christos 67 1.1 christos #ifdef CIPHER_THREAD_STATS 68 1.1 christos /* 69 1.1 christos * Struct to collect thread stats 70 1.1 christos */ 71 1.1 christos struct thread_stats { 72 1.1 christos u_int fills; 73 1.1 christos u_int skips; 74 1.1 christos u_int waits; 75 1.1 christos u_int drains; 76 1.1 christos }; 77 1.1 christos 78 1.1 christos /* 79 1.1 christos * Debug print the thread stats 80 1.1 christos * Use with pthread_cleanup_push for displaying at thread cancellation 81 1.1 christos */ 82 1.1 christos static void 83 1.1 christos thread_loop_stats(void *x) 84 1.1 christos { 85 1.1 christos struct thread_stats *s = x; 86 1.1 christos 87 1.1 christos debug("tid %lu - %u fills, %u skips, %u waits", pthread_self(), 88 1.1 christos s->fills, s->skips, s->waits); 89 1.1 christos } 90 1.1 christos 91 1.2 adam #define STATS_STRUCT(s) struct thread_stats s 92 1.2 adam #define STATS_INIT(s) { memset(&s, 0, sizeof(s)); } 93 1.2 adam #define STATS_FILL(s) { s.fills++; } 94 1.2 adam #define STATS_SKIP(s) { s.skips++; } 95 1.2 adam #define STATS_WAIT(s) { s.waits++; } 96 1.2 adam #define STATS_DRAIN(s) { s.drains++; } 97 1.1 christos #else 98 1.1 christos #define STATS_STRUCT(s) 99 1.1 christos #define STATS_INIT(s) 100 1.1 christos #define STATS_FILL(s) 101 1.1 christos #define STATS_SKIP(s) 102 1.1 christos #define STATS_WAIT(s) 103 1.1 christos #define STATS_DRAIN(s) 104 1.1 christos #endif 105 1.1 christos 106 1.1 christos /* Keystream Queue state */ 107 1.1 christos enum { 108 1.1 christos KQINIT, 109 1.1 christos KQEMPTY, 110 1.1 christos KQFILLING, 111 1.1 christos KQFULL, 112 1.1 christos KQDRAINING 113 1.1 christos }; 114 1.1 christos 115 1.1 christos /* Keystream Queue struct */ 116 1.1 christos struct kq { 117 1.1 christos u_char keys[KQLEN][AES_BLOCK_SIZE]; 118 1.1 christos u_char ctr[AES_BLOCK_SIZE]; 119 1.1 christos u_char pad0[CACHELINE_LEN]; 120 1.1 christos volatile int qstate; 121 1.1 christos pthread_mutex_t lock; 122 1.1 christos pthread_cond_t cond; 123 1.1 christos u_char pad1[CACHELINE_LEN]; 124 1.1 christos }; 125 1.1 christos 126 1.1 christos /* Context struct */ 127 1.1 christos struct ssh_aes_ctr_ctx 128 1.1 christos { 129 1.1 christos struct kq q[NUMKQ]; 130 1.1 christos AES_KEY aes_ctx; 131 1.2 adam STATS_STRUCT(stats); 132 1.1 christos u_char aes_counter[AES_BLOCK_SIZE]; 133 1.1 christos pthread_t tid[CIPHER_THREADS]; 134 1.1 christos int state; 135 1.1 christos int qidx; 136 1.1 christos int ridx; 137 1.1 christos }; 138 1.1 christos 139 1.1 christos /* <friedl> 140 1.1 christos * increment counter 'ctr', 141 1.1 christos * the counter is of size 'len' bytes and stored in network-byte-order. 142 1.1 christos * (LSB at ctr[len-1], MSB at ctr[0]) 143 1.1 christos */ 144 1.1 christos static void 145 1.1 christos ssh_ctr_inc(u_char *ctr, u_int len) 146 1.1 christos { 147 1.1 christos int i; 148 1.1 christos 149 1.1 christos for (i = len - 1; i >= 0; i--) 150 1.1 christos if (++ctr[i]) /* continue on overflow */ 151 1.1 christos return; 152 1.1 christos } 153 1.1 christos 154 1.1 christos /* 155 1.1 christos * Add num to counter 'ctr' 156 1.1 christos */ 157 1.1 christos static void 158 1.1 christos ssh_ctr_add(u_char *ctr, uint32_t num, u_int len) 159 1.1 christos { 160 1.1 christos int i; 161 1.1 christos uint16_t n; 162 1.1 christos 163 1.1 christos for (n = 0, i = len - 1; i >= 0 && (num || n); i--) { 164 1.1 christos n = ctr[i] + (num & 0xff) + n; 165 1.1 christos num >>= 8; 166 1.1 christos ctr[i] = n & 0xff; 167 1.1 christos n >>= 8; 168 1.1 christos } 169 1.1 christos } 170 1.1 christos 171 1.1 christos /* 172 1.1 christos * Threads may be cancelled in a pthread_cond_wait, we must free the mutex 173 1.1 christos */ 174 1.1 christos static void 175 1.1 christos thread_loop_cleanup(void *x) 176 1.1 christos { 177 1.1 christos pthread_mutex_unlock((pthread_mutex_t *)x); 178 1.1 christos } 179 1.1 christos 180 1.1 christos /* 181 1.1 christos * The life of a pregen thread: 182 1.1 christos * Find empty keystream queues and fill them using their counter. 183 1.1 christos * When done, update counter for the next fill. 184 1.1 christos */ 185 1.1 christos static void * 186 1.1 christos thread_loop(void *x) 187 1.1 christos { 188 1.1 christos AES_KEY key; 189 1.2 adam STATS_STRUCT(stats); 190 1.1 christos struct ssh_aes_ctr_ctx *c = x; 191 1.1 christos struct kq *q; 192 1.1 christos int i; 193 1.1 christos int qidx; 194 1.1 christos 195 1.1 christos /* Threads stats on cancellation */ 196 1.1 christos STATS_INIT(stats); 197 1.1 christos #ifdef CIPHER_THREAD_STATS 198 1.1 christos pthread_cleanup_push(thread_loop_stats, &stats); 199 1.1 christos #endif 200 1.1 christos 201 1.1 christos /* Thread local copy of AES key */ 202 1.1 christos memcpy(&key, &c->aes_ctx, sizeof(key)); 203 1.1 christos 204 1.1 christos /* 205 1.1 christos * Handle the special case of startup, one thread must fill 206 1.1 christos * the first KQ then mark it as draining. Lock held throughout. 207 1.1 christos */ 208 1.1 christos if (pthread_equal(pthread_self(), c->tid[0])) { 209 1.1 christos q = &c->q[0]; 210 1.1 christos pthread_mutex_lock(&q->lock); 211 1.1 christos if (q->qstate == KQINIT) { 212 1.1 christos for (i = 0; i < KQLEN; i++) { 213 1.1 christos AES_encrypt(q->ctr, q->keys[i], &key); 214 1.1 christos ssh_ctr_inc(q->ctr, AES_BLOCK_SIZE); 215 1.1 christos } 216 1.1 christos ssh_ctr_add(q->ctr, KQLEN * (NUMKQ - 1), AES_BLOCK_SIZE); 217 1.1 christos q->qstate = KQDRAINING; 218 1.1 christos STATS_FILL(stats); 219 1.1 christos pthread_cond_broadcast(&q->cond); 220 1.1 christos } 221 1.1 christos pthread_mutex_unlock(&q->lock); 222 1.1 christos } 223 1.1 christos else 224 1.1 christos STATS_SKIP(stats); 225 1.1 christos 226 1.1 christos /* 227 1.1 christos * Normal case is to find empty queues and fill them, skipping over 228 1.1 christos * queues already filled by other threads and stopping to wait for 229 1.1 christos * a draining queue to become empty. 230 1.1 christos * 231 1.1 christos * Multiple threads may be waiting on a draining queue and awoken 232 1.1 christos * when empty. The first thread to wake will mark it as filling, 233 1.1 christos * others will move on to fill, skip, or wait on the next queue. 234 1.1 christos */ 235 1.1 christos for (qidx = 1;; qidx = (qidx + 1) % NUMKQ) { 236 1.1 christos /* Check if I was cancelled, also checked in cond_wait */ 237 1.1 christos pthread_testcancel(); 238 1.1 christos 239 1.1 christos /* Lock queue and block if its draining */ 240 1.1 christos q = &c->q[qidx]; 241 1.1 christos pthread_mutex_lock(&q->lock); 242 1.1 christos pthread_cleanup_push(thread_loop_cleanup, &q->lock); 243 1.1 christos while (q->qstate == KQDRAINING || q->qstate == KQINIT) { 244 1.1 christos STATS_WAIT(stats); 245 1.1 christos pthread_cond_wait(&q->cond, &q->lock); 246 1.1 christos } 247 1.1 christos pthread_cleanup_pop(0); 248 1.1 christos 249 1.1 christos /* If filling or full, somebody else got it, skip */ 250 1.1 christos if (q->qstate != KQEMPTY) { 251 1.1 christos pthread_mutex_unlock(&q->lock); 252 1.1 christos STATS_SKIP(stats); 253 1.1 christos continue; 254 1.1 christos } 255 1.1 christos 256 1.1 christos /* 257 1.1 christos * Empty, let's fill it. 258 1.1 christos * Queue lock is relinquished while we do this so others 259 1.1 christos * can see that it's being filled. 260 1.1 christos */ 261 1.1 christos q->qstate = KQFILLING; 262 1.1 christos pthread_mutex_unlock(&q->lock); 263 1.1 christos for (i = 0; i < KQLEN; i++) { 264 1.1 christos AES_encrypt(q->ctr, q->keys[i], &key); 265 1.1 christos ssh_ctr_inc(q->ctr, AES_BLOCK_SIZE); 266 1.1 christos } 267 1.1 christos 268 1.1 christos /* Re-lock, mark full and signal consumer */ 269 1.1 christos pthread_mutex_lock(&q->lock); 270 1.1 christos ssh_ctr_add(q->ctr, KQLEN * (NUMKQ - 1), AES_BLOCK_SIZE); 271 1.1 christos q->qstate = KQFULL; 272 1.1 christos STATS_FILL(stats); 273 1.1 christos pthread_cond_signal(&q->cond); 274 1.1 christos pthread_mutex_unlock(&q->lock); 275 1.1 christos } 276 1.1 christos 277 1.1 christos #ifdef CIPHER_THREAD_STATS 278 1.1 christos /* Stats */ 279 1.1 christos pthread_cleanup_pop(1); 280 1.1 christos #endif 281 1.1 christos 282 1.1 christos return NULL; 283 1.1 christos } 284 1.1 christos 285 1.1 christos static int 286 1.1 christos ssh_aes_ctr(EVP_CIPHER_CTX *ctx, u_char *dest, const u_char *src, 287 1.1 christos u_int len) 288 1.1 christos { 289 1.1 christos struct ssh_aes_ctr_ctx *c; 290 1.1 christos struct kq *q, *oldq; 291 1.1 christos int ridx; 292 1.1 christos u_char *buf; 293 1.1 christos 294 1.1 christos if (len == 0) 295 1.1 christos return (1); 296 1.1 christos if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) == NULL) 297 1.1 christos return (0); 298 1.1 christos 299 1.1 christos q = &c->q[c->qidx]; 300 1.1 christos ridx = c->ridx; 301 1.1 christos 302 1.1 christos /* src already padded to block multiple */ 303 1.1 christos while (len > 0) { 304 1.1 christos buf = q->keys[ridx]; 305 1.1 christos 306 1.1 christos #ifdef CIPHER_BYTE_XOR 307 1.1 christos dest[0] = src[0] ^ buf[0]; 308 1.1 christos dest[1] = src[1] ^ buf[1]; 309 1.1 christos dest[2] = src[2] ^ buf[2]; 310 1.1 christos dest[3] = src[3] ^ buf[3]; 311 1.1 christos dest[4] = src[4] ^ buf[4]; 312 1.1 christos dest[5] = src[5] ^ buf[5]; 313 1.1 christos dest[6] = src[6] ^ buf[6]; 314 1.1 christos dest[7] = src[7] ^ buf[7]; 315 1.1 christos dest[8] = src[8] ^ buf[8]; 316 1.1 christos dest[9] = src[9] ^ buf[9]; 317 1.1 christos dest[10] = src[10] ^ buf[10]; 318 1.1 christos dest[11] = src[11] ^ buf[11]; 319 1.1 christos dest[12] = src[12] ^ buf[12]; 320 1.1 christos dest[13] = src[13] ^ buf[13]; 321 1.1 christos dest[14] = src[14] ^ buf[14]; 322 1.1 christos dest[15] = src[15] ^ buf[15]; 323 1.1 christos #else 324 1.1 christos *(uint64_t *)dest = *(uint64_t *)src ^ *(uint64_t *)buf; 325 1.1 christos *(uint64_t *)(dest + 8) = *(uint64_t *)(src + 8) ^ 326 1.1 christos *(uint64_t *)(buf + 8); 327 1.1 christos #endif 328 1.1 christos 329 1.1 christos dest += 16; 330 1.1 christos src += 16; 331 1.1 christos len -= 16; 332 1.1 christos ssh_ctr_inc(ctx->iv, AES_BLOCK_SIZE); 333 1.1 christos 334 1.1 christos /* Increment read index, switch queues on rollover */ 335 1.1 christos if ((ridx = (ridx + 1) % KQLEN) == 0) { 336 1.1 christos oldq = q; 337 1.1 christos 338 1.1 christos /* Mark next queue draining, may need to wait */ 339 1.1 christos c->qidx = (c->qidx + 1) % NUMKQ; 340 1.1 christos q = &c->q[c->qidx]; 341 1.1 christos pthread_mutex_lock(&q->lock); 342 1.1 christos while (q->qstate != KQFULL) { 343 1.1 christos STATS_WAIT(c->stats); 344 1.1 christos pthread_cond_wait(&q->cond, &q->lock); 345 1.1 christos } 346 1.1 christos q->qstate = KQDRAINING; 347 1.1 christos pthread_mutex_unlock(&q->lock); 348 1.1 christos 349 1.1 christos /* Mark consumed queue empty and signal producers */ 350 1.1 christos pthread_mutex_lock(&oldq->lock); 351 1.1 christos oldq->qstate = KQEMPTY; 352 1.1 christos STATS_DRAIN(c->stats); 353 1.1 christos pthread_cond_broadcast(&oldq->cond); 354 1.1 christos pthread_mutex_unlock(&oldq->lock); 355 1.1 christos } 356 1.1 christos } 357 1.1 christos c->ridx = ridx; 358 1.1 christos return (1); 359 1.1 christos } 360 1.1 christos 361 1.1 christos #define HAVE_NONE 0 362 1.1 christos #define HAVE_KEY 1 363 1.1 christos #define HAVE_IV 2 364 1.1 christos static int 365 1.1 christos ssh_aes_ctr_init(EVP_CIPHER_CTX *ctx, const u_char *key, const u_char *iv, 366 1.1 christos int enc) 367 1.1 christos { 368 1.1 christos struct ssh_aes_ctr_ctx *c; 369 1.1 christos int i; 370 1.1 christos 371 1.1 christos if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) == NULL) { 372 1.1 christos c = xmalloc(sizeof(*c)); 373 1.1 christos 374 1.1 christos c->state = HAVE_NONE; 375 1.1 christos for (i = 0; i < NUMKQ; i++) { 376 1.1 christos pthread_mutex_init(&c->q[i].lock, NULL); 377 1.1 christos pthread_cond_init(&c->q[i].cond, NULL); 378 1.1 christos } 379 1.1 christos 380 1.1 christos STATS_INIT(c->stats); 381 1.1 christos 382 1.1 christos EVP_CIPHER_CTX_set_app_data(ctx, c); 383 1.1 christos } 384 1.1 christos 385 1.1 christos if (c->state == (HAVE_KEY | HAVE_IV)) { 386 1.1 christos /* Cancel pregen threads */ 387 1.1 christos for (i = 0; i < CIPHER_THREADS; i++) 388 1.1 christos pthread_cancel(c->tid[i]); 389 1.1 christos for (i = 0; i < CIPHER_THREADS; i++) 390 1.1 christos pthread_join(c->tid[i], NULL); 391 1.1 christos /* Start over getting key & iv */ 392 1.1 christos c->state = HAVE_NONE; 393 1.1 christos } 394 1.1 christos 395 1.1 christos if (key != NULL) { 396 1.1 christos AES_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8, 397 1.1 christos &c->aes_ctx); 398 1.1 christos c->state |= HAVE_KEY; 399 1.1 christos } 400 1.1 christos 401 1.1 christos if (iv != NULL) { 402 1.1 christos memcpy(ctx->iv, iv, AES_BLOCK_SIZE); 403 1.1 christos c->state |= HAVE_IV; 404 1.1 christos } 405 1.1 christos 406 1.1 christos if (c->state == (HAVE_KEY | HAVE_IV)) { 407 1.1 christos /* Clear queues */ 408 1.1 christos memcpy(c->q[0].ctr, ctx->iv, AES_BLOCK_SIZE); 409 1.1 christos c->q[0].qstate = KQINIT; 410 1.1 christos for (i = 1; i < NUMKQ; i++) { 411 1.1 christos memcpy(c->q[i].ctr, ctx->iv, AES_BLOCK_SIZE); 412 1.1 christos ssh_ctr_add(c->q[i].ctr, i * KQLEN, AES_BLOCK_SIZE); 413 1.1 christos c->q[i].qstate = KQEMPTY; 414 1.1 christos } 415 1.1 christos c->qidx = 0; 416 1.1 christos c->ridx = 0; 417 1.1 christos 418 1.1 christos /* Start threads */ 419 1.1 christos for (i = 0; i < CIPHER_THREADS; i++) { 420 1.1 christos pthread_create(&c->tid[i], NULL, thread_loop, c); 421 1.1 christos } 422 1.1 christos pthread_mutex_lock(&c->q[0].lock); 423 1.1 christos while (c->q[0].qstate != KQDRAINING) 424 1.1 christos pthread_cond_wait(&c->q[0].cond, &c->q[0].lock); 425 1.1 christos pthread_mutex_unlock(&c->q[0].lock); 426 1.1 christos 427 1.1 christos } 428 1.1 christos return (1); 429 1.1 christos } 430 1.1 christos 431 1.1 christos static int 432 1.1 christos ssh_aes_ctr_cleanup(EVP_CIPHER_CTX *ctx) 433 1.1 christos { 434 1.1 christos struct ssh_aes_ctr_ctx *c; 435 1.1 christos int i; 436 1.1 christos 437 1.1 christos if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) != NULL) { 438 1.1 christos #ifdef CIPHER_THREAD_STATS 439 1.1 christos debug("main thread: %u drains, %u waits", c->stats.drains, 440 1.1 christos c->stats.waits); 441 1.1 christos #endif 442 1.1 christos /* Cancel pregen threads */ 443 1.1 christos for (i = 0; i < CIPHER_THREADS; i++) 444 1.1 christos pthread_cancel(c->tid[i]); 445 1.1 christos for (i = 0; i < CIPHER_THREADS; i++) 446 1.1 christos pthread_join(c->tid[i], NULL); 447 1.1 christos 448 1.1 christos memset(c, 0, sizeof(*c)); 449 1.4 christos free(c); 450 1.1 christos EVP_CIPHER_CTX_set_app_data(ctx, NULL); 451 1.1 christos } 452 1.1 christos return (1); 453 1.1 christos } 454 1.1 christos 455 1.1 christos /* <friedl> */ 456 1.1 christos const EVP_CIPHER * 457 1.1 christos evp_aes_ctr_mt(void) 458 1.1 christos { 459 1.1 christos static EVP_CIPHER aes_ctr; 460 1.1 christos 461 1.1 christos memset(&aes_ctr, 0, sizeof(EVP_CIPHER)); 462 1.1 christos aes_ctr.nid = NID_undef; 463 1.1 christos aes_ctr.block_size = AES_BLOCK_SIZE; 464 1.1 christos aes_ctr.iv_len = AES_BLOCK_SIZE; 465 1.1 christos aes_ctr.key_len = 16; 466 1.1 christos aes_ctr.init = ssh_aes_ctr_init; 467 1.1 christos aes_ctr.cleanup = ssh_aes_ctr_cleanup; 468 1.1 christos aes_ctr.do_cipher = ssh_aes_ctr; 469 1.1 christos #ifndef SSH_OLD_EVP 470 1.1 christos aes_ctr.flags = EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH | 471 1.1 christos EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CUSTOM_IV; 472 1.1 christos #endif 473 1.1 christos return (&aes_ctr); 474 1.1 christos } 475 1.2 adam #endif /* AES_CTR_MT */ 476