dodo  0.0.1
A C++ library to create containerized Linux services
datacrypt.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the dodo library (https://github.com/jmspit/dodo).
3  * Copyright (c) 2019 Jan-Marten Spit.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, version 3.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /**
19  * @file datacrypt.cpp
20  * Implements the dodo::common::DataCrypt class.
21  */
22 
23 #include <common/datacrypt.hpp>
24 #include <common/util.hpp>
25 
26 #include <openssl/conf.h>
27 #include <openssl/err.h>
28 
29 #include <iostream>
30 
31 namespace dodo::common {
32 
33  void DataCrypt::encrypt( Cipher cipher,
34  const std::string &key,
35  const Bytes& src,
36  std::string &dest ) {
37  EVP_CIPHER_CTX *ctx = nullptr;
38 
39  if ( !( ctx = EVP_CIPHER_CTX_new() ) )
40  throw_Exception( "EVP_CIPHER_CTX_new : " << common::getSSLErrors( '\n' ) );
41 
42 
43  Bytes k = paddedKey( cipher, key );
44  Bytes iv;
45  Bytes encrypted;
46  iv.random( ivOctets( cipher ) );
47  encrypted.reserve( cipherOctets( cipher, src.getSize() ) );
48 
49  int rc = 0;
50  switch ( cipher ) {
52  rc = EVP_EncryptInit_ex( ctx, EVP_aes_128_gcm(), nullptr, k.getArray(), iv.getArray() );
53  break;
55  rc = EVP_EncryptInit_ex( ctx, EVP_aes_192_gcm(), nullptr, k.getArray(), iv.getArray() );
56  break;
58  rc = EVP_EncryptInit_ex( ctx, EVP_aes_256_gcm(), nullptr, k.getArray(), iv.getArray() );
59  break;
60  case Cipher::Invalid :
61  throw_Exception( "cannot use Cipher 'Invalid'" );
62  break;
63  }
64  if ( rc != 1 ) throw_Exception( "EVP_EncryptInit_ex : " << common::getSSLErrors( '\n' ) );
65 
66  EVP_CIPHER_CTX_ctrl( ctx, EVP_CTRL_AEAD_SET_IVLEN, ivOctets( cipher ) * 8, nullptr );
67 
68  int enc_size = 0;
69  int len = 0;
70  rc = EVP_EncryptUpdate( ctx,
71  encrypted.getArray(),
72  (int*)&len,
73  src.getArray(),
74  (int)src.getSize() );
75  if ( rc != 1 ) throw_Exception( "EVP_EncryptUpdate : " << common::getSSLErrors( '\n' ) );
76  enc_size += len;
77 
78  rc = EVP_EncryptFinal_ex( ctx,
79  encrypted.getArray() + len,
80  &len);
81  if ( rc != 1 ) throw_Exception( "EVP_EncryptFinal_ex : " << common::getSSLErrors( '\n' ) );
82  enc_size += len;
83  encrypted.reserve( enc_size );
84 
85  Bytes tag;
86  tag.reserve( tagLength( cipher ) );
87  if ( EVP_CIPHER_CTX_ctrl( ctx, EVP_CTRL_GCM_GET_TAG, (int)tag.getSize(), tag.getArray() ) != 1 )
88  throw_Exception( "EVP_CIPHER_CTX_ctrl : " << common::getSSLErrors( '\n' ) );
89 
90  std::stringstream ss;
91  ss << "ENC[cipher:" << cipher2String( cipher ) << ",";
92  ss << "data:" << encrypted.encodeBase64();
93  ss << ",iv:" << iv.encodeBase64();
94  ss << ",tag:" << tag.encodeBase64();
95  ss << "]";
96  dest = ss.str();
97 
98  EVP_CIPHER_CTX_cleanup( ctx );
99  }
100 
101  bool DataCrypt::decode( const std::string &src,
102  std::string &cipher,
103  std::string &data,
104  std::string &iv,
105  std::string &tag ) {
106  cipher = "";
107  data = "";
108  iv = "";
109  tag = "";
110  if ( src.substr(0,4) != "ENC[" ) return false;
111  size_t close_pos = src.length() -1;
112  while ( std::isspace( src[close_pos] ) && close_pos > 0 ) close_pos--;
113  if ( src.substr(close_pos,1) != "]" ) return false;
114  std::vector<std::string> sections = split( src.substr(4,close_pos-4), ',' );
115  if ( sections.size() != 4 ) return false;
116  for ( auto s : sections ) {
117  std::vector<std::string> tokens = split( s, ':' );
118  if ( tokens.size() != 2 ) return false;
119  if ( tokens[0] == "cipher" ) cipher = tokens[1];
120  else if ( tokens[0] == "data" ) data = tokens[1];
121  else if ( tokens[0] == "iv" ) iv = tokens[1];
122  else if ( tokens[0] == "tag" ) tag = tokens[1];
123  else return false;
124  }
125  if ( cipher.length() == 0 || data.length() == 0 || iv.length() == 0 || tag.length() == 0 ) return false;
126  return true;
127  }
128 
129  int DataCrypt::decrypt( const std::string &key,
130  const std::string src,
131  Bytes &dest ) {
132 
133  std::string scipher;
134  std::string sdata;
135  std::string siv;
136  std::string stag;
137  int result = 0;
138  if ( !decode( src, scipher, sdata, siv, stag ) ) return 1;
139  Cipher cipher = string2Cipher( scipher );
140  Bytes data;
141  Bytes iv;
142  Bytes tag;
143  data.decodeBase64( sdata );
144  iv.decodeBase64( siv );
145  tag.decodeBase64( stag );
146 
147  Bytes k = paddedKey( cipher, key );
148 
149  EVP_CIPHER_CTX *ctx = nullptr;
150 
151  if ( !( ctx = EVP_CIPHER_CTX_new() ) )
152  throw_Exception( "EVP_CIPHER_CTX_new : " << common::getSSLErrors( '\n' ) );
153 
154  int rc = 0;
155  switch ( cipher ) {
157  rc = EVP_DecryptInit_ex( ctx, EVP_aes_128_gcm(), nullptr, k.getArray(), iv.getArray() );
158  break;
160  rc = EVP_DecryptInit_ex( ctx, EVP_aes_192_gcm(), nullptr, k.getArray(), iv.getArray() );
161  break;
163  rc = EVP_DecryptInit_ex( ctx, EVP_aes_256_gcm(), nullptr, k.getArray(), iv.getArray() );
164  break;
165  case Cipher::Invalid :
166  throw_Exception( "invalid cipher " << scipher );
167  break;
168  }
169  if ( rc != 1 ) throw_Exception( "EVP_DecryptInit_ex : " << common::getSSLErrors( '\n' ) );
170 
171  EVP_CIPHER_CTX_ctrl( ctx, EVP_CTRL_AEAD_SET_IVLEN, ivOctets( cipher ) * 8, nullptr );
172 
173  EVP_CIPHER_CTX_ctrl( ctx, EVP_CTRL_AEAD_SET_TAG, (int)tag.getSize(), tag.getArray() );
174 
175  Bytes tmp;
176  tmp.reserve( data.getSize() );
177  dest.reserve(0);
178 
179  int len = (int)data.getSize();
180 
181  rc = EVP_DecryptUpdate( ctx,
182  tmp.getArray(),
183  (int*)&len,
184  data.getArray(),
185  (int)data.getSize() );
186  if ( rc != 1 ) throw_Exception( "EVP_DecryptUpdate : " << common::getSSLErrors( '\n' ) );
187  dest.append( tmp, len );
188 
189  len = (int)data.getSize();
190  rc = EVP_DecryptFinal_ex( ctx,
191  tmp.getArray(),
192  &len);
193  if ( rc != 1 ) result = 2;
194  dest.append( tmp, len );
195 
196  EVP_CIPHER_CTX_cleanup(ctx);
197 
198  return result;
199  }
200 
201  std::string DataCrypt::paddedKey( Cipher cipher, const std::string key ) {
202  if ( key.length() < 1 ) throw_Exception( "empty key");
203  std::stringstream ss;
204  size_t idx = 0;
205  while( ss.str().length() < (size_t)keyOctets( cipher ) ) {
206  ss << key[idx];
207  if ( ++idx > key.length() - 1 ) idx = 0;
208  }
209  return ss.str();
210  }
211 
212 }
dodo::common::split
std::vector< std::string > split(const std::string &src, char delimiter=' ')
Split a string into substrings.
Definition: util.hpp:136
dodo::common::Bytes::encodeBase64
std::string encodeBase64() const
Encodes the this Bytes into a base64-encoded string.
Definition: bytes.cpp:137
dodo::common::Bytes::getArray
Octet * getArray() const
Return the array.
Definition: bytes.hpp:186
dodo::common::Bytes::decodeBase64
Bytes & decodeBase64(const std::string &src)
Decodes the base64 string into this Bytes.
Definition: bytes.cpp:115
dodo::common::DataCrypt::keyOctets
static int keyOctets(Cipher cipher)
Return the size of the key for the given Cipher in bits.
Definition: datacrypt.hpp:94
dodo::common::DataCrypt::decrypt
static int decrypt(const std::string &key, const std::string src, Bytes &dest)
Decrypt data with a key.
Definition: datacrypt.cpp:129
dodo::common::Bytes::reserve
void reserve(size_t size)
Reserve memory in the Bytes.
Definition: bytes.cpp:33
dodo::common::Bytes
An array of Octets with size elements.
Definition: bytes.hpp:44
dodo::common::DataCrypt::Cipher
Cipher
Cipher selection.
Definition: datacrypt.hpp:81
dodo::common::Bytes::append
void append(const Bytes &src)
Append another Bytes.
Definition: bytes.cpp:65
dodo::common::DataCrypt::string2Cipher
static Cipher string2Cipher(const std::string &s)
Convert a string representation to an Cipher.
Definition: datacrypt.hpp:169
dodo::common::DataCrypt::encrypt
static void encrypt(Cipher cipher, const std::string &key, const Bytes &src, std::string &dst)
Encrypt data with a key into a string (so the encrypted data will not contain a 0/zero).
Definition: datacrypt.cpp:33
dodo::common::Bytes::random
void random(size_t octets)
Generate a random set of Octets.
Definition: bytes.cpp:107
dodo::common::DataCrypt::cipherOctets
static size_t cipherOctets(Cipher cipher, size_t octets)
Calculate the size of the encrypted data from the input size.
Definition: datacrypt.hpp:209
dodo::common::DataCrypt::ivOctets
static int ivOctets(Cipher cipher)
Return the size of the IV (initialization vector) for the given Cipher in bits.
Definition: datacrypt.hpp:109
throw_Exception
#define throw_Exception(what)
Throws an Exception, passes FILE and LINE to constructor.
Definition: exception.hpp:174
dodo::common::DataCrypt::cipher2String
static std::string cipher2String(const Cipher &cipher)
String representation of an Cipher instance.
Definition: datacrypt.hpp:154
datacrypt.hpp
dodo::common::getSSLErrors
std::string getSSLErrors(char terminator)
Get all OpenSSL errors as a single string, and clear their error state.
Definition: util.cpp:154
dodo::common
Common and utility interfaces.
Definition: application.hpp:29
dodo::common::DataCrypt::Cipher::EVP_aes_128_gcm
@ EVP_aes_128_gcm
https://www.openssl.org/docs/man1.0.2/man3/EVP_aes_128_gcm.html
dodo::common::DataCrypt::paddedKey
static std::string paddedKey(Cipher cipher, const std::string key)
Pad or trim a key to match the key size for the Cipher.
Definition: datacrypt.cpp:201
dodo::common::DataCrypt::Cipher::EVP_aes_256_gcm
@ EVP_aes_256_gcm
https://www.openssl.org/docs/man1.0.2/man3/EVP_aes_256_gcm.html
dodo::common::DataCrypt::decode
static bool decode(const std::string &src, std::string &cipher, std::string &data, std::string &iv, std::string &tag)
Decode an ENC[] string into its parts.
Definition: datacrypt.cpp:101
dodo::common::Bytes::getSize
size_t getSize() const
Return the array size.
Definition: bytes.hpp:192
dodo::common::DataCrypt::tagLength
static int tagLength(Cipher cipher)
Return the tag length of the Cipher in octets.
Definition: datacrypt.hpp:139
dodo::common::DataCrypt::Cipher::EVP_aes_192_gcm
@ EVP_aes_192_gcm
https://www.openssl.org/docs/man1.0.2/man3/EVP_aes_192_gcm.html
util.hpp