diff --git a/lib/modules/Crypto.pmod/GCM.pmod b/lib/modules/Crypto.pmod/GCM.pmod deleted file mode 100644 index c8cf7f23677f1f57a8a30b9ffe999d3919494a5b..0000000000000000000000000000000000000000 --- a/lib/modules/Crypto.pmod/GCM.pmod +++ /dev/null @@ -1,24 +0,0 @@ -#pike __REAL_VERSION__ -#pragma strict_types -#require constant(Nettle.GCM) - -//! Implementation of the Galois Counter Mode (GCM). Works as -//! a wrapper for the cipher algorithm put in create. -//! -//! This is a so-called authenticated encryption with associated data -//! (AEAD) algorithm, and in addition to encryption also provides -//! message digests. -//! -//! The operation of GCM is specified in -//! NIST Special Publication 800-38D. -//! -//! @note -//! Wrapped ciphers MUST have a block size of @expr{16@}. -//! -//! @note -//! Note that this class is not available in all versions of Nettle. -//! -//! @seealso -//! @[CBC] - -inherit Nettle.GCM; diff --git a/lib/modules/SSL.pmod/Cipher.pmod b/lib/modules/SSL.pmod/Cipher.pmod index ca3b785a561ec80e6ef3a06e9f4a60bb724f429a..92b63feccd6ee34cf77f90949bae0e308ffa48ef 100644 --- a/lib/modules/SSL.pmod/Cipher.pmod +++ b/lib/modules/SSL.pmod/Cipher.pmod @@ -1335,24 +1335,6 @@ class Camellia } #endif -#if constant(Crypto.GCM) -//! -class AES_GCM -{ - inherit Crypto.GCM.State; - protected void create() { ::create(Crypto.AES()); } -} - -#if constant(Crypto.Camellia) -//! -class Camellia_GCM -{ - inherit Crypto.GCM.State; - protected void create() { ::create(Crypto.Camellia()); } -} -#endif -#endif - //! Signing using RSA. ADT.struct rsa_sign(object session, string cookie, ADT.struct struct) { @@ -1825,13 +1807,13 @@ array lookup(int suite, ProtocolVersion|int version, res->mac_algorithm = 0; // MACs are not used with AEAD. break; -#if constant(Crypto.GCM) +#if constant(Crypto.AES.GCM) case MODE_gcm: if (res->bulk_cipher_algorithm == AES) { - res->bulk_cipher_algorithm = AES_GCM; -#if constant(Crypto.Camellia) + res->bulk_cipher_algorithm = Crypto.AES.GCM.State; +#if constant(Crypto.Camellia.GCM) } else if (res->bulk_cipher_algorithm == Camellia) { - res->bulk_cipher_algorithm = Camellia_GCM; + res->bulk_cipher_algorithm = Crypto.Camellia.GCM.State; #endif } else { // Unsupported. diff --git a/lib/modules/SSL.pmod/Constants.pmod b/lib/modules/SSL.pmod/Constants.pmod index 3e138fb76ecfb1b8d765851e56c1bb70f89b63db..1556f407eeac5c01c9d4b69e603bd333b9f82488 100644 --- a/lib/modules/SSL.pmod/Constants.pmod +++ b/lib/modules/SSL.pmod/Constants.pmod @@ -902,7 +902,7 @@ constant CIPHER_SUITES = TLS_ecdhe_rsa_with_camellia_256_cbc_sha384: ({ KE_ecdhe_rsa, CIPHER_camellia256, HASH_sha384 }), #endif /* Crypto.Camellia */ -#if constant(Crypto.GCM) +#if constant(Crypto.AES.GCM) // GCM Suites: TLS_rsa_with_aes_128_gcm_sha256: ({ KE_rsa, CIPHER_aes, HASH_sha256, MODE_gcm }), TLS_dhe_rsa_with_aes_128_gcm_sha256: ({ KE_dhe_rsa, CIPHER_aes, HASH_sha256, MODE_gcm }), @@ -955,7 +955,7 @@ constant CIPHER_SUITES = TLS_ecdh_rsa_with_camellia_128_gcm_sha256: ({ KE_ecdh_rsa, CIPHER_camellia128, HASH_sha256, MODE_gcm }), TLS_ecdh_rsa_with_camellia_256_gcm_sha384: ({ KE_ecdh_rsa, CIPHER_camellia256, HASH_sha384, MODE_gcm }), #endif /* Crypto.Camellia */ -#endif /* Crypto.GCM */ +#endif /* Crypto.AES.GCM */ ]); constant HANDSHAKE_hello_request = 0; // RFC 5246 diff --git a/lib/modules/__builtin.pmod/Nettle.pmod/AEAD.pike b/lib/modules/__builtin.pmod/Nettle.pmod/AEAD.pike index f90de461ac2660be8d40aeebf709e7d9e3807a89..50fb03fdf837c07f935a2d99f8f58504387c7d1b 100644 --- a/lib/modules/__builtin.pmod/Nettle.pmod/AEAD.pike +++ b/lib/modules/__builtin.pmod/Nettle.pmod/AEAD.pike @@ -24,17 +24,14 @@ class State inherit Cipher::State; inherit __Hash::State; - protected void create(__builtin.Nettle.Cipher| - program(__builtin.Nettle.Cipher)|function c, - mixed ... args) + protected void create() { /* Needed to block the default implementation in __Hash.State. */ } } //! Calling `() will return a @[State] object. -State `()(__builtin.Nettle.Cipher|program(__builtin.Nettle.Cipher)|function c, - mixed ... args) { - return State(c, @args); +State `()() { + return State(); } diff --git a/src/post_modules/Nettle/Makefile.in b/src/post_modules/Nettle/Makefile.in index 3cadf2ff6500279b3f7d2f4d348fffa9051920b8..79d57554d94f87fba9bb08777bf6ee951f6eb788 100644 --- a/src/post_modules/Nettle/Makefile.in +++ b/src/post_modules/Nettle/Makefile.in @@ -17,7 +17,7 @@ mac.o: $(SRCDIR)/mac.c nt.o: $(SRCDIR)/nt.c $(SRCDIR)/aead.c: aead.H -$(SRCDIR)/cipher.c: cipher.H +$(SRCDIR)/cipher.c: cipher.H cipher16.H $(SRCDIR)/hash.c: hash.H $(SRCDIR)/mac.c: mac.H diff --git a/src/post_modules/Nettle/cipher.cmod b/src/post_modules/Nettle/cipher.cmod index ca14e4397ae1c0bc1154abdcebd78cd90d40ba8a..f4107a07a5fd5b631b7e1a5f0b8497904d4a574a 100644 --- a/src/post_modules/Nettle/cipher.cmod +++ b/src/post_modules/Nettle/cipher.cmod @@ -71,6 +71,25 @@ werror(const char *format, ...) /*! @module Nettle */ +/* Generic callback function for Nettle in case the crypt() + * function in the object isn't a Nettle function. + */ +static void pike_crypt_func(void *object, pike_nettle_size_t length, + uint8_t *dst, const uint8_t *src) +{ + int args; + struct pike_string *str; + push_string(make_shared_binary_string((const char *)src, length)); + args = safe_apply(object, "crypt", 1); + get_all_args("crypt", args, "%n", &str); + if (str->len != (ptrdiff_t)length) { + Pike_error("Bad string length %ld returned from crypt()\n", + DO_NOT_WARN((long)str->len)); + } + MEMCPY(dst, str->str, length); + pop_n_elems(args); +} + /* Calls Pike_error on errors */ typedef void (*pike_nettle_set_key_func)(void *ctx, ptrdiff_t length, const char *key, @@ -405,11 +424,519 @@ PIKECLASS Cipher } } /*! @endclass State */ - } + /*! @endclass Cipher */ +/*! @class Cipher16 + *! + *! This is the @[Cipher] class extended with algorithms + *! that require a block size of @expr{16@} bytes. + *! + *! @seealso + *! @[Cipher], @[GCM] + */ +PIKECLASS Cipher16 +{ + /* NOTE: MUST be first in the class to simplify access to symbols + * in Cipher! + */ + /*! @inherit Cipher + */ + INHERIT Nettle_Cipher; + +#ifdef HAVE_NETTLE_GCM_H +#include <nettle/gcm.h> + + /*! @module GCM + *! Implementation of the Galois Counter Mode (GCM). + *! + *! Works as a wrapper for the cipher implemented by overloading + *! the parent class (@[Cipher16]). + *! + *! This is a so-called authenticated encryption with associated data + *! (AEAD) algorithm, which in addition to encryption also provides + *! message digests. + *! + *! The operation of GCM is specified in + *! NIST Special Publication 800-38D. + *! + *! Typically accessed as @expr{Crypto.AES.GCM@} or + *! @expr{Crypto.Camellia.GCM@} + *! + *! @note + *! Note that this module is not available in all versions of Nettle. + *! + *! @seealso + *! @[CBC] + */ + + PIKEVAR object(Nettle_Cipher) GCM; + + PIKECLASS _GCM + program_flags PROGRAM_NEEDS_PARENT|PROGRAM_USES_PARENT; + flags ID_PROTECTED; + { + /*! @decl inherit __builtin.Nettle.AEAD + */ + INHERIT "__builtin.Nettle.AEAD"; + + /*! @decl string(0..255) name() + *! Returns the name of the base cipher with @expr{".GCM"@} appended. + */ + PIKEFUN string(0..255) name() + optflags OPT_TRY_OPTIMIZE; + { + apply_external(1, f_Nettle_Cipher_name_fun_num, args); + push_constant_text(".GCM"); + } + + /*! @decl int(16..16) block_size() + *! Returns the block size of the encapsulated cipher, + *! which is always @expr{16@} for GCM. + */ + PIKEFUN int(16..16) block_size() + optflags OPT_TRY_OPTIMIZE; + { + RETURN GCM_BLOCK_SIZE; + } + + /*! @decl int(16..16) digest_size() + *! Returns the size of the generated digest, + *! which is always @expr{16@} for GCM. + */ + PIKEFUN int(16..16) digest_size() + optflags OPT_TRY_OPTIMIZE; + { + RETURN GCM_BLOCK_SIZE; + } + + /*! @decl int(12..12) iv_size() + *! Returns the recommended size for the initialization vector + *! (ie @expr{12@}). + *! + *! Other sizes are allowed, but will be compressed or expanded + *! to this size using the encapsulated cipher. + */ + PIKEFUN int(12..12) iv_size() + optflags OPT_TRY_OPTIMIZE; + { + RETURN GCM_IV_SIZE; + } + + /*! @class State + *! + *! The state for a GCM instance. + */ + PIKECLASS State + program_flags PROGRAM_USES_PARENT|PROGRAM_NEEDS_PARENT; + { + 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. + */ + struct program *parent_prog = Pike_compiler->previous->new_program; + struct object *parent_obj = Pike_compiler->previous->fake_object; + int parent_State_fun_num = + really_low_find_shared_string_identifier(MK_STRING("State"), + parent_prog, + SEE_PROTECTED|SEE_PRIVATE); + if (parent_State_fun_num >= 0) { + struct program *parent_State_prog = + low_program_from_function(parent_obj, parent_State_fun_num); + if (parent_State_prog) { + low_inherit(parent_State_prog, 0, + parent_State_fun_num + + parent_prog->inherits[1].identifier_level, + 1 + 42, 0, NULL); + } + } + } + + PIKEVAR object object + flags ID_PRIVATE|ID_PROTECTED|ID_HIDDEN; + CVAR struct Nettle_Cipher_State_struct *crypt_state; + CVAR INT32 mode; + CVAR INT32 dmode; + CVAR struct gcm_key gcm_key; + CVAR struct gcm_ctx gcm_ctx; + + /* dmode flags */ +#define NO_ADATA 1 /* Disallow associated data. */ +#define NO_CDATA 2 /* Disallow crypted data. */ + + INIT + { + THIS->mode = -1; + } + + EXIT + gc_trivial; + { + if (THIS->object) { + free_object(THIS->object); + THIS->object = NULL; + } + } + + /*! @decl void create() + *! + *! Initialize the GCM state. + */ + PIKEFUN void create() + flags ID_PROTECTED; + { + struct object *o; + struct inherit *inh; + int f; + + apply_external(2, Nettle_Cipher_State_program_fun_num, 0); + if (TYPEOF(Pike_sp[-1]) != T_OBJECT) { + Pike_error("Unsupported return value from Cipher::State().\n"); + } + o = Pike_sp[-1].u.object; + if (!o->prog) { + Pike_error("Cipher::State() returned destructed object.\n"); + } + + f = find_identifier("crypt", o->prog); + if (f < 0) { + Pike_error("State object has no crypt() function.\n"); + } + + safe_apply(o, "block_size", 0); + + if (TYPEOF(Pike_sp[-1]) != T_INT) + Pike_error("block_size() didn't return an int.\n"); + + if (Pike_sp[-1].u.integer != GCM_BLOCK_SIZE) + Pike_error("cipher has an invalid block size for GCM.\n"); + + if (THIS->object) free_object(THIS->object); + add_ref(THIS->object = o); + + inh = INHERIT_FROM_INT(o->prog, f); + if (inh->prog == Nettle_Cipher_State_program) { + /* crypt() is from Nettle.Cipher.State. + */ + THIS->crypt_state = (struct Nettle_Cipher_State_struct *) + get_inherit_storage(o, inh - o->prog->inherits); + } + + pop_n_elems(2); + + THIS->mode = -1; + } + + /*! @decl string(0..255) name() + *! Returns the string @expr{"x.GCM"@} where x is the + *! encapsulated algorithm. + */ + PIKEFUN string(0..255) name() + optflags OPT_TRY_OPTIMIZE; + { + safe_apply(THIS->object, "name", 0); + push_constant_text(".GCM"); + f_add(2); + } + + /*! @decl int(16..16) block_size() + *! Returns the block size of the encapsulated cipher, + *! which is always @expr{16@} for GCM. + */ + PIKEFUN int(16..16) block_size() + optflags OPT_TRY_OPTIMIZE; + { + RETURN GCM_BLOCK_SIZE; + } + + /*! @decl int(16..16) digest_size() + *! Returns the size of the generated digest, + *! which is always @expr{16@} for GCM. + */ + PIKEFUN int(16..16) digest_size() + optflags OPT_TRY_OPTIMIZE; + { + RETURN GCM_BLOCK_SIZE; + } + + /*! @decl int(12..12) iv_size() + *! Returns the recommended size for the initialization vector + *! (ie @expr{12@}). + *! + *! Other sizes are allowed, but will be compressed or expanded + *! to this size using the encapsulated cipher. + */ + PIKEFUN int(12..12) iv_size() + optflags OPT_TRY_OPTIMIZE; + { + RETURN GCM_IV_SIZE; + } + + /*! @decl int(0..) key_size() + *! Returns the key size of the encapsulated cipher. + */ + PIKEFUN int(0..) key_size() + optflags OPT_EXTERNAL_DEPEND; + { + safe_apply(THIS->object, "key_size", args); + } + + /*! @decl this_program set_encrypt_key(string(0..255) key, int|void flags) + *! + *! Prepare the cipher and the wrapper for encrypting with the given + *! @[key]. The @[key] memory will be cleared before released. + *! + *! @note + *! Note that this operation does not by itself reset the + *! context sufficiently to start a new message; @[set_iv()] + *! needs to be called too. + *! + *! @seealso + *! @[set_decrypt_key()], @[set_iv()] + */ + PIKEFUN object set_encrypt_key(string(0..255) key, int|void flags) + optflags OPT_SIDE_EFFECT; + { + crypt_func func = pike_crypt_func; + void *ctx = THIS->object; + key->flags |= STRING_CLEAR_ON_EXIT; + safe_apply(THIS->object, "set_encrypt_key", args); + pop_stack(); + + if (THIS->crypt_state && THIS->crypt_state->crypt) { + func = THIS->crypt_state->crypt; + ctx = THIS->crypt_state->ctx; + } + gcm_set_key(&THIS->gcm_key, ctx, func); + THIS->mode = 0; + + push_object(this_object()); + } + + /*! @decl this_program set_decrypt_key(string(0..255) key, int|void flags) + *! + *! Prepare the cipher and the wrapper for decrypting with the given + *! @[key]. The @[key] memory will be cleared before released. + *! + *! @note + *! Note that this operation does not by itself reset the + *! context sufficiently to start a new message; @[set_iv()] + *! needs to be called too. + *! + *! @seealso + *! @[set_encrypt_key()], @[set_iv()] + */ + PIKEFUN object set_decrypt_key(string(0..255) key, int|void flags) + optflags OPT_SIDE_EFFECT; + { + crypt_func func = pike_crypt_func; + void *ctx = THIS->object; + key->flags |= STRING_CLEAR_ON_EXIT; + /* NOTE: GCM always uses the encryption function + * of the underlying cipher! + */ + safe_apply(THIS->object, "set_encrypt_key", args); + pop_stack(); + + if (THIS->crypt_state && THIS->crypt_state->crypt) { + func = THIS->crypt_state->crypt; + ctx = THIS->crypt_state->ctx; + } + gcm_set_key(&THIS->gcm_key, ctx, func); + THIS->mode = 1; + + push_object(this_object()); + } + + /*! @decl this_program set_iv(string(0..255) iv) + *! + *! Set the initialization vector to @[iv]. The @[iv] memory will be + *! cleared before released. + *! + *! Also resets all state needed to start a new message. + *! + *! @note + *! For @[iv]s of length other than @expr{12@}, an encryption or + *! decryption key must have been set first. + *! + *! @seealso + *! @[set_encrypt_key()], @[set_decrypt_key()]. + */ + PIKEFUN object set_iv(string(0..255) iv) + optflags OPT_SIDE_EFFECT; + { + iv->flags |= STRING_CLEAR_ON_EXIT; + NO_WIDE_STRING(iv); + if ((THIS->mode < 0) && (iv->len != GCM_IV_SIZE)) + Pike_error("The key must be set to use an iv of length other than %d.\n", + GCM_IV_SIZE); + gcm_set_iv(&THIS->gcm_ctx, &THIS->gcm_key, iv->len, STR0(iv)); + THIS->dmode = 0; + RETURN this_object(); + } + + /*! @decl void update(string(0..255) public_data) + *! + *! Add @[public_data] to be authenticated. + *! + *! The length of @[public_data] MUST be a multiple of the + *! block size (ie @expr{16@}) for all calls except the last. + *! + *! All calls of @[update()] need to be performed before + *! any calls of @[crypt()]. + */ + PIKEFUN void update(string(0..255) data) + { + NO_WIDE_STRING(data); + + if (!THIS->object || !THIS->object->prog) { + Pike_error("Lookup in destructed object.\n"); + } + + if (THIS->mode < 0) + Pike_error("Key schedule not initialized.\n"); + + if (THIS->dmode & NO_ADATA) + Pike_error("Public data not allowed now.\n"); + + gcm_update(&THIS->gcm_ctx, &THIS->gcm_key, data->len, STR0(data)); + + if (data->len & (GCM_BLOCK_SIZE - 1)) + THIS->dmode |= NO_ADATA; + + pop_n_elems(args); + } + + /*! @decl string(0..255) crypt(string(0..255) data) + *! + *! Encrypt/decrypt @[data] and return the result. @[data] must + *! be an integral number of blocks. + *! + *! The length of @[data] MUST be a multiple of the block size + *! (ie @expr{16@}) for all calls except the last. + *! + *! Neither the input or output data is not automatically memory + *! scrubbed, unless @[String.secure] has been called on the data. + *! + *! @seealso + *! @[update()], @[digest()] + */ + PIKEFUN string(0..255) crypt(string(0..255) data) + { + struct pike_string *result; + ONERROR uwp; + crypt_func func = pike_crypt_func; + void *ctx = THIS->object; + + NO_WIDE_STRING(data); + + if (!THIS->object || !THIS->object->prog) { + Pike_error("Lookup in destructed object.\n"); + } + + if (THIS->mode < 0) + Pike_error("Key schedule not initialized.\n"); + + if (THIS->dmode & NO_CDATA) + Pike_error("More data not allowed before the iv is reset.\n"); + + result = begin_shared_string(data->len); + SET_ONERROR (uwp, do_free_string, result); + + if (THIS->crypt_state && THIS->crypt_state->crypt) { + func = THIS->crypt_state->crypt; + ctx = THIS->crypt_state->ctx; + } + + if (!THIS->mode) { + gcm_encrypt(&THIS->gcm_ctx, &THIS->gcm_key, ctx, func, + data->len, STR0(result), STR0(data)); + } else { + gcm_decrypt(&THIS->gcm_ctx, &THIS->gcm_key, ctx, func, + data->len, STR0(result), STR0(data)); + } + + THIS->dmode |= NO_ADATA; + + if (data->len & (GCM_BLOCK_SIZE - 1)) + THIS->dmode |= NO_CDATA; + + pop_n_elems(args); + push_string(end_shared_string(result)); + UNSET_ONERROR(uwp); + } + + /*! @decl string(0..255) digest() + *! + *! Generate a message digest for the data accumulated so far. + *! + *! @note + *! @[set_iv()] needs to be called to start the next message. + *! + *! @seealso + *! @[update()], @[digest()] + */ + PIKEFUN string(0..255) digest() + { + struct pike_string *result; + ONERROR uwp; + crypt_func func = pike_crypt_func; + void *ctx = THIS->object; + + if (!THIS->object || !THIS->object->prog) { + Pike_error("Lookup in destructed object.\n"); + } + + if (THIS->mode < 0) + Pike_error("Key schedule not initialized.\n"); + + result = begin_shared_string(GCM_BLOCK_SIZE); + SET_ONERROR (uwp, do_free_string, result); + + if (THIS->crypt_state && THIS->crypt_state->crypt) { + func = THIS->crypt_state->crypt; + ctx = THIS->crypt_state->ctx; + } + + gcm_digest(&THIS->gcm_ctx, &THIS->gcm_key, ctx, func, + GCM_BLOCK_SIZE, STR0(result)); + + THIS->dmode |= NO_ADATA | NO_CDATA; + + pop_n_elems(args); + push_string(end_shared_string(result)); + UNSET_ONERROR(uwp); + } + } + /*! @endclass + */ + } + + /*! @endmodule + */ + +#endif /* HAVE_NETTLE_GCM_H */ + + INIT + { +#ifdef HAVE_NETTLE_GCM_H + apply_current(Nettle_Cipher16_cq__GCM_program_fun_num, 0); + if (TYPEOF(Pike_sp[-1]) == T_OBJECT) { + add_ref(THIS_NETTLE_CIPHER16->GCM = Pike_sp[-1].u.object); + } + pop_stack(); +#endif + } +} +/*! @endclass Cipher16 + */ + static void pike_aes_set_encrypt_key(void *ctx, ptrdiff_t length, const char *key, @@ -434,7 +961,7 @@ pike_aes_set_decrypt_key(void *ctx, #cmod_define PIKE_NAME AES #cmod_define NETTLE_NAME aes -#cmod_include "cipher.H" +#cmod_include "cipher16.H" #cmod_undef PIKE_NAME #cmod_undef NETTLE_NAME @@ -772,7 +1299,7 @@ pike_camellia_set_decrypt_key(void *ctx, #cmod_define PIKE_NAME CAMELLIA #cmod_define NETTLE_NAME camellia -#cmod_include "cipher.H" +#cmod_include "cipher16.H" #cmod_undef PIKE_NAME #cmod_undef NETTLE_NAME @@ -1549,7 +2076,7 @@ pike_serpent_set_key(void *ctx, #cmod_define PIKE_NAME SERPENT #cmod_define NETTLE_NAME serpent -#cmod_include "cipher.H" +#cmod_include "cipher16.H" #cmod_undef PIKE_NAME #cmod_undef NETTLE_NAME @@ -1573,7 +2100,7 @@ pike_twofish_set_key(void *ctx, #cmod_define PIKE_NAME Twofish #cmod_define NETTLE_NAME twofish -#cmod_include "cipher.H" +#cmod_include "cipher16.H" #cmod_undef PIKE_NAME #cmod_undef NETTLE_NAME @@ -1689,25 +2216,6 @@ static struct object *make_cipher_object(INT32 args) { } -/* Generic callback function for Nettle in case the crypt() - * function in the object isn't a Nettle function. - */ -static void pike_crypt_func(void *object, unsigned length, uint8_t *dst, - const uint8_t *src) -{ - int args; - struct pike_string *str; - push_string(make_shared_binary_string((const char *)src, length)); - args = safe_apply(object, "crypt", 1); - get_all_args("crypt", args, "%n", &str); - if (str->len != (ptrdiff_t)length) { - Pike_error("Bad string length %ld returned from crypt()\n", - DO_NOT_WARN((long)str->len)); - } - MEMCPY(dst, str->str, length); - pop_n_elems(args); -} - /*! @class CBC *! Implementation of the cipher block chaining mode (CBC). Works as *! a wrapper for the cipher algorithm put in create. @@ -2264,476 +2772,6 @@ PIKECLASS CTR #endif /* HAVE_NETTLE_CTR_H */ -#ifdef HAVE_NETTLE_GCM_H -#include <nettle/gcm.h> - -/*! @class GCM - *! Implementation of the Galois Counter Mode (GCM). Works as - *! a wrapper for the cipher algorithm put in create. - *! - *! This is a so-called authenticated encryption with associated data - *! (AEAD) algorithm, and in addition to encryption also provides - *! message digests. - *! - *! The operation of GCM is specified in - *! NIST Special Publication 800-38D. - *! - *! @note - *! Wrapped ciphers MUST have a block size of @expr{16@}. - *! - *! @note - *! Use @[Crypto.GCM] instead. - *! - *! @note - *! Note that this class is not available in all versions of Nettle. - *! - *! @seealso - *! @[Crypto.GCM], @[CBC] - */ -PIKECLASS GCM -{ - /*! @decl inherit __builtin.Nettle.AEAD - */ - INHERIT "__builtin.Nettle.AEAD"; - - /*! @decl string(0..255) name() - *! Returns the string @expr{"GCM"@}. - */ - PIKEFUN string(0..255) name() - optflags OPT_TRY_OPTIMIZE; - { - push_constant_text("GCM"); - } - - /*! @decl int(16..16) block_size() - *! Returns the block size of the encapsulated cipher, - *! which is always @expr{16@} for GCM. - */ - PIKEFUN int(16..16) block_size() - optflags OPT_TRY_OPTIMIZE; - { - RETURN GCM_BLOCK_SIZE; - } - - /*! @decl int(16..16) digest_size() - *! Returns the size of the generated digest, - *! which is always @expr{16@} for GCM. - */ - PIKEFUN int(16..16) digest_size() - optflags OPT_TRY_OPTIMIZE; - { - RETURN GCM_BLOCK_SIZE; - } - - /*! @decl int(12..12) iv_size() - *! Returns the recommended size for the initialization vector - *! (ie @expr{12@}). - *! - *! Other sizes are allowed, but will be compressed or expanded - *! to this size using the encapsulated cipher. - */ - PIKEFUN int(12..12) iv_size() - optflags OPT_TRY_OPTIMIZE; - { - RETURN GCM_IV_SIZE; - } - - /*! @class State - *! - *! The state for a GCM instance. - */ - PIKECLASS State - program_flags PROGRAM_USES_PARENT|PROGRAM_NEEDS_PARENT; - { - 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. - */ - struct program *parent_prog = Pike_compiler->previous->new_program; - struct object *parent_obj = Pike_compiler->previous->fake_object; - int parent_State_fun_num = - really_low_find_shared_string_identifier(MK_STRING("State"), - parent_prog, - SEE_PROTECTED|SEE_PRIVATE); - if (parent_State_fun_num >= 0) { - struct program *parent_State_prog = - low_program_from_function(parent_obj, parent_State_fun_num); - if (parent_State_prog) { - low_inherit(parent_State_prog, 0, - parent_State_fun_num + - parent_prog->inherits[1].identifier_level, - 1 + 42, 0, NULL); - } - } - } - - PIKEVAR object object - flags ID_PRIVATE|ID_PROTECTED|ID_HIDDEN; - CVAR struct Nettle_Cipher_State_struct *crypt_state; - CVAR INT32 mode; - CVAR INT32 dmode; - CVAR struct gcm_key gcm_key; - CVAR struct gcm_ctx gcm_ctx; - - /* dmode flags */ -#define NO_ADATA 1 /* Disallow associated data. */ -#define NO_CDATA 2 /* Disallow crypted data. */ - - INIT - { - THIS->mode = -1; - } - - EXIT - gc_trivial; - { - if (THIS->object) { - free_object(THIS->object); - THIS->object = NULL; - } - } - - /*! @decl void create(program|object|function cipher, mixed ... args) - *! - *! Initialize the GCM wrapper with a cipher algorithm. If it is a - *! program, an object will be instantiated with @[args] as - *! arguments. If it is an object that doesn't conform to the cipher - *! API, but has an @[LFUN::`()], that LFUN will be called. If it is - *! a function, that function will be called with @[args] as - *! arguments. - *! - *! @note - *! For correct key setup, @[set_encrypt_key()] or @[set_decrypt_key()] - *! MUST be used. - */ - PIKEFUN void create(program|object|function cipher, mixed ... more) - flags ID_PROTECTED; - { - struct object *o = make_cipher_object(args); - struct inherit *inh; - int f; - - if (THIS->object) free_object(THIS->object); - THIS->object = o; - - f = find_identifier("crypt", o->prog); - inh = INHERIT_FROM_INT(o->prog, f); - if (inh->prog == Nettle_Cipher_State_program) { - /* crypt() is from Nettle.Cipher.State. - * Check if the context and crypt function are valid. - */ - THIS->crypt_state = (struct Nettle_Cipher_State_struct *) - get_inherit_storage(o, inh - o->prog->inherits); - } - - safe_apply(THIS->object, "block_size", 0); - - if (TYPEOF(Pike_sp[-1]) != T_INT) - Pike_error("block_size() didn't return an int.\n"); - - if (Pike_sp[-1].u.integer != GCM_BLOCK_SIZE) - Pike_error("cipher has an invalid block size for GCM.\n"); - - pop_stack(); - - THIS->mode = -1; - } - - /*! @decl string(0..255) name() - *! Returns the string @expr{"GCM(x)"@} where x is the - *! encapsulated algorithm. - */ - PIKEFUN string(0..255) name() - optflags OPT_TRY_OPTIMIZE; - { - push_constant_text("GCM("); - safe_apply(THIS->object, "name", 0); - push_constant_text(")"); - f_add(3); - } - - /*! @decl int(16..16) block_size() - *! Returns the block size of the encapsulated cipher, - *! which is always @expr{16@} for GCM. - */ - PIKEFUN int(16..16) block_size() - optflags OPT_TRY_OPTIMIZE; - { - RETURN GCM_BLOCK_SIZE; - } - - /*! @decl int(16..16) digest_size() - *! Returns the size of the generated digest, - *! which is always @expr{16@} for GCM. - */ - PIKEFUN int(16..16) digest_size() - optflags OPT_TRY_OPTIMIZE; - { - RETURN GCM_BLOCK_SIZE; - } - - /*! @decl int(12..12) iv_size() - *! Returns the recommended size for the initialization vector - *! (ie @expr{12@}). - *! - *! Other sizes are allowed, but will be compressed or expanded - *! to this size using the encapsulated cipher. - */ - PIKEFUN int(12..12) iv_size() - optflags OPT_TRY_OPTIMIZE; - { - RETURN GCM_IV_SIZE; - } - - /*! @decl int(0..) key_size() - *! Returns the key size of the encapsulated cipher. - */ - PIKEFUN int(0..) key_size() - optflags OPT_EXTERNAL_DEPEND; - { - safe_apply(THIS->object, "key_size", args); - } - - /*! @decl this_program set_encrypt_key(string(0..255) key, int|void flags) - *! - *! Prepare the cipher and the wrapper for encrypting with the given - *! @[key]. The @[key] memory will be cleared before released. - *! - *! @note - *! Note that this operation does not by itself reset the - *! context sufficiently to start a new message; @[set_iv()] - *! needs to be called too. - *! - *! @seealso - *! @[set_decrypt_key()], @[set_iv()] - */ - PIKEFUN object set_encrypt_key(string(0..255) key, int|void flags) - optflags OPT_SIDE_EFFECT; - { - crypt_func func = pike_crypt_func; - void *ctx = THIS->object; - key->flags |= STRING_CLEAR_ON_EXIT; - safe_apply(THIS->object, "set_encrypt_key", args); - pop_stack(); - - if (THIS->crypt_state && THIS->crypt_state->crypt) { - func = THIS->crypt_state->crypt; - ctx = THIS->crypt_state->ctx; - } - gcm_set_key(&THIS->gcm_key, ctx, func); - THIS->mode = 0; - - push_object(this_object()); - } - - /*! @decl this_program set_decrypt_key(string(0..255) key, int|void flags) - *! - *! Prepare the cipher and the wrapper for decrypting with the given - *! @[key]. The @[key] memory will be cleared before released. - *! - *! @note - *! Note that this operation does not by itself reset the - *! context sufficiently to start a new message; @[set_iv()] - *! needs to be called too. - *! - *! @seealso - *! @[set_encrypt_key()], @[set_iv()] - */ - PIKEFUN object set_decrypt_key(string(0..255) key, int|void flags) - optflags OPT_SIDE_EFFECT; - { - crypt_func func = pike_crypt_func; - void *ctx = THIS->object; - key->flags |= STRING_CLEAR_ON_EXIT; - /* NOTE: GCM always uses the encryption function - * of the underlying cipher! - */ - safe_apply(THIS->object, "set_encrypt_key", args); - pop_stack(); - - if (THIS->crypt_state && THIS->crypt_state->crypt) { - func = THIS->crypt_state->crypt; - ctx = THIS->crypt_state->ctx; - } - gcm_set_key(&THIS->gcm_key, ctx, func); - THIS->mode = 1; - - push_object(this_object()); - } - - /*! @decl this_program set_iv(string(0..255) iv) - *! - *! Set the initialization vector to @[iv]. The @[iv] memory will be - *! cleared before released. - *! - *! Also resets all state needed to start a new message. - *! - *! @note - *! For @[iv]s of length other than @expr{12@}, an encryption or - *! decryption key must have been set first. - *! - *! @seealso - *! @[set_encrypt_key()], @[set_decrypt_key()]. - */ - PIKEFUN object set_iv(string(0..255) iv) - optflags OPT_SIDE_EFFECT; - { - iv->flags |= STRING_CLEAR_ON_EXIT; - NO_WIDE_STRING(iv); - if ((THIS->mode < 0) && (iv->len != GCM_IV_SIZE)) - Pike_error("The key must be set to use an iv of length other than %d.\n", - GCM_IV_SIZE); - gcm_set_iv(&THIS->gcm_ctx, &THIS->gcm_key, iv->len, STR0(iv)); - THIS->dmode = 0; - RETURN this_object(); - } - - /*! @decl void update(string(0..255) public_data) - *! - *! Add @[public_data] to be authenticated. - *! - *! The length of @[public_data] MUST be a multiple of the - *! block size (ie @expr{16@}) for all calls except the last. - *! - *! All calls of @[update()] need to be performed before - *! any calls of @[crypt()]. - */ - PIKEFUN void update(string(0..255) data) - { - NO_WIDE_STRING(data); - - if (!THIS->object || !THIS->object->prog) { - Pike_error("Lookup in destructed object.\n"); - } - - if (THIS->mode < 0) - Pike_error("Key schedule not initialized.\n"); - - if (THIS->dmode & NO_ADATA) - Pike_error("Public data not allowed now.\n"); - - gcm_update(&THIS->gcm_ctx, &THIS->gcm_key, data->len, STR0(data)); - - if (data->len & (GCM_BLOCK_SIZE - 1)) - THIS->dmode |= NO_ADATA; - - pop_n_elems(args); - } - - /*! @decl string(0..255) crypt(string(0..255) data) - *! - *! Encrypt/decrypt @[data] and return the result. @[data] must - *! be an integral number of blocks. - *! - *! The length of @[data] MUST be a multiple of the block size - *! (ie @expr{16@}) for all calls except the last. - *! - *! Neither the input or output data is not automatically memory - *! scrubbed, unless @[String.secure] has been called on the data. - *! - *! @seealso - *! @[update()], @[digest()] - */ - PIKEFUN string(0..255) crypt(string(0..255) data) - { - struct pike_string *result; - ONERROR uwp; - crypt_func func = pike_crypt_func; - void *ctx = THIS->object; - - NO_WIDE_STRING(data); - - if (!THIS->object || !THIS->object->prog) { - Pike_error("Lookup in destructed object.\n"); - } - - if (THIS->mode < 0) - Pike_error("Key schedule not initialized.\n"); - - if (THIS->dmode & NO_CDATA) - Pike_error("More data not allowed before the iv is reset.\n"); - - result = begin_shared_string(data->len); - SET_ONERROR (uwp, do_free_string, result); - - if (THIS->crypt_state && THIS->crypt_state->crypt) { - func = THIS->crypt_state->crypt; - ctx = THIS->crypt_state->ctx; - } - - if (!THIS->mode) { - gcm_encrypt(&THIS->gcm_ctx, &THIS->gcm_key, ctx, func, - data->len, STR0(result), STR0(data)); - } else { - gcm_decrypt(&THIS->gcm_ctx, &THIS->gcm_key, ctx, func, - data->len, STR0(result), STR0(data)); - } - - THIS->dmode |= NO_ADATA; - - if (data->len & (GCM_BLOCK_SIZE - 1)) - THIS->dmode |= NO_CDATA; - - pop_n_elems(args); - push_string(end_shared_string(result)); - UNSET_ONERROR(uwp); - } - - /*! @decl string(0..255) digest() - *! - *! Generate a message digest for the data accumulated so far. - *! - *! @note - *! @[set_iv()] needs to be called to start the next message. - *! - *! @seealso - *! @[update()], @[digest()] - */ - PIKEFUN string(0..255) digest() - { - struct pike_string *result; - ONERROR uwp; - crypt_func func = pike_crypt_func; - void *ctx = THIS->object; - - if (!THIS->object || !THIS->object->prog) { - Pike_error("Lookup in destructed object.\n"); - } - - if (THIS->mode < 0) - Pike_error("Key schedule not initialized.\n"); - - result = begin_shared_string(GCM_BLOCK_SIZE); - SET_ONERROR (uwp, do_free_string, result); - - if (THIS->crypt_state && THIS->crypt_state->crypt) { - func = THIS->crypt_state->crypt; - ctx = THIS->crypt_state->ctx; - } - - gcm_digest(&THIS->gcm_ctx, &THIS->gcm_key, ctx, func, - GCM_BLOCK_SIZE, STR0(result)); - - THIS->dmode |= NO_ADATA | NO_CDATA; - - pop_n_elems(args); - push_string(end_shared_string(result)); - UNSET_ONERROR(uwp); - } - } - /*! @endclass - */ -} - -/*! @endclass - */ - -#endif /* HAVE_NETTLE_GCM_H */ - #define PAD_SSL 0 #define PAD_ISO_10126 1 #define PAD_ANSI_X923 2 diff --git a/src/post_modules/Nettle/cipher16.H b/src/post_modules/Nettle/cipher16.H new file mode 100644 index 0000000000000000000000000000000000000000..229b99e92194c1ebe7c7d3560f67b2dbd4b226b1 --- /dev/null +++ b/src/post_modules/Nettle/cipher16.H @@ -0,0 +1,82 @@ +/* -*- C -*- + * + * Cmod header-file acting as a template for the cipher classes + * that have a 16-byte block size. + */ + +DOCSTART() @class PIKE_NAME + *! + *! Implementation of the PIKE_NAME cipher. + *! +DOCEND() +PIKECLASS PIKE_NAME +{ + DOCSTART() @decl inherit Cipher16 + DOCEND() + INHERIT Nettle_Cipher16; + + static const struct pike_cipher cmod_CONCAT_EVAL(pike_, NETTLE_NAME) = + _PIKE_CIPHER(NETTLE_NAME, PIKE_NAME); + + INIT + { + struct Nettle_Cipher_struct *cipher; + ASSIGN_CURRENT_STORAGE(cipher, struct Nettle_Cipher_struct, 2, + Nettle_Cipher_program); + cipher->meta = &cmod_CONCAT_EVAL(pike_, NETTLE_NAME); + } + + DOCSTART() @class State + *! + *! State for PIKE_NAME encyption. + *! + DOCEND() + PIKECLASS State + program_flags PROGRAM_NEEDS_PARENT|PROGRAM_USES_PARENT; + { + DOCSTART() @decl inherit Cipher::State + DOCEND() + + EXTRA + { + /* Perform an inherit of the Cipher.State class that our parent + * contains via its inherit of Cipher16. + */ + struct program *parent_prog = Pike_compiler->previous->new_program; + struct object *parent_obj = Pike_compiler->previous->fake_object; + int parent_State_fun_num = + really_low_find_shared_string_identifier(MK_STRING("State"), + parent_prog, + SEE_PROTECTED|SEE_PRIVATE); + if (parent_State_fun_num >= 0) { + struct program *parent_State_prog = + low_program_from_function(parent_obj, parent_State_fun_num); + if (parent_State_prog) { + low_inherit(parent_State_prog, 0, + parent_State_fun_num + + parent_prog->inherits[1].identifier_level, + 1 + 42, 0, NULL); + } + } + } + + CVAR struct cmod_CONCAT_EVAL(NETTLE_NAME, _ctx) NETTLE_NAME; + + INIT + { + struct Nettle_Cipher_State_struct *state; + ASSIGN_CURRENT_STORAGE(state, struct Nettle_Cipher_State_struct, 1, + Nettle_Cipher_State_program); + + fprintf(stderr, "%s: Setting state->ctx (%p) to %p\n", + TOSTR(PIKE_NAME), state, &THIS->NETTLE_NAME); + state->ctx = &THIS->NETTLE_NAME; + } + } + DOCSTART() @endclass State + DOCEND() + +} + +DOCSTART() @endclass PIKE_NAME +DOCEND()