size_t Cipher::put(const unsigned char *data, size_t size) { if(size % keys.iosize() || !bufaddr) return 0; size_t count = 0; while(bufsize && size + bufpos > bufsize) { size_t diff = bufsize - bufpos; count += put(data, diff); data += diff; size -= diff; } switch(bufmode) { case Cipher::ENCRYPT: gnutls_cipher_encrypt2((CIPHER_CTX)context, (void *)data, size, bufaddr + bufpos, size); break; case Cipher::DECRYPT: gnutls_cipher_decrypt2((CIPHER_CTX)context, data, size, bufaddr + bufpos, size); } count += size; if(!count) { release(); return 0; } bufpos += size; if(bufsize && bufpos >= bufsize) { push(bufaddr, bufsize); bufpos = 0; } return count; }
static gboolean purple_aes_cipher_gnutls_decrypt(const guchar *input, guchar *output, size_t len, guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size, PurpleCipherBatchMode batch_mode) { gnutls_cipher_hd_t handle; int ret; /* We have to simulate ECB mode, which is not supported by GnuTLS. */ if (batch_mode == PURPLE_CIPHER_BATCH_MODE_ECB) { size_t i; for (i = 0; i < len / PURPLE_AES_BLOCK_SIZE; i++) { int offset = i * PURPLE_AES_BLOCK_SIZE; guchar iv_local[PURPLE_AES_BLOCK_SIZE]; gboolean succ; memcpy(iv_local, iv, sizeof(iv_local)); succ = purple_aes_cipher_gnutls_decrypt( input + offset, output + offset, PURPLE_AES_BLOCK_SIZE, iv_local, key, key_size, PURPLE_CIPHER_BATCH_MODE_CBC); if (!succ) return FALSE; } return TRUE; } handle = purple_aes_cipher_gnutls_crypt_init(iv, key, key_size); if (handle == NULL) return FALSE; ret = gnutls_cipher_decrypt2(handle, input, len, output, len); gnutls_cipher_deinit(handle); if (ret != 0) { purple_debug_error("cipher-aes", "gnutls_cipher_decrypt2 failed: %d\n", ret); return FALSE; } return TRUE; }
static gboolean purple_aes_cipher_gnutls_decrypt(const guchar *input, guchar *output, size_t len, guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size) { gnutls_cipher_hd_t handle; int ret; handle = purple_aes_cipher_gnutls_crypt_init(iv, key, key_size); if (handle == NULL) return FALSE; ret = gnutls_cipher_decrypt2(handle, input, len, output, len); gnutls_cipher_deinit(handle); if (ret != 0) { purple_debug_error("cipher-aes", "gnutls_cipher_decrypt2 failed: %d\n", ret); return FALSE; } return TRUE; }
gchar *password_decrypt_gnutls(const gchar *password, const gchar *decryption_passphrase) { gchar **tokens, *tmp; gnutls_cipher_algorithm_t algo; gnutls_cipher_hd_t handle; gnutls_datum_t key, iv; int keylen, blocklen, ret; gsize len; unsigned char *buf; guint rounds; size_t commapos; g_return_val_if_fail(password != NULL, NULL); g_return_val_if_fail(decryption_passphrase != NULL, NULL); tokens = g_strsplit_set(password, "{}", 3); /* Parse the string, retrieving algorithm and encrypted data. * We expect "{algorithm,rounds}base64encodedciphertext". */ if (tokens[0] == NULL || strlen(tokens[0]) != 0 || tokens[1] == NULL || strlen(tokens[1]) == 0 || tokens[2] == NULL || strlen(tokens[2]) == 0) { debug_print("Garbled password string.\n"); g_strfreev(tokens); return NULL; } commapos = strcspn(tokens[1], ","); if (commapos == strlen(tokens[1]) || commapos == 0) { debug_print("Garbled algorithm substring.\n"); g_strfreev(tokens); return NULL; } buf = g_strndup(tokens[1], commapos); if ((algo = gnutls_cipher_get_id(buf)) == GNUTLS_CIPHER_UNKNOWN) { debug_print("Password string has unknown algorithm: '%s'\n", buf); g_free(buf); g_strfreev(tokens); return NULL; } g_free(buf); if ((rounds = atoi(tokens[1] + commapos + 1)) <= 0) { debug_print("Invalid number of rounds: %d\n", rounds); g_strfreev(tokens); return NULL; } /* ivlen = gnutls_cipher_get_iv_size(algo); */ keylen = gnutls_cipher_get_key_size(algo); blocklen = gnutls_cipher_get_block_size(algo); /* digestlen = gnutls_hash_get_len(digest); */ /* Take the passphrase and compute a key derivation of suitable * length to be used as encryption key for our block cipher. */ key.data = _make_key_deriv(decryption_passphrase, rounds, keylen); key.size = keylen; /* Prepare random IV for cipher */ iv.data = malloc(IVLEN); iv.size = IVLEN; if (!get_random_bytes(iv.data, IVLEN)) { g_free(key.data); g_free(iv.data); g_strfreev(tokens); return NULL; } /* Prepare encrypted password string for decryption. */ tmp = g_base64_decode(tokens[2], &len); g_strfreev(tokens); /* Initialize the decryption */ ret = gnutls_cipher_init(&handle, algo, &key, &iv); if (ret < 0) { debug_print("Cipher init failed: %s\n", gnutls_strerror(ret)); g_free(key.data); g_free(iv.data); return NULL; } buf = malloc(BUFSIZE + blocklen); memset(buf, 0, BUFSIZE + blocklen); ret = gnutls_cipher_decrypt2(handle, tmp, len, buf, BUFSIZE + blocklen); if (ret < 0) { debug_print("Decryption failed: %s\n", gnutls_strerror(ret)); g_free(key.data); g_free(iv.data); g_free(buf); gnutls_cipher_deinit(handle); return NULL; } /* Cleanup */ gnutls_cipher_deinit(handle); g_free(key.data); g_free(iv.data); tmp = g_strndup(buf + blocklen, MIN(strlen(buf + blocklen), BUFSIZE)); g_free(buf); return tmp; }
char * crypto_decrypt (const char *cipher, int key_type, GByteArray *data, const char *iv, const gsize iv_len, const char *key, const gsize key_len, gsize *out_len, GError **error) { gnutls_cipher_hd_t ctx; gnutls_datum_t key_dt, iv_dt; int err; int cipher_mech, i; char *output = NULL; gboolean success = FALSE; gsize pad_len, real_iv_len; if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) { cipher_mech = GNUTLS_CIPHER_3DES_CBC; real_iv_len = SALT_LEN; } else if (!strcmp (cipher, CIPHER_DES_CBC)) { cipher_mech = GNUTLS_CIPHER_DES_CBC; real_iv_len = SALT_LEN; } else if (!strcmp (cipher, CIPHER_AES_CBC)) { cipher_mech = GNUTLS_CIPHER_AES_128_CBC; real_iv_len = 16; } else { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_UNKNOWN_CIPHER, _("Private key cipher '%s' was unknown."), cipher); return NULL; } if (iv_len < real_iv_len) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_RAW_IV_INVALID, _("Invalid IV length (must be at least %zd)."), real_iv_len); return NULL; } output = g_malloc0 (data->len); key_dt.data = (unsigned char *) key; key_dt.size = key_len; iv_dt.data = (unsigned char *) iv; iv_dt.size = iv_len; err = gnutls_cipher_init (&ctx, cipher_mech, &key_dt, &iv_dt); if (err < 0) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_CIPHER_INIT_FAILED, _("Failed to initialize the decryption cipher context: %s (%s)"), gnutls_strerror_name (err), gnutls_strerror (err)); goto out; } err = gnutls_cipher_decrypt2 (ctx, data->data, data->len, output, data->len); if (err < 0) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED, _("Failed to decrypt the private key: %s (%s)"), gnutls_strerror_name (err), gnutls_strerror (err)); goto out; } pad_len = output[data->len - 1]; /* Check if the padding at the end of the decrypted data is valid */ if (pad_len == 0 || pad_len > real_iv_len) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED, _("Failed to decrypt the private key: unexpected padding length.")); goto out; } /* Validate tail padding; last byte is the padding size, and all pad bytes * should contain the padding size. */ for (i = 1; i <= pad_len; ++i) { if (output[data->len - i] != pad_len) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED, _("Failed to decrypt the private key.")); goto out; } } *out_len = data->len - pad_len; success = TRUE; out: if (!success) { if (output) { /* Don't expose key material */ memset (output, 0, data->len); g_free (output); output = NULL; } } gnutls_cipher_deinit (ctx); return output; }