pike.git / src / post_modules / Nettle / aead.cmod

version» Context lines:

pike.git/src/post_modules/Nettle/aead.cmod:1: + /* -*- c -*- + || This file is part of Pike. For copyright information see COPYRIGHT. + || Pike is distributed under GPL, LGPL and MPL. See the file COPYING + || for more information. + */    -  + #include "module.h" + #include "interpret.h" + #include "operators.h" + #include "threads.h" + #include "pike_compiler.h" + #include "module_support.h" +  + #include "nettle_config.h" +  + #ifdef HAVE_LIBNETTLE +  + DECLARATIONS +  + #include "nettle.h" +  + #ifdef HAVE_NETTLE_CHACHA_POLY1305_H + #include <nettle/chacha-poly1305.h> + #endif +  + /*! @module Nettle +  */ +  + /* Calls Pike_error on errors */ + typedef void (*pike_nettle_set_key_func)(void *ctx, +  ptrdiff_t length, +  const uint8_t *key); +  + struct pike_aead + { +  const char *name; +  +  size_t context_size; +  +  unsigned digest_size; +  +  unsigned block_size; +  +  /* Suggested key size; other sizes are sometimes possible. */ +  unsigned key_size; +  +  unsigned iv_size; +  +  pike_nettle_set_key_func set_encrypt_key; +  pike_nettle_set_key_func set_decrypt_key; +  +  /* NB: Use pike_nettle_hash_update_func here to get both a length field, +  * and a const uint8_t source. +  */ +  pike_nettle_hash_update_func set_iv; +  +  pike_nettle_crypt_func encrypt; +  pike_nettle_crypt_func decrypt; +  +  pike_nettle_hash_update_func update; +  pike_nettle_hash_digest_func digest; + }; +  + #define _PIKE_AEAD(name, NAME) { \ +  #name, \ +  sizeof(struct name##_ctx), \ +  NAME##_DIGEST_SIZE, \ +  NAME##_BLOCK_SIZE, \ +  NAME##_KEY_SIZE, \ +  NAME##_NONCE_SIZE, \ +  pike_##name##_set_encrypt_key, \ +  pike_##name##_set_decrypt_key, \ +  pike_##name##_set_iv, \ +  (pike_nettle_crypt_func) name##_encrypt, \ +  (pike_nettle_crypt_func) name##_decrypt, \ +  (pike_nettle_hash_update_func) name##_update, \ +  (pike_nettle_hash_digest_func) name##_digest, \ + } +  + /*! @class AEAD +  *! +  *! Represents information about an Authenticated Encryption with +  *! Associated Data (AEAD) algorithm, such as name, key size, digest size, +  *! and block size. +  */ + PIKECLASS AEAD + { +  /*! @decl inherit __builtin.Nettle.AEAD +  */ +  INHERIT "__builtin.Nettle.AEAD"; +  +  CVAR const struct pike_aead *meta; +  +  /*! @decl string(0..255) name() +  *! +  *! @returns +  *! A human readable name for the algorithm. +  */ +  PIKEFUN string(0..255) name() +  optflags OPT_TRY_OPTIMIZE; +  { +  if (!THIS->meta) +  Pike_error("Cipher not properly initialized.\n"); +  +  push_text(THIS->meta->name); +  } +  +  /*! @decl int(0..) digest_size(void) +  *! +  *! Returns the size of a MAC digest. +  */ +  PIKEFUN int(0..) digest_size() +  optflags OPT_TRY_OPTIMIZE; +  { +  if (!THIS->meta) +  Pike_error("Cipher not properly initialized.\n"); +  +  push_int(THIS->meta->digest_size); +  } +  +  /*! @decl int(0..) key_size() +  *! +  *! @returns +  *! The recommended key size for the cipher. +  */ +  PIKEFUN int(0..) key_size() +  optflags OPT_TRY_OPTIMIZE; +  { +  if (!THIS->meta) +  Pike_error("Cipher not properly initialized.\n"); +  +  push_int(THIS->meta->key_size); +  } +  +  /*! @decl int(0..) block_size() +  *! +  *! @returns +  *! The block size of the AEAD algorithm. +  *! +  *! @note +  *! Note that AEAD algorithms often support automatic padding, +  *! so that the last block does not need to be complete. +  */ +  PIKEFUN int(0..) block_size() +  optflags OPT_TRY_OPTIMIZE; +  { +  if (!THIS->meta) +  Pike_error("Cipher not properly initialized.\n"); +  +  push_int(THIS->meta->block_size); +  } +  +  /*! @decl int(0..) iv_size(void) +  *! +  *! Returns the size of the iv/nonce of the AEAD algorithm (if any). +  *! +  *! Returns @expr{0@} (zero) if there is no configurable iv/nonce. +  */ +  PIKEFUN int(0..) iv_size() +  optflags OPT_TRY_OPTIMIZE; +  { +  if (!THIS->meta) +  Pike_error("AEAD not properly initialized.\n"); +  +  push_int(THIS->meta->iv_size); +  } +  + #ifdef PIKE_NULL_IS_SPECIAL +  INIT +  { +  THIS->meta = NULL; +  } + #endif +  +  /*! @class State +  *! +  *! Base class for AEAD contexts. +  */ +  PIKECLASS State +  program_flags PROGRAM_USES_PARENT|PROGRAM_NEEDS_PARENT|PROGRAM_CLEAR_STORAGE; +  { +  DOCSTART() @decl inherit AEAD::State +  DOCEND() +  +  EXTRA +  { +  /* Perform an inherit of the State class (if any) that our parent +  * may contain via its inherit of __builtin.Nettle.AEAD. +  */ +  lexical_inherit(1, MK_STRING("State"), 0, REPORT_WARNING); +  } +  +  CVAR pike_nettle_crypt_func crypt; +  CVAR void *ctx; +  CVAR int key_size; +  + #define GET_META() (((struct Nettle_AEAD_struct *)parent_storage(1, Nettle_AEAD_program))->meta) +  +  /*! @decl State set_encrypt_key(string(0..255) key) +  *! +  *! Initializes the object for encryption. The @[key] memory will be +  *! cleared before released. +  *! +  *! @seealso +  *! @[set_decrypt_key], @[crypt] +  */ +  PIKEFUN object set_encrypt_key(string(0..255) key) +  optflags OPT_SIDE_EFFECT; +  rawtype tFunc(tStr8 tOr(tInt, tVoid), tObjImpl_NETTLE_CIPHER_STATE); +  { +  const struct pike_aead *meta = GET_META(); +  +  if (!THIS->ctx || !meta) +  Pike_error("CipherState not properly initialized.\n"); +  +  NO_WIDE_STRING(key); +  key->flags |= STRING_CLEAR_ON_EXIT; +  meta->set_encrypt_key(THIS->ctx, key->len, (const uint8_t*)key->str); +  +  THIS->crypt = meta->encrypt; +  THIS->key_size = key->len; +  +  RETURN this_object(); +  } +  +  /*! @decl State set_decrypt_key(string(0..255) key) +  *! +  *! Initializes the object for decryption. The @[key] memory will be +  *! cleared before released. +  *! +  *! @seealso +  *! @[set_encrypt_key], @[crypt] +  */ +  PIKEFUN object set_decrypt_key(string(0..255) key) +  optflags OPT_SIDE_EFFECT; +  rawtype tFunc(tStr8 tOr(tInt, tVoid), tObjImpl_NETTLE_CIPHER_STATE); +  { +  const struct pike_aead *meta = GET_META(); +  +  if (!THIS->ctx || !meta) +  Pike_error("CipherState not properly initialized.\n"); +  +  NO_WIDE_STRING(key); +  key->flags |= STRING_CLEAR_ON_EXIT; +  meta->set_decrypt_key(THIS->ctx, key->len, (const uint8_t*)key->str); +  THIS->crypt = meta->decrypt; +  THIS->key_size = key->len; +  +  RETURN this_object(); +  } +  +  /*! @decl string(0..255) make_key() +  *! +  *! Generate a key by calling @[random_string] and initialize this +  *! object for encryption with that key. +  *! +  *! @returns +  *! The generated key. The key memory will be cleared before +  *! released. +  *! +  *! @seealso +  *! @[set_encrypt_key] +  */ +  PIKEFUN string(0..255) make_key() +  optflags OPT_EXTERNAL_DEPEND; +  { +  const struct pike_aead *meta = GET_META(); +  +  push_random_string(meta->key_size); +  +  stack_dup(); +  Pike_sp[-1].u.string->flags |= STRING_CLEAR_ON_EXIT; +  apply_current(f_Nettle_AEAD_State_set_encrypt_key_fun_num, 1); +  pop_stack(); +  } +  +  /*! @decl string(0..255) crypt(string(0..255) data) +  *! +  *! Encrypts or decrypts data, using the current key. Neither the +  *! input nor output data is automatically memory scrubbed, +  *! unless @[String.secure] has been called on them. +  *! +  *! @param data +  *! Data must be an integral number of blocks, except for the +  *! last segment. +  *! +  *! @returns +  *! The encrypted or decrypted data. +  */ +  PIKEFUN string(0..255) crypt (string(0..255) data) +  optflags OPT_EXTERNAL_DEPEND | OPT_SIDE_EFFECT; +  { +  const struct pike_aead *meta = GET_META(); +  struct pike_string *result; +  pike_nettle_crypt_func crypt = THIS->crypt; +  void *ctx = THIS->ctx; +  +  if (!THIS->ctx || !THIS->crypt || !meta) +  Pike_error("CipherState not properly initialized.\n"); +  +  NO_WIDE_STRING(data); +  +  result = begin_shared_string(data->len); +  if (data->len >= CIPHER_THREADS_ALLOW_THRESHOLD) { +  THREADS_ALLOW(); +  crypt(ctx, data->len, STR0(result), STR0(data)); +  THREADS_DISALLOW(); +  } else { +  crypt(ctx, data->len, STR0(result), STR0(data)); +  } +  result = end_shared_string(result); +  push_string(result); +  } +  +  /*! @decl string(0..255) name(void) +  *! +  *! @returns +  *! A human readable name for the algorithm. +  *! +  *! @note +  *! The default implementation just calls @[Cipher::name()] +  *! in the parent. +  */ +  PIKEFUN string(0..255) name() +  optflags OPT_TRY_OPTIMIZE; +  { +  apply_external(1, f_Nettle_AEAD_name_fun_num, args); +  } +  +  /*! @decl int(0..) digest_size(void) +  *! +  *! Returns the size of a MAC digest. +  */ +  PIKEFUN int(0..) digest_size() +  optflags OPT_TRY_OPTIMIZE; +  { +  apply_external(1, f_Nettle_AEAD_digest_size_fun_num, args); +  } +  +  /*! @decl int(0..) key_size(void) +  *! +  *! @returns +  *! The actual key size for this cipher. +  */ +  PIKEFUN int(0..) key_size() +  optflags OPT_TRY_OPTIMIZE; +  { +  RETURN THIS->key_size; +  } +  +  /*! @decl int(0..) block_size(void) +  *! +  *! @returns +  *! The block size for this cipher. +  *! +  *! @note +  *! The default implementation just calls @[Cipher::block_size()] +  *! in the parent. +  */ +  PIKEFUN int(0..) block_size() +  optflags OPT_TRY_OPTIMIZE; +  { +  apply_external(1, f_Nettle_AEAD_block_size_fun_num, args); +  } +  +  /*! @decl int(0..) iv_size(void) +  *! +  *! Returns the size of the iv/nonce of the AEAD algorithm (if any). +  *! +  *! Returns @expr{0@} (zero) if there is no configurable iv/nonce. +  */ +  PIKEFUN int(0..) iv_size() +  optflags OPT_TRY_OPTIMIZE; +  { +  apply_external(1, f_Nettle_AEAD_iv_size_fun_num, args); +  } +  +  /*! @decl State set_iv(string(0..255) iv) +  *! +  *! Set the iv/nonce (if supported) for the AEAD. +  *! +  *! @returns +  *! Returns @expr{this@} in order to simplify chaining +  *! of function calls. +  */ +  PIKEFUN object set_iv(string(0..255) iv) +  optflags OPT_SIDE_EFFECT; +  rawtype tFunc(tStr8, tObjImpl_NETTLE_MAC_STATE); +  { +  void *ctx = THIS->ctx; +  const struct pike_aead *meta = GET_META(); +  +  if (!ctx || !meta) +  Pike_error("State not properly initialized.\n"); +  +  iv->flags |= STRING_CLEAR_ON_EXIT; +  NO_WIDE_STRING(iv); +  +  /* NB: Check iv length here so that we can use the +  * Nettle implementation straight in meta->set_iv. +  */ +  if ((unsigned)iv->len != meta->iv_size || !meta->iv_size) { +  Pike_error("Invalid iv/nonce.\n"); +  } +  +  meta->set_iv(ctx, iv->len, STR0(iv)); +  +  push_object(this_object()); +  } +  +  /*! @decl State update(string(0..255) data) +  *! +  *! Add some more associated data. +  *! +  *! All associated data typically needs to be added before +  *! any data to actually encrypt. +  *! +  *! @returns +  *! Returns @expr{this@} in order to simplify chaining +  *! of function calls. +  */ +  PIKEFUN object update(string(0..255) data) +  optflags OPT_SIDE_EFFECT; +  rawtype tFunc(tStr8, tObjImpl_NETTLE_MAC_STATE); +  { +  void *ctx = THIS->ctx; +  const struct pike_aead *meta = GET_META(); +  +  if (!ctx || !meta) +  Pike_error("State not properly initialized.\n"); +  +  NO_WIDE_STRING(data); +  +  /* Only thread this block for significant data size */ +  if (data->len > HASH_THREADS_ALLOW_THRESHOLD) { +  THREADS_ALLOW(); +  meta->update(ctx, data->len, (const uint8_t *)data->str); +  THREADS_DISALLOW(); +  } else { +  meta->update(ctx, data->len, (const uint8_t *)data->str); +  } +  +  push_object(this_object()); +  } +  +  /*! @decl string(0..255) digest(int|void length) +  *! +  *! Generates a digest, and resets the AEAD contents. +  *! +  *! Also updates the iv/nonce (if any). +  *! +  *! @param length +  *! If the length argument is provided, the digest is truncated +  *! to the given length. +  *! +  *! @returns +  *! The digest. +  */ +  PIKEFUN string(0..255) digest(int|void arg) +  { +  const struct pike_aead *meta; +  struct pike_string *digest; +  unsigned length; +  +  meta = GET_META(); +  +  if (!THIS->ctx || !meta) +  Pike_error("State not properly initialized.\n"); +  +  if (!arg) +  length = meta->digest_size; +  else +  { +  if (TYPEOF(*arg) != PIKE_T_INT) +  Pike_error("Bad argument type.\n"); +  if (arg->u.integer < 0) +  Pike_error("Invalid length, must be positive.\n"); +  if ((unsigned)arg->u.integer > meta->digest_size) +  Pike_error("Unsupported digest length.\n"); +  +  length = arg->u.integer; +  } +  +  digest = begin_shared_string(length); +  meta->digest(THIS->ctx, length, (uint8_t *)digest->str); +  push_string(end_shared_string(digest)); +  } +  + #ifdef PIKE_NULL_IS_SPECIAL +  INIT +  { +  THIS->ctx = NULL; +  THIS->crypt = NULL; +  THIS->key_size = 0; +  } + #endif +  +  EXIT +  { +  if (THIS->ctx && Pike_fp->current_object->prog) +  { +  const struct pike_aead *meta = GET_META(); +  assert(meta); +  memset(THIS->ctx, 0, meta->context_size); +  } +  } +  } +  /*! @endclass State */ + } + /*! @endclass AEAD +  */ +  + /* FIXME: Consider adding special cases for gcm-aes and gcm-camellia. */ +  + #ifdef HAVE_NETTLE_CHACHA_POLY1305_H +  + /* NB: Nettle 3.0 had an implementation of the draft AEAD construct, +  * which is incompatible with the RFC 7539 definition. +  * +  * In the old implementation the length of the nonce was 8 bytes. +  */ + #if CHACHA_POLY1305_NONCE_SIZE == 12 +  + static void pike_chacha_poly1305_set_encrypt_key(void *ctx, +  ptrdiff_t length, +  const uint8_t *key) + { +  if (length != CHACHA_POLY1305_KEY_SIZE) { +  Pike_error("Bad key size.\n"); +  } +  chacha_poly1305_set_key(ctx, key); + } +  + static void pike_chacha_poly1305_set_decrypt_key(void *ctx, +  ptrdiff_t length, +  const uint8_t *key) + { +  if (length != CHACHA_POLY1305_KEY_SIZE) { +  Pike_error("Bad key size.\n"); +  } +  chacha_poly1305_set_key(ctx, key); + } +  + static void pike_chacha_poly1305_set_iv(void *ctx, +  pike_nettle_size_t length, +  const uint8_t *key) + { +  if (length != CHACHA_POLY1305_NONCE_SIZE) { +  Pike_error("Bad nonce size.\n"); +  } +  chacha_poly1305_set_nonce(ctx, key); + } +  + #cmod_define PIKE_NAME CHACHA_POLY1305 + #cmod_define NETTLE_NAME chacha_poly1305 + #cmod_include "aead.H" + #cmod_undef PIKE_NAME + #cmod_undef NETTLE_NAME +  + #endif /* CHACHA_POLY1305_NONCE_SIZE == 12 */ +  + #endif /* HAVE_NETTLE_CHACHA_POLY1305_H */ +  + /*! @endmodule Nettle */ +  + void + aead_init(void) + { +  INIT; + } +  + void + aead_exit(void) + { +  EXIT; + } +  + #endif /* HAVE_LIBNETTLE */   Newline at end of file added.