Skip to content

Commit

Permalink
Improve error reporting in ThemisPP (#385)
Browse files Browse the repository at this point in the history
* Add Themis status to exceptions

Just like for JavaScript, it may be interesting for the user to check
the status code. Add the status code field to the exception type.

Default status code is "invalid parameter" as that's what most checks
performed by ThemisPP are. Do not use actual default arguments to keep
the ABI stable.

* Secure Message: add empty key checks

Just like with JsThemis, Secure Message in ThemisPP has an interface
that may be hard to use correctly. Furthermore, it may be 'successfully'
misused by providing an empty private or public key and then calling
"wrap" or "unwrap" (actually doing "sign" or "verify").

Introduce additional length checks for the keys to ensure that keys are
set when they should be set.

While we're here, use the new status reporting feature of exceptions to
return Themis status codes together with errors.

* Secure Cell: add empty key checks

Add a similar early check for empty password (aka "master key"). While
we're here, update the error messages in exceptions and include status
code into them.

* Secure Comparator: improved error messages

Update the error messages for consistency, add status code information.

* Secure Session: add empty key checks

Add checks for empty client ID and private key at construction, these
will throw exceptions now. Also update the error handling code to
include status codes into exceptions and use consistent messages.

* Key generation: improved error messages

Make the error messages consistent with other facilities. Include the
status code into exceptions where possible.

* More paranoid checks for empty data buffers

It is really important to verify that the vectors are non-empty
before taking a pointer to their first element (as in "&data[0]")
because that's undefined behavior if the vector is empty.

The same is true for vector's iterators (data_t::const_iterator). While
it tends to work with current libstdc++ implementations we still should
not dereference invalid iterators as that's undefined behavior as well.

* Improve iterator range validation

A likely error with iterators is accidentally misplacing begin and end
iterators in a pair. If we do a check like this then (end, begin) pair
will be considered valid. We should check that the "end" iterator comes
after the "begin" one instead (and that there's at least one element
between them).

This does not protect from all possible errors (e.g., using iterators
from two distinct containers), but it should be good enough for now.
  • Loading branch information
ilammy authored Feb 20, 2019
1 parent 7ce5d68 commit 29ebf63
Show file tree
Hide file tree
Showing 9 changed files with 365 additions and 93 deletions.
26 changes: 21 additions & 5 deletions src/wrappers/themis/themispp/exception.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,29 @@
#define THEMISPP_EXCEPTION_HPP_

#include <stdexcept>
#include <themis/themis.h>

namespace themispp{
class exception_t: public std::runtime_error{
public:
explicit exception_t(const char* what):
std::runtime_error(what){}
};
class exception_t: public std::runtime_error
{
public:
explicit exception_t(const char* what)
: std::runtime_error(what)
, status_(THEMIS_INVALID_PARAMETER)
{}

exception_t(const char* what, ::themis_status_t status)
: std::runtime_error(what)
, status_(status)
{}

::themis_status_t status() const {
return status_;
}

private:
::themis_status_t status_;
};
}//themispp ns

#endif
129 changes: 102 additions & 27 deletions src/wrappers/themis/themispp/secure_cell.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,22 @@ namespace themispp{

secure_cell_t(data_t::const_iterator password_begin, data_t::const_iterator password_end):
_password(password_begin, password_end),
_res(0){}
_res(0)
{
if(_password.empty()){
throw themispp::exception_t("Secure Cell must have non-empty password");
}
}

secure_cell_t(const data_t& password):
_password(password.begin(), password.end()),
_res(0){}

_res(0)
{
if(_password.empty()){
throw themispp::exception_t("Secure Cell must have non-empty password");
}
}

virtual const data_t& encrypt(data_t::const_iterator data_begin, data_t::const_iterator data_end, data_t::const_iterator context_begin, data_t::const_iterator context_end)=0;
virtual const data_t& decrypt(data_t::const_iterator data_begin, data_t::const_iterator data_end, data_t::const_iterator context_begin, data_t::const_iterator context_end)=0;

Expand Down Expand Up @@ -105,23 +115,43 @@ namespace themispp{
secure_cell_optional_context_t(password){}

const data_t& encrypt(data_t::const_iterator data_begin, data_t::const_iterator data_end, data_t::const_iterator context_begin, data_t::const_iterator context_end){
if(data_end<=data_begin){
throw themispp::exception_t("Secure Cell (Seal) failed to encrypt message: data must be non-empty");
}
themis_status_t status=THEMIS_FAIL;
const uint8_t* context_ptr=(context_end>context_begin) ? &(*context_begin) : NULL;
const size_t context_len=(context_end>context_begin) ? (context_end-context_begin) : 0;
size_t encrypted_length=0;
if(themis_secure_cell_encrypt_seal(&_password[0], _password.size(), &(*context_begin), context_end-context_begin, &(*data_begin), data_end-data_begin, NULL, &encrypted_length)!=THEMIS_BUFFER_TOO_SMALL)
throw themispp::exception_t("Secure Cell (Seal) failed encrypting");
status=themis_secure_cell_encrypt_seal(&_password[0], _password.size(), context_ptr, context_len, &(*data_begin), data_end-data_begin, NULL, &encrypted_length);
if(status!=THEMIS_BUFFER_TOO_SMALL){
throw themispp::exception_t("Secure Cell (Seal) failed to encrypt message", status);
}
_res.resize(encrypted_length);
if(themis_secure_cell_encrypt_seal(&_password[0], _password.size(), &(*context_begin), context_end-context_begin, &(*data_begin), data_end-data_begin, &_res[0], &encrypted_length)!=THEMIS_SUCCESS)
throw themispp::exception_t("Secure Cell (Seal) failed encrypting");
status=themis_secure_cell_encrypt_seal(&_password[0], _password.size(), context_ptr, context_len, &(*data_begin), data_end-data_begin, &_res[0], &encrypted_length);
if(status!=THEMIS_SUCCESS){
throw themispp::exception_t("Secure Cell (Seal) failed to encrypt message", status);
}
return _res;
}
using secure_cell_optional_context_t::encrypt;

const data_t& decrypt(data_t::const_iterator data_begin, data_t::const_iterator data_end, data_t::const_iterator context_begin, data_t::const_iterator context_end){
if(data_end<=data_begin){
throw themispp::exception_t("Secure Cell (Seal) failed to decrypt message: data must be non-empty");
}
themis_status_t status=THEMIS_FAIL;
const uint8_t* context_ptr=(context_end>context_begin) ? &(*context_begin) : NULL;
const size_t context_len=(context_end>context_begin) ? (context_end-context_begin) : 0;
size_t decrypted_length=0;
if(themis_secure_cell_decrypt_seal(&_password[0], _password.size(), &(*context_begin), context_end-context_begin, &(*data_begin), data_end-data_begin, NULL, &decrypted_length)!=THEMIS_BUFFER_TOO_SMALL)
throw themispp::exception_t("Secure Cell (Seal) failed decrypting");
status=themis_secure_cell_decrypt_seal(&_password[0], _password.size(), context_ptr, context_len, &(*data_begin), data_end-data_begin, NULL, &decrypted_length);
if(status!=THEMIS_BUFFER_TOO_SMALL){
throw themispp::exception_t("Secure Cell (Seal) failed to decrypt message", status);
}
_res.resize(decrypted_length);
if(themis_secure_cell_decrypt_seal(&_password[0], _password.size(), &(*context_begin), context_end-context_begin, &(*data_begin), data_end-data_begin, &_res[0], &decrypted_length)!=THEMIS_SUCCESS)
throw themispp::exception_t("Secure Cell (Seal) failed decrypting");
status=themis_secure_cell_decrypt_seal(&_password[0], _password.size(), context_ptr, context_len, &(*data_begin), data_end-data_begin, &_res[0], &decrypted_length);
if(status!=THEMIS_SUCCESS){
throw themispp::exception_t("Secure Cell (Seal) failed to decrypt message", status);
}
return _res;
}
using secure_cell_optional_context_t::decrypt;
Expand All @@ -138,25 +168,48 @@ namespace themispp{
_token(0){}

const data_t& encrypt(data_t::const_iterator data_begin, data_t::const_iterator data_end, data_t::const_iterator context_begin, data_t::const_iterator context_end){
if(data_end<=data_begin){
throw themispp::exception_t("Secure Cell (Token Protect) failed to encrypt message: data must be non-empty");
}
themis_status_t status=THEMIS_FAIL;
const uint8_t* context_ptr=(context_end>context_begin) ? &(*context_begin) : NULL;
const size_t context_len=(context_end>context_begin) ? (context_end-context_begin) : 0;
size_t encrypted_length=0;
size_t token_length=0;
if(themis_secure_cell_encrypt_token_protect(&_password[0], _password.size(), &(*context_begin), context_end-context_begin, &(*data_begin), data_end-data_begin, NULL, &token_length, NULL, &encrypted_length)!=THEMIS_BUFFER_TOO_SMALL)
throw themispp::exception_t("Secure Cell (Token Protect) failed encrypting");
status=themis_secure_cell_encrypt_token_protect(&_password[0], _password.size(), context_ptr, context_len, &(*data_begin), data_end-data_begin, NULL, &token_length, NULL, &encrypted_length);
if(status!=THEMIS_BUFFER_TOO_SMALL){
throw themispp::exception_t("Secure Cell (Token Protect) failed to encrypt message", status);
}
_res.resize(encrypted_length);
_token.resize(token_length);
if(themis_secure_cell_encrypt_token_protect(&_password[0], _password.size(), &(*context_begin), context_end-context_begin, &(*data_begin), data_end-data_begin, &_token[0], &token_length, &_res[0], &encrypted_length)!=THEMIS_SUCCESS)
throw themispp::exception_t("Secure Cell (Token Protect) failed encrypting");
status=themis_secure_cell_encrypt_token_protect(&_password[0], _password.size(), context_ptr, context_len, &(*data_begin), data_end-data_begin, &_token[0], &token_length, &_res[0], &encrypted_length);
if(status!=THEMIS_SUCCESS){
throw themispp::exception_t("Secure Cell (Token Protect) failed to encrypt message", status);
}
return _res;
}
using secure_cell_optional_context_t::encrypt;

const data_t& decrypt(data_t::const_iterator data_begin, data_t::const_iterator data_end, data_t::const_iterator context_begin, data_t::const_iterator context_end){
if(data_end<=data_begin){
throw themispp::exception_t("Secure Cell (Token Protect) failed to decrypt message: data must be non-empty");
}
if(_token.empty()){
throw themispp::exception_t("Secure Cell (Token Protect) failed to decrypt message: token must be non-empty (set with set_token())");
}
themis_status_t status=THEMIS_FAIL;
const uint8_t* context_ptr=(context_end>context_begin) ? &(*context_begin) : NULL;
const size_t context_len=(context_end>context_begin) ? (context_end-context_begin) : 0;
size_t decrypted_length=0;
if(themis_secure_cell_decrypt_token_protect(&_password[0], _password.size(), &(*context_begin), context_end-context_begin, &(*data_begin), data_end-data_begin, &_token[0], _token.size(), NULL, &decrypted_length)!=THEMIS_BUFFER_TOO_SMALL)
throw themispp::exception_t("Secure Cell (Token Protect) failed decrypting");
status=themis_secure_cell_decrypt_token_protect(&_password[0], _password.size(), context_ptr, context_len, &(*data_begin), data_end-data_begin, &_token[0], _token.size(), NULL, &decrypted_length);
if(status!=THEMIS_BUFFER_TOO_SMALL){
throw themispp::exception_t("Secure Cell (Token Protect) failed to decrypt message", status);
}
_res.resize(decrypted_length);
if(themis_secure_cell_decrypt_token_protect(&_password[0], _password.size(), &(*context_begin), context_end-context_begin, &(*data_begin), data_end-data_begin, &_token[0], _token.size(), &_res[0], &decrypted_length)!=THEMIS_SUCCESS)
throw themispp::exception_t("Secure Cell (Token Protect) failed decrypting");
status=themis_secure_cell_decrypt_token_protect(&_password[0], _password.size(), context_ptr, context_len, &(*data_begin), data_end-data_begin, &_token[0], _token.size(), &_res[0], &decrypted_length);
if(status!=THEMIS_SUCCESS){
throw themispp::exception_t("Secure Cell (Token Protect) failed to decrypt message", status);
}
return _res;
}

Expand All @@ -174,23 +227,45 @@ namespace themispp{
secure_cell_t(password){}

const data_t& encrypt(data_t::const_iterator data_begin, data_t::const_iterator data_end, data_t::const_iterator context_begin, data_t::const_iterator context_end){
if(data_end<=data_begin){
throw themispp::exception_t("Secure Cell (Context Imprint) failed to encrypt message: data must be non-empty");
}
if(context_end<=context_begin){
throw themispp::exception_t("Secure Cell (Context Imprint) failed to encrypt message: context must be non-empty");
}
themis_status_t status=THEMIS_FAIL;
size_t encrypted_length=0;
if(themis_secure_cell_encrypt_context_imprint(&_password[0], _password.size(), &(*context_begin), context_end-context_begin, &(*data_begin), data_end-data_begin, NULL, &encrypted_length)!=THEMIS_BUFFER_TOO_SMALL)
throw themispp::exception_t("Secure Cell (Context Imprint) failed encrypting");
status=themis_secure_cell_encrypt_context_imprint(&_password[0], _password.size(), &(*context_begin), context_end-context_begin, &(*data_begin), data_end-data_begin, NULL, &encrypted_length);
if(status!=THEMIS_BUFFER_TOO_SMALL){
throw themispp::exception_t("Secure Cell (Context Imprint) failed to encrypt message", status);
}
_res.resize(encrypted_length);
if(themis_secure_cell_encrypt_context_imprint(&_password[0], _password.size(), &(*context_begin), context_end-context_begin, &(*data_begin), data_end-data_begin, &_res[0], &encrypted_length)!=THEMIS_SUCCESS)
throw themispp::exception_t("Secure Cell (Context Imprint) failed encrypting");
status=themis_secure_cell_encrypt_context_imprint(&_password[0], _password.size(), &(*context_begin), context_end-context_begin, &(*data_begin), data_end-data_begin, &_res[0], &encrypted_length);
if(status!=THEMIS_SUCCESS){
throw themispp::exception_t("Secure Cell (Context Imprint) failed to encrypt message", status);
}
return _res;
}
using secure_cell_t::encrypt;

const data_t& decrypt(data_t::const_iterator data_begin, data_t::const_iterator data_end, data_t::const_iterator context_begin, data_t::const_iterator context_end){
if(data_end<=data_begin){
throw themispp::exception_t("Secure Cell (Context Imprint) failed to decrypt message: data must be non-empty");
}
if(context_end<=context_begin){
throw themispp::exception_t("Secure Cell (Context Imprint) failed to decrypt message: context must be non-empty");
}
themis_status_t status=THEMIS_FAIL;
size_t decrypted_length=0;
if(themis_secure_cell_decrypt_context_imprint(&_password[0], _password.size(), &(*context_begin), context_end-context_begin, &(*data_begin), data_end-data_begin, NULL, &decrypted_length)!=THEMIS_BUFFER_TOO_SMALL)
throw themispp::exception_t("Secure Cell (Context Imprint) failed decrypting");
status=themis_secure_cell_decrypt_context_imprint(&_password[0], _password.size(), &(*context_begin), context_end-context_begin, &(*data_begin), data_end-data_begin, NULL, &decrypted_length);
if(status!=THEMIS_BUFFER_TOO_SMALL){
throw themispp::exception_t("Secure Cell (Context Imprint) failed to decrypt message", status);
}
_res.resize(decrypted_length);
if(themis_secure_cell_decrypt_context_imprint(&_password[0], _password.size(), &(*context_begin), context_end-context_begin, &(*data_begin), data_end-data_begin, &_res[0], &decrypted_length)!=THEMIS_SUCCESS)
throw themispp::exception_t("Secure Cell (Context Imprint) failed decrypting");
status=themis_secure_cell_decrypt_context_imprint(&_password[0], _password.size(), &(*context_begin), context_end-context_begin, &(*data_begin), data_end-data_begin, &_res[0], &decrypted_length);
if(status!=THEMIS_SUCCESS){
throw themispp::exception_t("Secure Cell (Context Imprint) failed to decrypt message", status);
}
return _res;
}
using secure_cell_t::decrypt;
Expand Down
44 changes: 31 additions & 13 deletions src/wrappers/themis/themispp/secure_comparator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,36 +31,54 @@ namespace themispp{

secure_comparator_t(const data_t& shared_secret):
comparator_(NULL){
if(shared_secret.empty()){
throw themispp::exception_t("Secure Comparator must have non-empty shared secret");
}
res_.reserve(512);
comparator_=secure_comparator_create();
if(!comparator_)
throw themispp::exception_t("Secure Comparator failed creating");
if(THEMIS_SUCCESS!=secure_comparator_append_secret(comparator_, &shared_secret[0], shared_secret.size()))
throw themispp::exception_t("Secure Comparator failed appending secret");
if(!comparator_){
throw themispp::exception_t("Secure Comparator construction failed");
}
themis_status_t status=secure_comparator_append_secret(comparator_, &shared_secret[0], shared_secret.size());
if(THEMIS_SUCCESS!=status){
throw themispp::exception_t("Secure Comparator failed to append secret", status);
}
}

virtual ~secure_comparator_t(){
secure_comparator_destroy(comparator_);
}

const data_t& init(){
themis_status_t status=THEMIS_FAIL;
size_t data_length=0;
if(THEMIS_BUFFER_TOO_SMALL!=secure_comparator_begin_compare(comparator_, NULL, &data_length))
throw themispp::exception_t("Secure Comparator failed making initialisation message");
status=secure_comparator_begin_compare(comparator_, NULL, &data_length);
if(THEMIS_BUFFER_TOO_SMALL!=status){
throw themispp::exception_t("Secure Comparator failed to initialize comparison", status);
}
res_.resize(data_length);
if(THEMIS_SCOMPARE_SEND_OUTPUT_TO_PEER!=secure_comparator_begin_compare(comparator_, &res_[0], &data_length))
throw themispp::exception_t("Secure Comparator failed making initialisation message");
status=secure_comparator_begin_compare(comparator_, &res_[0], &data_length);
if(THEMIS_SCOMPARE_SEND_OUTPUT_TO_PEER!=status){
throw themispp::exception_t("Secure Comparator failed to initialize comparison", status);
}
return res_;
}

const data_t& proceed(const std::vector<uint8_t>& data){
if(data.empty()){
throw themispp::exception_t("Secure Comparator failed to proceed comparison: data must be non-empty");
}
themis_status_t status=THEMIS_FAIL;
size_t res_data_length=0;
if(THEMIS_BUFFER_TOO_SMALL!=secure_comparator_proceed_compare(comparator_, &data[0], data.size(), NULL, &res_data_length))
throw themispp::exception_t("Secure Comparator failed proceeding message");
status=secure_comparator_proceed_compare(comparator_, &data[0], data.size(), NULL, &res_data_length);
if(THEMIS_BUFFER_TOO_SMALL!=status){
throw themispp::exception_t("Secure Comparator failed to proceed comparison", status);
}
res_.resize(res_data_length);
themis_status_t res=secure_comparator_proceed_compare(comparator_, &data[0], data.size(), &res_[0], &res_data_length);
if(THEMIS_SCOMPARE_SEND_OUTPUT_TO_PEER!=res && THEMIS_SUCCESS!=res)
throw themispp::exception_t("Secure Comparator failed proceeding message");
status=secure_comparator_proceed_compare(comparator_, &data[0], data.size(), &res_[0], &res_data_length);
if(THEMIS_SCOMPARE_SEND_OUTPUT_TO_PEER!=status && THEMIS_SUCCESS!=status){
throw themispp::exception_t("Secure Comparator failed to proceed comparison", status);
}
return res_;
}

Expand Down
19 changes: 12 additions & 7 deletions src/wrappers/themis/themispp/secure_keygen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,24 @@ namespace themispp{
}

void gen(){
themis_status_t status=THEMIS_FAIL;
size_t private_key_length=max_key_length_t_p;
size_t public_key_length=max_key_length_t_p;
switch(alg_t_p){
case EC:
if(themis_gen_ec_key_pair(&private_key[0], &private_key_length, &public_key[0], &public_key_length)!=THEMIS_SUCCESS)
throw themispp::exception_t("Themis failed generating EC KeyPair");
break;
status=themis_gen_ec_key_pair(&private_key[0], &private_key_length, &public_key[0], &public_key_length);
if(status!=THEMIS_SUCCESS){
throw themispp::exception_t("Themis failed to generate EC key pair", status);
}
break;
case RSA:
if(themis_gen_rsa_key_pair(&private_key[0], &private_key_length, &public_key[0], &public_key_length)!=THEMIS_SUCCESS)
throw themispp::exception_t("Themis failed generating RSA KeyPair");
break;
status=themis_gen_rsa_key_pair(&private_key[0], &private_key_length, &public_key[0], &public_key_length);
if(status!=THEMIS_SUCCESS){
throw themispp::exception_t("Themis failed to generate RSA key pair", status);
}
break;
default:
throw themispp::exception_t("Themis failed generating KeyPair, unsupported algorithm");
throw themispp::exception_t("Themis failed generate key pair: unsupported algorithm");
}
private_key.resize(private_key_length);
public_key.resize(public_key_length);
Expand Down
Loading

0 comments on commit 29ebf63

Please sign in to comment.