Moved some parts to a static lib so both the executable and the tests can link to it.

Written additional tests.
This commit is contained in:
eelke 2017-02-26 19:29:50 +01:00
parent 0a809a7288
commit d0ea9dfa0c
39 changed files with 1767 additions and 493 deletions

6
core/Core.cpp Normal file
View file

@ -0,0 +1,6 @@
#include "Core.h"
Core::Core()
{
}

12
core/Core.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef CORE_H
#define CORE_H
class Core
{
public:
Core();
};
#endif // CORE_H

64
core/CsvWriter.cpp Normal file
View file

@ -0,0 +1,64 @@
#include "csvwriter.h"
CsvWriter::CsvWriter()
{}
CsvWriter::CsvWriter(QTextStream *output)
: m_output(output)
{}
void CsvWriter::setDestination(QTextStream *output)
{
m_output = output;
m_column = 0;
}
void CsvWriter::setSeperator(QChar ch)
{
m_seperator = ch;
}
void CsvWriter::setQuote(QChar ch)
{
m_quote = ch;
}
void CsvWriter::writeField(QString field)
{
QTextStream &out = *m_output;
if (m_column > 0) {
out << m_seperator;
}
// if field contains any of seperator, quote or newline then it needs to be quoted
// when quoted quotes need to be doubled to escape them
bool needs_quotes = false;
for (auto ch : field) {
if (ch == '\n' || ch == m_seperator || ch == m_quote) {
needs_quotes = true;
break;
}
}
if (needs_quotes) {
out << m_quote;
for (auto ch : field) {
if (ch == m_quote)
out << m_quote;
out << ch;
}
out << m_quote;
}
else {
out << field;
}
++m_column;
}
void CsvWriter::nextRow()
{
QTextStream &out = *m_output;
out << '\n';
m_column = 0;
}

23
core/CsvWriter.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef CSVWRITER_H
#define CSVWRITER_H
#include <ostream>
#include <QTextStream>
class CsvWriter {
public:
CsvWriter();
explicit CsvWriter(QTextStream *output);
void setDestination(QTextStream *output);
void setSeperator(QChar ch);
void setQuote(QChar ch);
void writeField(QString field);
void nextRow();
private:
QChar m_seperator = ',';
QChar m_quote = '"';
QTextStream *m_output = nullptr;
int m_column = 0;
};
#endif // CSVWRITER_H

269
core/Expected.h Normal file
View file

@ -0,0 +1,269 @@
#ifndef EXPECTED_H
#define EXPECTED_H
#include <exception>
template <typename T>
class Expected {
union {
T m_value;
std::exception_ptr m_error;
};
bool m_valid;
Expected() {} // internal use
public:
Expected(const T& rhs)
: m_value(rhs), m_valid(true)
{}
Expected(T&& rhs)
: m_value(std::move(rhs))
, m_valid(true)
{}
Expected(const Expected& rhs)
: m_valid(rhs.valid)
{
if (m_valid) {
new (&m_value) T(rhs.m_value);
}
else {
new (&m_error) std::exception_ptr(rhs.m_error);
}
}
Expected(Expected &&rhs)
: m_valid(rhs.m_valid)
{
if (m_valid) {
new (&m_value) T(std::move(rhs.m_value));
}
else {
new (&m_error) std::exception_ptr(std::move(rhs.m_error));
}
}
~Expected()
{
if (m_valid) {
m_value.~T();
}
else {
using std::exception_ptr;
m_error.~exception_ptr();
}
}
// void swap(Expected& rhs)
// {
// if (m_valid) {
// if (rhs.m_valid) {
// using std::swap;
// swap(m_value, rhs.m_value);
// }
// else {
// auto t = std::move(rhs.m_error);
// new(&rhs.m_value) T(std::move(m_value));
// new(&m_error) std::exception_ptr(t);
// std::swap(m_valid, rhs.m_valid);
// }
// }
// else {
// if (rhs.m_valid) {
// rhs.swap(*this);
// }
// else {
// using std::swap;
// swap(m_error, rhs.m_error);
// std::swap(m_valid, rhs.m_valid);
// }
// }
// }
template <class E>
static Expected<T> fromException(const E& exception)
{
if (typeid(exception) != typeid(E)) {
throw std::invalid_argument("slicing detected");
}
return fromException(std::make_exception_ptr(exception));
}
static Expected<T> fromException(std::exception_ptr p)
{
Expected<T> result;
result.m_valid = false;
new (&result.m_error) std::exception_ptr(std::move(p));
return result;
}
static Expected<T> fromException()
{
return fromException(std::current_exception());
}
bool valid() const
{
return m_valid;
}
T& get()
{
if (!m_valid) {
std::rethrow_exception(m_error);
}
return m_value;
}
const T& get() const
{
if (!m_valid) {
std::rethrow_exception(m_error);
}
return m_value;
}
template <class E>
bool hasException() const
{
try {
if (!m_valid) {
std::rethrow_exception(m_error);
}
}
catch (const E& ) {
return true;
}
catch (...) {
}
return false;
}
template <class F>
static Expected fromCode(F fun)
{
try {
return Expected(fun());
}
catch (...) {
return fromException();
}
}
};
template <>
class Expected<void> {
std::exception_ptr m_error;
bool m_valid;
public:
Expected<void>()
: m_valid(true)
{}
Expected(const Expected& rhs)
: m_valid(rhs.m_valid)
, m_error(rhs.m_error)
{}
Expected(Expected<void> &&rhs)
: m_valid(rhs.m_valid)
, m_error(std::move(rhs.m_error))
{}
~Expected()
{}
// void swap(Expected& rhs)
// {
// if (m_valid) {
// if (!rhs.m_valid) {
// m_error = rhs.m_error;
// std::swap(m_valid, rhs.m_valid);
// }
// }
// else {
// if (rhs.m_valid) {
// rhs.swap(*this);
// }
// else {
// using std::swap;
// swap(m_error, rhs.m_error);
// std::swap(m_valid, rhs.m_valid);
// }
// }
// }
template <class E>
static Expected<void> fromException(const E& exception)
{
if (typeid(exception) != typeid(E)) {
throw std::invalid_argument("slicing detected");
}
return fromException(std::make_exception_ptr(exception));
}
static Expected<void> fromException(std::exception_ptr p)
{
Expected<void> result;
result.m_valid = false;
result.m_error = std::exception_ptr(std::move(p));
return result;
}
static Expected<void> fromException()
{
return fromException(std::current_exception());
}
bool valid() const
{
return m_valid;
}
void get() const
{
if (!m_valid) {
std::rethrow_exception(m_error);
}
}
template <class E>
bool hasException() const
{
try {
if (!m_valid) {
std::rethrow_exception(m_error);
}
}
catch (const E& ) {
return true;
}
catch (...) {
}
return false;
}
template <class F>
static Expected fromCode(F fun)
{
try {
fun();
return Expected<void>();
}
catch (...) {
return fromException();
}
}
};
#endif // EXPECTED_H

244
core/PasswordManager.cpp Normal file
View file

@ -0,0 +1,244 @@
#include "PasswordManager.h"
#include <botan/filters.h>
#include <botan/pipe.h>
#include <botan/sha2_64.h>
#include <botan/hash.h>
#include <botan/hmac.h>
#include <botan/pbkdf2.h>
#include <botan/rng.h>
#include <botan/base64.h>
#include <botan/loadstor.h>
#include <botan/mem_ops.h>
#include <boost/assert.hpp>
#include "Core.h"
using namespace Botan;
namespace {
/*
First 24 bits of SHA-256("Botan Cryptobox"), followed by 8 0 bits
for later use as flags, etc if needed
*/
const uint8_t c_PasswordVersionCode = 0x10;
const size_t c_VersionCodeLen = 1;
const size_t CIPHER_KEY_LEN = 32;
const size_t CIPHER_IV_LEN = 16;
const size_t MAC_KEY_LEN = 32;
const size_t MAC_OUTPUT_LEN = 20;
const size_t PBKDF_SALT_LEN = 10;
const size_t PBKDF_ITERATIONS = 8 * 1024;
const size_t PBKDF_OUTPUT_LEN = CIPHER_KEY_LEN + CIPHER_IV_LEN + MAC_KEY_LEN;
const char * const c_Cipher = "Serpent/CTR-BE";
const char * const c_IniGroupSecurity = "Security";
StrengthenedKey generateKey(const std::string &passphrase, const uint8_t *salt, int saltlength)
{
PKCS5_PBKDF2 pbkdf(new HMAC(new SHA_512));
OctetString master_key = pbkdf.derive_key(
PBKDF_OUTPUT_LEN,
passphrase,
salt, saltlength,
PBKDF_ITERATIONS);
const uint8_t* mk = master_key.begin();
return StrengthenedKey(
SymmetricKey(mk, CIPHER_KEY_LEN),
SymmetricKey(mk + CIPHER_KEY_LEN, MAC_KEY_LEN),
InitializationVector(mk + CIPHER_KEY_LEN + MAC_KEY_LEN, CIPHER_IV_LEN));
}
// secure_vector<uint8_t> pbkdf_salt(PBKDF_SALT_LEN);
// rng.randomize( pbkdf_salt.data(), pbkdf_salt.size());
// StrengthenedKey strengthened_key = generateKey(passphrase, pbkdf_salt.data(), pbkdf_salt.size());
std::string encrypt(const std::string &input,
const StrengthenedKey &strengthened_key)
{
Pipe pipe(get_cipher(c_Cipher, strengthened_key.cipher_key,
strengthened_key.iv, ENCRYPTION),
new Fork(
nullptr,
new MAC_Filter(new HMAC(new SHA_512),
strengthened_key.mac_key, MAC_OUTPUT_LEN)));
pipe.process_msg((const uint8_t*)input.data(), input.length());
/*
Output format is:
mac (20 bytes)
ciphertext
*/
const size_t ciphertext_len = pipe.remaining(0);
std::vector<uint8_t> out_buf(MAC_OUTPUT_LEN + ciphertext_len);
BOTAN_ASSERT_EQUAL(
pipe.read(&out_buf[0], MAC_OUTPUT_LEN, 1),
MAC_OUTPUT_LEN, "MAC output");
BOTAN_ASSERT_EQUAL(
pipe.read(&out_buf[MAC_OUTPUT_LEN], ciphertext_len, 0),
ciphertext_len, "Ciphertext size");
return base64_encode(out_buf.data(), out_buf.size());
}
std::string decrypt(const std::string &input, const StrengthenedKey &strengthened_key)
{
secure_vector<uint8_t> ciphertext = base64_decode(input);
if(ciphertext.size() < (MAC_OUTPUT_LEN)) {
throw Decoding_Error("Invalid encrypted password input");
}
Pipe pipe(new Fork(
get_cipher(c_Cipher, strengthened_key.cipher_key, strengthened_key.iv, DECRYPTION),
new MAC_Filter(new HMAC(new SHA_512), strengthened_key.mac_key, MAC_OUTPUT_LEN)
));
const size_t ciphertext_offset = MAC_OUTPUT_LEN;
pipe.process_msg(&ciphertext[ciphertext_offset], ciphertext.size() - ciphertext_offset);
uint8_t computed_mac[MAC_OUTPUT_LEN];
BOTAN_ASSERT_EQUAL(MAC_OUTPUT_LEN, pipe.read(computed_mac, MAC_OUTPUT_LEN, 1), "MAC size");
if(!same_mem(computed_mac, &ciphertext[0], MAC_OUTPUT_LEN)) {
throw Decoding_Error("Encrypted password integrity failure");
}
return pipe.read_all_as_string(0);
}
struct constants {
const int pbkdf_salt_len;
};
constants v1_consts = {
10
};
} // end of unnamed namespace
/*
* File layout:
*
* Header
* version
* key_salt
* hash_salt
* master_hash
*
*
* Passwords
* key = pw
*/
PasswordManager::PasswordManager()
{
}
Expected<bool> PasswordManager::unlock(const std::string &master_password)
{
try {
bool result = false;
if (m_masterHash.length() == 0 && master_password.empty()) {
result = true;
} else {
StrengthenedKey key = generateKey(master_password, m_keySalt.begin(), m_keySalt.length());
OctetString hash = hashStrengthenedKey(key, m_hashSalt);
BOOST_ASSERT_MSG(hash.length() == m_masterHash.length(), "Both hashes should have the same length! Versioning error?");
if (same_mem(m_masterHash.begin(), hash.begin(), hash.length())) {
result = true;
m_masterKey = key;
}
}
return result;
} catch (...) {
return Expected<bool>::fromException();
}
}
Expected<bool> PasswordManager::changeMasterPassword(const std::string &old_master_password,
const std::string &new_master_password)
{
try {
bool result = false;
if (m_masterHash.length() == 0 && old_master_password.empty()) {
// Nothing set yet so we initialize for first use
m_keySalt = OctetString(m_rng, v1_consts.pbkdf_salt_len);
m_masterKey = generateKey(new_master_password, m_keySalt.begin(), m_keySalt.length());
m_hashSalt = OctetString(m_rng, v1_consts.pbkdf_salt_len);
m_masterHash = hashStrengthenedKey(m_masterKey, m_hashSalt);
result = true;
}
return result;
} catch (...) {
return Expected<bool>::fromException();
}
}
void PasswordManager::lock()
{
m_masterKey = StrengthenedKey();
}
bool PasswordManager::locked() const
{
return m_masterKey.cipher_key.size() == 0;
}
Expected<void> PasswordManager::savePassword(const std::string &key, const std::string &password)
{
if (locked()) {
return Expected<void>::fromException(std::logic_error("Need to unlock the password manager first"));
}
std::string epw = encrypt(password, m_masterKey);
m_store.emplace(key, epw);
return Expected<void>();
}
Expected<bool> PasswordManager::getPassword(const std::string &key, std::string &out)
{
if (locked()) {
return Expected<bool>::fromException(std::logic_error("Need to unlock the password manager first"));
}
auto fi = m_store.find(key);
bool result = false;
if (fi != m_store.end()) {
out = decrypt(fi->second, m_masterKey);
result = true;
}
return result;
}
Botan::OctetString PasswordManager::hashStrengthenedKey(const StrengthenedKey &key, const OctetString &salt)
{
std::unique_ptr<Botan::HashFunction> hash3(Botan::HashFunction::create("SHA-3"));
BOOST_ASSERT_MSG(hash3 != nullptr, "SHA-3 algorithm not available");
hash3->update(salt.begin(), salt.length());
hash3->update(key.cipher_key.begin(), key.cipher_key.length());
hash3->update(key.mac_key.begin(), key.mac_key.length());
hash3->update(key.iv.begin(), key.iv.length());
return hash3->final();
}

66
core/PasswordManager.h Normal file
View file

@ -0,0 +1,66 @@
#ifndef PASSWORDMANAGER_H
#define PASSWORDMANAGER_H
#include "Expected.h"
#include <string>
#include <botan/botan.h>
#include <botan/symkey.h>
#include <map>
struct StrengthenedKey {
Botan::SymmetricKey cipher_key;
Botan::SymmetricKey mac_key;
Botan::InitializationVector iv;
StrengthenedKey() {}
StrengthenedKey(const Botan::SymmetricKey &ck, const Botan::SymmetricKey &mk,
const Botan::InitializationVector &i)
: cipher_key(ck)
, mac_key(mk)
, iv(i)
{}
};
class PasswordManager {
public:
// static PasswordManager create(const std::string &file_name);
PasswordManager();
/** Unlocks the passwords of the connections.
*
* \return Normally it return a bool specifying if the password was accepted.
* on rare occasions it could return an error.
*/
Expected<bool> unlock(const std::string &master_password);
Expected<bool> changeMasterPassword(const std::string &master_password,
const std::string &new_master_password);
/** Forget master password
*/
void lock();
bool locked() const;
Expected<void> savePassword(const std::string &key, const std::string &password);
Expected<bool> getPassword(const std::string &key, std::string &out);
private:
Botan::AutoSeeded_RNG m_rng;
Botan::OctetString m_keySalt; // salt for generating crypto key
StrengthenedKey m_masterKey; // crypto key
Botan::OctetString m_hashSalt; // salt of the hash of the passphrase
Botan::OctetString m_masterHash; // hash for checking the passphrase
using t_KeyPasswords = std::map<std::string, std::string>;
t_KeyPasswords m_store;
static Botan::OctetString hashStrengthenedKey(const StrengthenedKey &key, const Botan::OctetString &salt);
};
#endif // PASSWORDMANAGER_H

69
core/ScopeGuard.h Normal file
View file

@ -0,0 +1,69 @@
#ifndef SCOPEGUARD_H
#define SCOPEGUARD_H
/** \brief Template class for executing code at scope exit.
*
* By default the object will be an active mode and execute the function
* passed to the constructor when the object is destructed. You can however
* cancel this action by calling dismiss().
*
* There is a clever macro that allows you to write something like
* SCOPE_EXIT { foo(); };
*/
template<class Fun>
class ScopeGuard {
Fun f_;
bool active_;
public:
ScopeGuard(Fun f)
: f_(std::move(f))
, active_(true) {
}
~ScopeGuard() { if(active_) f_(); }
void dismiss() { active_=false; }
ScopeGuard() = delete;
ScopeGuard(const ScopeGuard&) = delete;
ScopeGuard& operator=(const ScopeGuard&) = delete;
ScopeGuard(ScopeGuard&& rhs)
: f_(std::move(rhs.f_))
, active_(rhs.active_)
{
rhs.dismiss();
}
};
template<class Fun>
ScopeGuard<Fun> scopeGuard(Fun f)
{
return ScopeGuard<Fun>(std::move(f));
}
namespace ScopeGuard_detail {
enum class ScopeGuardOnExit {};
template<typename Fun>
ScopeGuard<Fun> operator+(ScopeGuardOnExit, Fun&& fn) {
return ScopeGuard<Fun>(std::forward<Fun>(fn));
}
}
#define CONCATENATE_IMPL(s1, s2) s1##s2
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
#ifdef __COUNTER__
#define ANONYMOUS_VARIABLE(str) \
CONCATENATE(str,__COUNTER__)
#else
#define ANONYMOUS_VARIABLE(str) \
CONCATENATE(str,__LINE__)
#endif
#define SCOPE_EXIT \
auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \
= ::ScopeGuard_detail::ScopeGuardOnExit() + [&]()
#endif // SCOPEGUARD_H

162
core/SqlLexer.cpp Normal file
View file

@ -0,0 +1,162 @@
#include "SqlLexer.h"
SqlLexer::SqlLexer(const QString &block, LexerState currentstate)
: m_block(block)
, m_state(currentstate)
{}
QChar SqlLexer::nextChar()
{
QChar result = QChar::Null;
if (m_pos < m_block.size()) {
result = m_block.at(m_pos++);
}
return result;
}
QChar SqlLexer::peekChar()
{
QChar result = QChar::Null;
if (m_pos < m_block.size()) {
result = m_block.at(m_pos);
}
return result;
}
/**
* @brief NextBasicToken
* @param in
* @param ofs
* @param start
* @param length
* @return false when input seems invalid, it will return what it did recognize but something wasn't right, parser should try to recover
*/
bool SqlLexer::nextBasicToken(int &startpos, int &length, BasicTokenType &tokentype, QString &out)
{
// Basically chops based on white space
// it does also recognize comments and quoted strings/identifiers
while (true) {
startpos = m_pos;
QChar c = nextChar();
// if (LexerState::Null == m_state) {
if (c.isSpace()) {
// Just skip whitespace
}
else if (c == '-' && peekChar() == '-') { // two dashes, start of comment
// Loop till end of line or end of block
c = nextChar();
for (;;) {
c = peekChar();
if (c != QChar::Null && c != '\n')
nextChar();
else
break;
}
length = m_pos - startpos;
tokentype = BasicTokenType::Comment;
return true;
}
else if (c == '\'') {
// Single quoted string so it's an SQL text literal
while (true) {
c = peekChar();
if (c == QChar::Null || c == '\n') {
// unexpected end, pretend nothings wrong
length = m_pos - startpos;
tokentype = BasicTokenType::QuotedString;
return true;
}
else {
nextChar();
if (c == '\'') {
// maybe end of string literal
if (peekChar() == '\'') {
// Nope, just double quote to escape quote
nextChar(); // eat it
}
else {
length = m_pos - startpos;
tokentype = BasicTokenType::QuotedString;
return true;
}
}
}
}
}
else if (c == '"') {
// Double quoted identifier
while (true) {
c = peekChar();
if (c == QChar::Null || c == '\n') {
// unexpected end, pretend nothings wrong
length = m_pos - startpos;
tokentype = BasicTokenType::QuotedIdentifier;
return true;
}
else {
nextChar();
if (c == '"') {
// maybe end of string literal
if (peekChar() == '"') {
// Nope, just double quote to escape quote
nextChar(); // eat it
}
else {
length = m_pos - startpos;
tokentype = BasicTokenType::QuotedIdentifier;
return true;
}
}
}
}
}
// else if (c == '/' && peekChar() == '*') {
// nextChar();
// m_state = LexerState::InBlockComment;
// }
else if (c == QChar::Null) {
break;
}
else {
// Undetermined symbol
for (;;) {
c = peekChar();
if (c.isLetterOrNumber() || c == '_')
nextChar();
else
break;
}
length = m_pos - startpos;
tokentype = BasicTokenType::Symbol;
QStringRef sr(&m_block, startpos, length);
out = sr.toString();
return true;
}
// }
// else if (LexerState::InBlockComment == m_state) {
// if (c == QChar::Null) {
// // eof current buffer, we need to return state so
// if (m_pos == startpos) {
// break;
// }
// else {
// length = m_pos - startpos;
// tokentype = BasicTokenType::OpenBlockComment;
// return true;
// }
// }
// else if (c == '*') {
// nextChar();
// if (peekChar() == '/') {
// nextChar();
// length = m_pos - startpos;
// tokentype = BasicTokenType::BlockComment;
// m_state = LexerState::Null;
// return true;
// }
// }
// }
}
return false;
}

49
core/SqlLexer.h Normal file
View file

@ -0,0 +1,49 @@
#ifndef SQLLEXER_H
#define SQLLEXER_H
#include <QString>
enum class BasicTokenType {
None,
End, // End of input
Symbol, // can be many things, keyword, object name, operator, ..
Comment,
BlockComment,
OpenBlockComment, // Busy with a block comment end not detected before end of current input
QuotedString,
DollarQuotedString,
QuotedIdentifier
};
enum class LexerState {
Null,
InDollarQuotedString,
InBlockComment
};
class SqlLexer {
public:
SqlLexer(const QString &block, LexerState currentstate);
QChar nextChar();
QChar peekChar();
/**
* @brief NextBasicToken
* @param in
* @param ofs
* @param start
* @param length
* @return false when input seems invalid, it will return what it did recognize but something wasn't right, parser should try to recover
*/
bool nextBasicToken(int &startpos, int &length, BasicTokenType &tokentype, QString &out);
LexerState currentState() const { return m_state; }
private:
QString m_block;
int m_pos = 0;
LexerState m_state;
};
#endif // SQLLEXER_H

43
core/core.pro Normal file
View file

@ -0,0 +1,43 @@
#-------------------------------------------------
#
# Project created by QtCreator 2017-02-26T10:51:14
#
#-------------------------------------------------
QT -= gui
TARGET = core
TEMPLATE = lib
CONFIG += staticlib c++11
INCLUDEPATH += C:\prog\include C:\VSproj\boost_1_63_0
DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX
LIBS += /LIBPATH:C:\VSproj\boost_1_63_0\stage\lib /LIBPATH:c:\prog\lib\ libpq.lib fmt.lib User32.lib ws2_32.lib
# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += Core.cpp \
my_boost_assert_handler.cpp \
SqlLexer.cpp \
PasswordManager.cpp \
CsvWriter.cpp
HEADERS += Core.h \
PasswordManager.h \
SqlLexer.h \
ScopeGuard.h \
CsvWriter.h
unix {
target.path = /usr/lib
INSTALLS += target
}

View file

@ -0,0 +1,20 @@
#include <sstream>
#include <stdexcept>
namespace boost
{
void assertion_failed(char const * expr, char const * function, char const * file, long line)
{
std::ostringstream out;
out << "Assertion failure int " << function << " " << file << ":" << line;
throw std::runtime_error(out.str());
}
void assertion_failed_msg(char const * expr, char const * msg, char const * function, char const * file, long line)
{
std::ostringstream out;
out << "Assertion failure int " << function << " " << file << ":" << line << "\n"<< msg;
throw std::runtime_error(out.str());
}
}