1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
|
/*
* Distributed under the OSI-approved Apache License, Version 2.0. See
* accompanying file Copyright.txt for details.
*
* EncryptionOperator.cpp
*
* Created on: Dec 7, 2021
* Author: Caitlin Ross <caitlin.ross@kitware.com>
*/
#include "EncryptionOperator.h"
#include <fstream>
#include <string>
#include <sodium.h>
namespace adios2
{
namespace plugin
{
struct EncryptionOperator::EncryptImpl
{
std::string KeyFilename;
unsigned char Key[crypto_secretbox_KEYBYTES] = {0};
bool KeyValid = false;
~EncryptImpl()
{
// unlocks the memory location as well as zeroing out the data
sodium_munlock(Key, crypto_secretbox_KEYBYTES);
}
void GenerateOrReadKey()
{
// if the key file already exists, we'll use that key,
// otherwise we'll generate one and write it out
std::fstream keyFile(KeyFilename.c_str());
if (keyFile)
{
keyFile.read(reinterpret_cast<char *>(&Key), crypto_secretbox_KEYBYTES);
keyFile.close();
}
else
{
keyFile.open(KeyFilename.c_str(), std::fstream::out);
if (!keyFile)
{
throw std::runtime_error("couldn't open file to write key");
}
crypto_secretbox_keygen(Key);
keyFile.write(reinterpret_cast<char *>(&Key), crypto_secretbox_KEYBYTES);
keyFile.close();
}
// lock the key to avoid swapping to disk
if (sodium_mlock(Key, crypto_secretbox_KEYBYTES) == -1)
{
throw std::runtime_error("Unable to lock memory location of secret key,"
" due to system limit on amount of memory that can be locked "
"by a process.");
}
KeyValid = true;
}
};
EncryptionOperator::EncryptionOperator(const Params ¶meters)
: PluginOperatorInterface(parameters), Impl(new EncryptImpl)
{
if (sodium_init() < 0)
{
throw std::runtime_error("libsodium could not be initialized");
}
// in the case "secretkeyfile" is found, so we know the operator should
// calling Operate(). If "secretkeyfile" is not found, then the operator
// should be calling InverseOperate(), due to ADIOS calling InverseOperate()
// not allowing Parameters to be passed.
auto skFileIt = m_Parameters.find("secretkeyfile");
if (skFileIt != m_Parameters.end())
{
Impl->KeyFilename = skFileIt->second;
Impl->GenerateOrReadKey();
}
}
EncryptionOperator::~EncryptionOperator() {}
#if defined(__clang__)
#if __has_feature(memory_sanitizer)
// Memory Sanitizer has an issue with some libsodium calls.
__attribute__((no_sanitize("memory")))
#endif
#endif
size_t
EncryptionOperator::Operate(const char *dataIn, const Dims &blockStart, const Dims &blockCount,
const DataType type, char *bufferOut)
{
if (!Impl->KeyValid)
{
throw std::runtime_error("EncryptionOperator::Operate was called, but"
" a valid secret key has not been generated. "
"Did you add the SecretKeyFile"
" param when setting up the operator?");
}
// offset for writing into bufferOut
size_t offset = 0;
// write any parameters we need to save for the InverseOperate() call
// In this case, we just write out the size of the data
size_t sizeIn = helper::GetTotalSize(blockCount, helper::GetDataTypeSize(type));
PutParameter(bufferOut, offset, sizeIn);
// create the nonce directly in the output buffer, since we'll need it for
// decryption
unsigned char *nonce = reinterpret_cast<unsigned char *>(bufferOut + offset);
randombytes_buf(nonce, crypto_secretbox_NONCEBYTES);
offset += crypto_secretbox_NONCEBYTES;
// encrypt data directly into the output buffer
size_t cipherTextSize = sizeIn + crypto_secretbox_MACBYTES;
unsigned char *cipherText = reinterpret_cast<unsigned char *>(bufferOut + offset);
crypto_secretbox_easy(cipherText, reinterpret_cast<const unsigned char *>(dataIn), sizeIn,
nonce, Impl->Key);
offset += cipherTextSize;
// need to return the size of data in the buffer
return offset;
}
#if defined(__clang__)
#if __has_feature(memory_sanitizer)
// Memory Sanitizer has an issue with some libsodium calls.
__attribute__((no_sanitize("memory")))
#endif
#endif
size_t
EncryptionOperator::InverseOperate(const char *bufferIn, const size_t sizeIn, char *dataOut)
{
size_t offset = 0;
// need to grab any parameter(s) we saved in Operate()
const size_t dataBytes = GetParameter<size_t>(bufferIn, offset);
// grab the nonce ptr
const unsigned char *nonce = reinterpret_cast<const unsigned char *>(bufferIn + offset);
offset += crypto_secretbox_NONCEBYTES;
// grab the cipher text ptr
size_t cipherTextSize = dataBytes + crypto_secretbox_MACBYTES;
const unsigned char *cipherText = reinterpret_cast<const unsigned char *>(bufferIn + offset);
offset += cipherTextSize;
// decrypt directly into dataOut buffer
if (crypto_secretbox_open_easy(reinterpret_cast<unsigned char *>(dataOut), cipherText,
cipherTextSize, nonce, Impl->Key) != 0)
{
throw std::runtime_error("message forged!");
}
// return the size of the data
return dataBytes;
}
bool EncryptionOperator::IsDataTypeValid(const DataType type) const { return true; }
size_t EncryptionOperator::GetEstimatedSize(const size_t ElemCount, const size_t ElemSize,
const size_t ndims, const size_t *dims) const
{
size_t sizeIn = ElemCount * ElemSize;
return (sizeof(size_t) // Data size
+ crypto_secretbox_NONCEBYTES // Nonce
+ sizeIn // Data
+ crypto_secretbox_MACBYTES // MAC
);
}
} // end namespace plugin
} // end namespace adios2
extern "C" {
adios2::plugin::EncryptionOperator *OperatorCreate(const adios2::Params ¶meters)
{
return new adios2::plugin::EncryptionOperator(parameters);
}
void OperatorDestroy(adios2::plugin::EncryptionOperator *obj) { delete obj; }
}
|