OSStatus AES_GCM_Decrypt( AES_GCM_Context *inContext, const void *inSrc, size_t inLen, void *inDst ) { OSStatus err; err = gcm_decrypt( inDst, inSrc, inLen, &inContext->ctx ); require_noerr( err, exit ); exit: return( err ); }
void Document::decrypt(const uint8_t* key, int key_size, const std::string& key_name) { // Only accept 256bit keys. if (k_key_size != key_size) { throw LJ__Exception("Decrypt key must be 256bits."); } // Get the document to decrypt. const lj::bson::Node& source_node = doc_->nav(k_crypt_data).nav(key_name); lj::bson::Binary_type bt; uint32_t source_size; const uint8_t* source = lj::bson::as_binary(source_node, &bt, &source_size); // Get the initialization vector for the encrypted data. const lj::bson::Node& ivector_node = doc_->nav(k_crypt_vector).nav(key_name); uint32_t iv_size; const uint8_t* iv = lj::bson::as_binary(ivector_node, &bt, &iv_size); // Check to make sure the initialization vector is the correct length. if (GCM_IV_SIZE != iv_size) { throw LJ__Exception("Initialization vector for this encrypted data is incorrect."); } // Prepare all the data structures for the AES crypto in GCM mode. struct aes_ctx cipher_ctx; aes_set_encrypt_key(&cipher_ctx, key_size, key); struct gcm_key auth_key; gcm_set_key(&auth_key, &cipher_ctx, (nettle_crypt_func*) & aes_encrypt); struct gcm_ctx auth_ctx; gcm_set_iv(&auth_ctx, &auth_key, GCM_IV_SIZE, iv); // Perform the actual encryption. std::unique_ptr < uint8_t[] > destination(new uint8_t[source_size]); gcm_decrypt(&auth_ctx, &auth_key, &cipher_ctx, (nettle_crypt_func*) & aes_encrypt, source_size, destination.get(), source); // Extract the authentication information. uint8_t auth_tag[GCM_BLOCK_SIZE]; gcm_digest(&auth_ctx, &auth_key, &cipher_ctx, (nettle_crypt_func*) & aes_encrypt, GCM_BLOCK_SIZE, auth_tag); // Get the authentication vector from the encryption. const lj::bson::Node& authentication_node = doc_->nav(k_crypt_auth).nav(key_name); uint32_t authentication_size; const uint8_t* original_auth_tag = lj::bson::as_binary(authentication_node, &bt, &authentication_size); // Check to make sure the authentication tag is the correct size. if (GCM_BLOCK_SIZE != authentication_size) { throw LJ__Exception("Authentication tag for this encrypted data is incorrect."); } // Compare the authentication tag from decryption to the authentication tag from encryption. if (memcmp(auth_tag, original_auth_tag, GCM_BLOCK_SIZE) != 0) { throw LJ__Exception("Authentication tags did not match. Data may be corrupted."); } // The destination should now contain whatever was originally encrypted. // Turn the data back into a bson document lj::bson::Node changes(lj::bson::Type::k_document, destination.get()); // Clean up anything sensitive from memory. lj::Wiper < uint8_t[]>::wipe(destination, source_size); lj::Wiper < uint8_t[]>::wipe(auth_tag, GCM_BLOCK_SIZE); // try to combine the documents. This may throw an exception if things are // messed up. lj::bson::combine(doc_->nav("."), changes.nav(".")); // Remove the encrypted data from the document. doc_->nav(k_crypt_vector).set_child(key_name, nullptr); doc_->nav(k_crypt_auth).set_child(key_name, nullptr); doc_->nav(k_crypt_data).set_child(key_name, nullptr); }