Skip to content
Snippets Groups Projects
Verified Commit 7be42844 authored by Daniel Lyubomirov's avatar Daniel Lyubomirov
Browse files

[17] tests, fixes and docs

parent 42f4aa96
No related branches found
No related tags found
1 merge request!97Crypto Storage and LoginWithNewDevice and LoginWithPreviouslyAddedDevice APIs
Showing
with 841 additions and 60 deletions
......@@ -71,6 +71,7 @@ set(VEREIGNLIB_SRC
vereign/encoding/base64.cc
vereign/encoding/hex.cc
vereign/crypto/rand.cc
vereign/crypto/aes.cc
vereign/crypto/rsa.cc
vereign/crypto/bio.cc
......@@ -80,6 +81,7 @@ set(VEREIGNLIB_SRC
vereign/kvstore/detail/base_crypto_storage.cc
vereign/kvstore/sqlite_storage.cc
vereign/kvstore/crypto_storage.cc
vereign/kvstore/detail/value_encoder.cc
vereign/identity/provider.cc
......
#include "vereign/crypto/rand.hh"
#include "vereign/kvstore/sqlite_storage.hh"
#include <boost/core/ignore_unused.hpp>
#include <iostream>
#include <boost/filesystem.hpp>
#include <openssl/digest.h>
#include <openssl/evp.h>
#include <vereign/core/string.hh>
#include <vereign/bytes/view.hh>
#include <vereign/bytes/view_dump.hh>
#include <vereign/fs/util.hh>
#include <vereign/fs/path.hh>
#include <vereign/fs/operations.hh>
using namespace vereign;
auto main(int argc, char** argv) -> int {
boost::ignore_unused(argc);
boost::ignore_unused(argv);
argc = 0;
argv = nullptr;
return 0;
}
#include <memory>
#include <new>
#include <stdexcept>
#include <vereign/bytes/buffer.hh>
#include <vereign/bytes/errors.hh>
#include <memory>
namespace vereign::bytes {
Buffer::Buffer() noexcept
......@@ -111,9 +112,9 @@ void Buffer::Reserve(std::size_t size) {
return;
}
auto cap = cap_;
while (size > cap - size_) {
cap = cap * 2;
auto cap = cap_ * 2;
if (size > cap - size_) {
cap = size_ + size;
}
auto newData = reinterpret_cast<uint8_t*>(std::realloc(data_, cap));
......@@ -129,12 +130,12 @@ void Buffer::Reset() {
size_ = 0;
}
void Buffer::IncSize(std::size_t size) {
if (size_ + size > cap_) {
throw std::runtime_error("cannot increment size pass the capacity");
void Buffer::IncSize(std::size_t val) {
if (size_ + val > cap_) {
throw IncrementOutOfBounds{};
}
size_ += size;
size_ += val;
}
auto Buffer::WriteWithinCap(bytes::View src) noexcept -> std::size_t {
......@@ -162,7 +163,7 @@ auto Buffer::View(std::size_t start) const noexcept -> bytes::View {
auto Buffer::operator[](std::size_t index) -> uint8_t& {
if (index >= cap_) {
throw std::runtime_error("index out of bounds");
throw IndexOutOfBounds{};
}
return data_[index];
......@@ -170,7 +171,7 @@ auto Buffer::operator[](std::size_t index) -> uint8_t& {
auto Buffer::operator[](std::size_t index) const -> const uint8_t& {
if (index >= cap_) {
throw std::runtime_error("index out of bounds");
throw IndexOutOfBounds{};
}
return data_[index];
......
......@@ -6,12 +6,45 @@
namespace vereign::bytes {
/**
* Dynamically expandable memory buffer.
*
* The buffer is a 3-tuple - pointer, size and capacity.
* Typically used in functions for output parameters and return values.
* Provides API that is easy to use with C APIs.
*
* The buffer is move only.
*/
class Buffer {
public:
/**
* Creates empty buffer.
*/
Buffer() noexcept;
/**
* Creates a buffer with reserved memory capacity.
*
* The size of the buffer is zero.
*
* @param cap The capacity of the buffer.
*
* @throws std::bad_alloc when memory reservation fails.
*/
Buffer(std::size_t cap);
/**
* Creates a buffer by copying from source bytes view.
*
* @param src The source that will be copied from.
*
* @throws std::bad_alloc when memory reservation fails.
*/
Buffer(View src);
/**
* The buffer is movable.
*/
Buffer(Buffer&& other) noexcept;
auto operator=(Buffer&& other) noexcept -> Buffer&;
......@@ -19,28 +52,216 @@ public:
Buffer(const Buffer&) = delete;
auto operator=(const Buffer&) -> Buffer& = delete;
/**
* Frees the buffer memory.
*/
~Buffer();
/**
* Returns a pointer to the first byte of the buffer.
*
* Buffer::begin(), Buffer::end() pair is useful for range loops.
*
* Example:
* @code
* auto buf = bytes::Buffer{bytes::View("foo")};
* for (const auto& byte : buf) {
* byte = 'x';
* }
*
* assert(buf.View().String() == "xxx");
* @endcode
*/
auto begin() noexcept -> uint8_t*;
/**
* Returns a pointer to the first byte of the buffer.
*
* Buffer::begin(), Buffer::end() pair is useful for range loops.
*
* Example:
* @code
* auto buf = bytes::Buffer{bytes::View("foo bar")};
* std::string s;
*
* for (const auto& byte : buf) {
* s += byte;
* }
*
* assert(s == "foo bar");
* @endcode
*/
auto begin() const noexcept -> const uint8_t*;
/**
* Returns a pointer to the byte following the last byte in the buffer.
*
* Note that this is the last byte in the range [0, size).
* It is often used when calling C APIs.
*
* Example:
* @code
* auto buf = bytes::Buffer{bytes::View("foo bar")};
*
* buf.Reserve(4);
* std::strncpy((char*)buf.end(), " baz", 4);
* buf.IncSize(4);
*
* assert(buf.View().String() == "foo bar baz");
* @endcode
*/
auto end() noexcept -> uint8_t*;
/**
* Returns a read only pointer to the byte following the last byte in the buffer.
*
* Note that this is the last byte in the range [0, size).
*/
auto end() const noexcept -> const uint8_t*;
/**
* Access a byte in the range of [0, cap).
*
* @param index The index of the byte to access.
*
* @throws std::runtime_error when the passed index is out of bounds.
*/
auto operator[](std::size_t index) -> uint8_t&;
/**
* Read only access a byte in the range of [0, cap).
*
* @param index The index of the byte to access.
*
* @throws std::runtime_error when the passed index is out of bounds.
*/
auto operator[](std::size_t index) const -> const uint8_t&;
/**
* Retrieve buffer size.
*
* @returns the buffer size.
*/
auto Size() const noexcept -> std::size_t;
/**
* Retrieve buffer capacity.
*
* @returns the buffer capacity.
*/
auto Cap() const noexcept -> std::size_t;
/**
* Reserve memory, so that there is at least `size` free capacity.
*
* If there is already enough free capacity, no memory allocation is done.
* The allocated memory may be bigger than what is needed.
*
* If the call is successful then it is guaranteed that `this->FreeCap() >= size`.
*
* Example:
* @code
* auto buf = bytes::Buffer{bytes::View("foo bar")};
*
* buf.Reserve(4); // ensure there will be a free capacity for 4 bytes
* std::strncpy((char*)buf.end(), " baz", 4); // copy the bytes
* buf.IncSize(4); // update the buffer size with the newly written bytes
*
* assert(buf.View().String() == "foo bar baz");
* @endcode
*
* @param size The desired free capacity.
*
* @throws std::bad_alloc when memory allocation fails.
*/
void Reserve(std::size_t size);
/**
* Sets the buffer size to zero.
*
* This does not free any memory, so the capacity stays the intact, and the buffer can be reused.
*/
void Reset();
void IncSize(std::size_t size);
/**
* Increments the size of the buffer.
*
* It is typically used after some function has written bytes to the end of the buffer.
*
* @param val The value that will be added to the current size.
*/
void IncSize(std::size_t val);
/**
* Retrieve the buffer free capacity.
*
* This is equal to `this->Cap() - this->Size()`.
*/
auto FreeCap() const noexcept -> std::size_t;
/**
* Adds bytes up to the currently available buffer capacity.
*
* After the operation succeeds, the buffer size will be incremented with the number of bytes
* that have been copied.
*
* Example:
* @code
* auto buf = bytes::Buffer{3};
* buf.WriteWithinCap(bytes::View("foo bar"));
*
* // only 3 bytes are written
* assert(buf.View.String() == "foo");
* @endcode
*
* @param src The source that will be appended to the buffer.
* @returns The amount of bytes that were actually copied into the buffer.
*/
auto WriteWithinCap(bytes::View src) noexcept -> std::size_t;
/**
* Adds a source view of bytes to the buffer.
*
* If the buffer does not have enough capacity, it will be expanded.
*
* Example:
* @code
* auto buf = bytes::Buffer{3};
* buf.WriteWithinCap(bytes::View("foo bar"));
*
* // all bytes are written
* assert(buf.View.String() == "foo bar");
* @endcode
*
* @param The source that will be appended to the buffer.
* @returns The amount of bytes that were copied into the buffer. That is equal to src.Size().
*/
auto Write(bytes::View src) -> std::size_t;
/**
* Retrieve a read only view of the buffer.
*
* Example:
* @code
* auto buf = bytes::Buffer{bytes::View("123")};
* assert(buf.View().String() == "123");
* @endcode
*
* @returns a read only view range [0, this->Size()).
*/
auto View() const noexcept -> bytes::View;
/**
* Retrieve a read only view of the buffer staring from a given offset.
*
* Example:
* @code
* auto buf = bytes::Buffer{bytes::View("123")};
* assert(buf.View(1).String() == "23");
* @endcode
*
* @returns a read only view range [start, this->Size()).
*/
auto View(std::size_t start) const noexcept -> bytes::View;
private:
......
#ifndef __VEREIGN_BYTES_ERRORS_HH
#define __VEREIGN_BYTES_ERRORS_HH
#include <stdexcept>
namespace vereign::bytes {
class Error : public std::runtime_error {
public:
Error(const std::string& what)
: std::runtime_error{what}
{
}
};
class IndexOutOfBounds : public Error {
public:
IndexOutOfBounds()
: Error{"index out of bounds"}
{
}
};
class IncrementOutOfBounds : public Error {
public:
IncrementOutOfBounds()
: Error{"cannot increment size pass the capacity"}
{
}
};
} // namespace vereign::bytes
#endif // __VEREIGN_BYTES_ERRORS_HH
#ifndef __VEREIGN_BYTES_VIEW_HH
#define __VEREIGN_BYTES_VIEW_HH
#include <vereign/bytes/errors.hh>
#include <cstring>
#include <string>
#include <string_view>
......@@ -9,37 +11,79 @@
namespace vereign::bytes {
/**
* Bytes view represents a read only access to a range of bytes.
*
* The View is a 2-tuple with pointer and size.
* Typically used in functions for input parameters.
*
* **NOTE: The View does not own the memory that it references.**
*/
class View {
public:
/**
* Creates empty view.
*/
View() = default;
/**
* Creates a view from raw pointer and a size.
*
* @param data Pointer to the memory.
* @param size The size of the memory.
*/
View(const uint8_t* data, std::size_t size) noexcept
: size_{size},
data_{data}
{
}
/**
* Create a view from a string view.
*
* @param str The input string view.
*/
View(std::string_view str) noexcept
: size_{str.length()},
data_{str.length() > 0 ? reinterpret_cast<const uint8_t*>(str.data()): nullptr}
{
}
/**
* Creates a view from wide string view.
*
* @param str The input string.
*/
View(std::wstring_view str) noexcept
: size_{str.length() * sizeof(wchar_t)},
data_{size_ > 0 ? reinterpret_cast<const uint8_t*>(str.data()): nullptr}
{
}
/**
* Creates a view from void pointer and a size.
*
* @param data Pointer to the memory.
* @param size The size of the memory.
*/
View(const void* ptr, std::size_t size) noexcept
: size_{size},
data_{static_cast<const uint8_t*>(ptr)}
{
}
// default copyable.
View(const View&) = default;
auto operator=(const View&) -> View& = default;
/**
* Slice returns a new view in the interval [start, size).
*
* If the start is bigger than the size of the slice it returns empty view.
*
* @param start The beginning of the new View.
* @returns a new view in the interval [start, size).
*/
auto Slice(std::size_t start) const -> View {
if (start >= size_) {
return View(data_, 0);
......@@ -48,6 +92,15 @@ public:
return View(data_ + start, size_ - start);
}
/**
* Slice returns a new view in the interval [start, end).
*
* If the start is bigger than the size of the slice it returns empty view.
* If the end is bigger than the size of the slice it returns [start, size).
*
* @param start The beginning of the new View.
* @returns a new view in the interval [start, size).
*/
auto Slice(std::size_t start, std::size_t end) const -> View {
if (start >= size_) {
return View(data_, 0);
......@@ -56,22 +109,65 @@ public:
return View(data_ + start, std::min(size_, end) - start);
}
/**
* Retrieve a pointer to the data.
*
* @returns a pointer to the data.
*/
auto Data() const noexcept -> const uint8_t* {
return data_;
}
/**
* Retrieve a char pointer to the data.
*
* @returns a char pointer to the data.
*/
auto CharData() const noexcept -> const char* {
return reinterpret_cast<const char*>(data_);
}
/**
* Retrieve a wide char pointer to the data.
*
* @returns a wide char pointer to the data.
*/
auto WideCharData() const noexcept -> const wchar_t* {
return reinterpret_cast<const wchar_t*>(data_);
}
/**
* Retrieve view size.
*
* @returns view size.
*/
auto Size() const noexcept -> std::size_t {
return size_;
}
/**
* Retrieve a string view of the data.
*
* @returns a string view of the data.
*/
auto String() const noexcept -> std::string_view {
return std::string_view{CharData(), size_};
}
/**
* Retrieve a wide string view of the data.
*
* @returns a wide string view of the data.
*/
auto WideString() const noexcept -> std::wstring_view {
return std::wstring_view{WideCharData(), size_/sizeof(wchar_t)};
}
/**
* Binary compare the contents of two views.
*
* @returns true if the views are of the same size and if all the bytes in the two views are equal.
*/
auto operator==(View other) const noexcept -> bool {
if (size_ != other.size_) {
return false;
......@@ -80,13 +176,26 @@ public:
return std::memcmp(data_, other.data_, size_) == 0;
}
/**
* Binary compare the contents of two views.
*
* @returns true if the views are of different size or if the bytes in the two views are not equal.
*/
auto operator!=(View other) const noexcept -> bool {
return !(*this == other);
}
/**
* Access a single byte in the view.
*
* @param index The index of the byte that will be returned.
* @returns the byte at the specified index.
*
* @throws bytes::IndexOutOfBounds when the index is out of bounds.
*/
auto operator[](std::size_t index) const -> const uint8_t& {
if (index >= size_ ) {
throw std::runtime_error("index out of bounds");
throw IndexOutOfBounds{};
}
return data_[index];
......
......@@ -19,11 +19,11 @@ void GCM256Encrypt(
bytes::View src,
bytes::View key,
bytes::Buffer& iv,
bytes::Buffer& encrypted,
bytes::Buffer& tag
bytes::Buffer& tag,
bytes::Buffer& encrypted
) {
iv.Reserve(gcmIVSizeBytes);
crypto::Rand(iv);
crypto::Rand(iv, gcmIVSizeBytes);
encrypted.Reserve(src.Size() + aes256BlockSizeBytes);
......@@ -61,7 +61,7 @@ void GCM256Encrypt(
encrypted.IncSize(bytes_written);
tag.Reserve(gcmTagSizeBytes);
r = EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, tag.FreeCap(), tag.end());
r = EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, gcmTagSizeBytes, tag.end());
if (r != 1) {
throw OpenSSLError("getting GCM tag failed");
}
......
#ifndef __VEREIGN_CRYPTO_AES_HH
#define __VEREIGN_CRYPTO_AES_HH
#include "vereign/bytes/view.hh"
#include <vereign/bytes/buffer.hh>
/**
* Provides utilities for AES encryption/decryption.
*/
namespace vereign::crypto::aes {
/**
* Encrypt given bytes with AES256-GCM.
*
* Example:
* @code
* const std::string input{"foo bar"};
* auto key = crypto::Rand(32); // 256 bits
*
* bytes::Buffer iv;
* bytes::Buffer tag;
* bytes::Buffer encrypted;
*
* crypto::aes::GCM256Encrypt(bytes::View(input), key.View(), iv, tag, encrypted);
* @endcode
*
* @param src The bytes that will be encrypted.
* @param key The AES 256 bit key.
* @param iv The initialization vector that was used during the encryption.
* @param tag The authentication tag that was produced during the encryption.
* @param encrypted The result of the encryption.
*
* @throws crypto::OpenSSLError on failure.
*/
void GCM256Encrypt(
bytes::View src,
bytes::View key,
bytes::Buffer& iv,
bytes::Buffer& encrypted,
bytes::Buffer& tag
bytes::Buffer& tag,
bytes::Buffer& encrypted
);
/**
* Decrypts given bytes with AES256-GCM.
*
* Example:
* @code
* const std::string input{"foo bar"};
* auto key = crypto::Rand(32); // 256 bits
*
* bytes::Buffer iv;
* bytes::Buffer tag;
* bytes::Buffer encrypted;
*
* crypto::aes::GCM256Encrypt(bytes::View(input), key.View(), iv, tag, encrypted);
*
* bytes::Buffer decrypted;
* crypto::aes::GCM256Decrypt(encrypted.View(), key.View(), iv.View(), tag.View(), decrypted);
*
* assert(input == decrypted.View().String());
* @endcode
*
* @param src The bytes that will be decrypted.
* @param key The AES 256 bit key.
* @param iv The initialization vector that was used during the encryption.
* @param tag The authentication tag that was produced during the encryption.
* @param encrypted The result of the decryption.
*
* @throws crypto::OpenSSLError on failure.
*/
void GCM256Decrypt(
bytes::View src,
bytes::View key,
......
......@@ -6,6 +6,12 @@
namespace vereign::crypto::bio {
/**
* Creates a bytes::View for given OpenSSL BIO.
*
* @param bio The input BIO.
* @returns a bytes view of OpenSSL BIO.
*/
auto View(const BIO* bio) -> bytes::View;
} // vereign::crypto::bio
......
......@@ -5,6 +5,22 @@
namespace vereign::crypto::digest {
/**
* Creates a SHA1 hash of given bytes.
*
* Example:
* @code
* const std::string input{"foo bar"};
*
* bytes::Buffer hash;
* crypto::digest::sha1(bytes::View(data), hash);
* @endcode
*
* @param src The input bytes.
* @param result The SHA1 hash of the input.
*
* @throws crypto::OpenSSLError on failure.
*/
void sha1(bytes::View src, bytes::Buffer& result);
} // vereign::crypto::digest
......
......@@ -6,6 +6,9 @@
namespace vereign::crypto {
/**
* The base error type for the namespace vereign::crypto.
*/
class Error : public std::runtime_error {
public:
Error(const std::string& what)
......@@ -14,6 +17,9 @@ public:
}
};
/**
* An error thrown by the crypto functions.
*/
class OpenSSLError : public Error {
public:
OpenSSLError(const std::string& what)
......
#include <vereign/crypto/rand.hh>
#include <openssl/rand.h>
#include <vereign/crypto/errors.hh>
namespace vereign::crypto {
void Rand(bytes::Buffer& buf, std::size_t size) {
buf.Reserve(size);
int result = RAND_bytes(buf.end(), size);
if (result == 0) {
ERR_clear_error();
throw Error("crypto rand failed");
}
buf.IncSize(size);
}
auto Rand(std::size_t size) -> bytes::Buffer {
bytes::Buffer buf{size};
Rand(buf, size);
return buf;
}
} // vereign::crypto
......@@ -2,21 +2,46 @@
#define __VEREIGN_CRYPTO_RAND_HH
#include <vereign/bytes/buffer.hh>
#include <vereign/crypto/errors.hh>
#include <openssl/rand.h>
namespace vereign::crypto {
inline void Rand(bytes::Buffer& buf) {
int result = RAND_bytes(buf.end(), buf.FreeCap());
if (result == 0) {
ERR_clear_error();
throw Error("crypto rand failed");
}
/**
* Appends a random bytes to a buffer.
*
* After the operation is finished, the passed `buf` will be with incremented size of the newly
* added random bytes.
*
* Example:
* @code
* bytes::Bytes buf;
* crypto::Rand(buf, 16);
*
* assert(buf.Size() == 16);
* @endcode
*
* @param buf The buffer that will be filled with random bytes.
* @param size The number of the random bytes.
*
* @throws crypto::Error on failure.
*/
void Rand(bytes::Buffer& buf, std::size_t size);
buf.IncSize(buf.FreeCap());
}
/**
* Generate random bytes.
*
* Example:
* @code
* auto buf = crypto::Rand(16);
*
* assert(buf.Size() == 16);
* @endcode
*
* @param size The number of the random bytes.
* @returns buffer with the generated random bytes.
*
* @throws crypto::Error on failure.
*/
auto Rand(std::size_t size) -> bytes::Buffer;
} // vereign::crypto
......
......@@ -9,15 +9,137 @@
namespace vereign::crypto::rsa {
/**
* Generates new RSA key.
*
* Example:
* @code
* auto key = crypto::rsa::GenerateKey(2048);
* @endcode
*
* @param bits The length of the key.
* @returns the newly generated key.
*
* @throws crypto::OpenSSLError on failure.
*/
auto GenerateKey(int bits) -> bssl::UniquePtr<EVP_PKEY>;
/**
* Encrypts given bytes with RSA public key.
*
* Example:
* @code
* const std::string input{"foo bar"};
* auto key = crypto::rsa::GenerateKey(2048);
* bytes::Buffer encrypted;
*
* crypto::rsa::PublicKeyEncrypt(key.get(), bytes::View(input), encrypted);
*
* bytes::Buffer decrypted;
* crypto::rsa::PrivateKeyDecrypt(key.get(), encrypted.View(), decrypted);
*
* assert(decrypted.View() == bytes.View(input));
* @endcode
*
* @param key The RSA key.
* @param src The bytes that will be encrypted.
* @param encrypted The result of the encryption.
*
* @throws crypto::OpenSSLError on failure.
*/
void PublicKeyEncrypt(EVP_PKEY* key, bytes::View src, bytes::Buffer& encrypted);
/**
* Decrypts given bytes with RSA private key.
*
* Example:
* @code
* const std::string input{"foo bar"};
* auto key = crypto::rsa::GenerateKey(2048);
* bytes::Buffer encrypted;
*
* crypto::rsa::PublicKeyEncrypt(key.get(), bytes::View(input), encrypted);
*
* bytes::Buffer decrypted;
* crypto::rsa::PrivateKeyDecrypt(key.get(), encrypted.View(), decrypted);
*
* assert(decrypted.View() == bytes.View(input));
* @endcode
*
* @param key The RSA key.
* @param src The bytes that will be decrypted.
* @param decrypted The result of the decryption.
*
* @throws crypto::OpenSSLError on failure.
*/
void PrivateKeyDecrypt(EVP_PKEY* key, bytes::View src, bytes::Buffer& decrypted);
/**
* Exports a public key part to PEM format.
*
* @code
* auto key = crypto::rsa::GenerateKey(2048);
*
* auto bio = crypto::rsa::ExportPublicKeyToPEM(key.get());
* std::cout << crypto::bio::View(bio.get()).String() << std::endl;
* @endcode
*
* @param key The key to export.
* @returns a memory BIO with the exported key.
*
* @throws crypto::OpenSSLError on failure.
*/
auto ExportPublicKeyToPEM(EVP_PKEY* key) -> bssl::UniquePtr<BIO>;
/**
* Import public key from PEM format.
*
* @code
* auto key = crypto::rsa::GenerateKey(2048);
*
* auto bio = crypto::rsa::ExportPublicKeyToPEM(key.get());
* auto imported_key = crypto::rsa::ImportPublicKeyFromPEM(crypto::bio::View(bio.get()));
* @endcode
*
* @param pem PEM encoded key.
* @returns imported key.
*
* @throws crypto::OpenSSLError on failure.
*/
auto ImportPublicKeyFromPEM(bytes::View pem) -> bssl::UniquePtr<EVP_PKEY>;
/**
* Export private key from PEM format.
*
* @code
* auto key = crypto::rsa::GenerateKey(2048);
*
* auto bio = crypto::rsa::ExportPrivateKeyToPEM(key.get());
* std::cout << crypto::bio::View(bio.get()).String() << std::endl;
* @endcode
*
* @param key The key to export.
* @returns a memory BIO with the exported key.
*
* @throws crypto::OpenSSLError on failure.
*/
auto ExportPrivateKeyToPEM(EVP_PKEY* key) -> bssl::UniquePtr<BIO>;
/**
* Import private key from PEM format.
*
* @code
* auto key = crypto::rsa::GenerateKey(2048);
*
* auto bio = crypto::rsa::ExportPrivateKeyToPEM(key.get());
* auto imported_key = crypto::rsa::ImportPrivateKeyFromPEM(crypto::bio::View(bio.get()));
* @endcode
*
* @param pem PEM encoded key.
* @returns imported key.
*
* @throws crypto::OpenSSLError on failure.
*/
auto ImportPrivateKeyFromPEM(bytes::View pem) -> bssl::UniquePtr<EVP_PKEY>;
} // vereign::crypto::rsa
......
......@@ -6,7 +6,40 @@
namespace vereign::encoding::base64 {
/**
* Encodes source bytes into base64 encoding.
*
* No new lines are inserted.
*
* Example:
* @code
* std::string s{"foob"};
* bytes::Buffer encoded;
* encoding::base64::Encode(bytes::View(s), encoded);
*
* assert(encoded.View().String() == "Zm9vYg==")
* @endcode
*
* @param src The source bytes that will be encoded.
* @param encoded The encoded bytes.
*/
void Encode(bytes::View src, bytes::Buffer& encoded);
/**
* Decodes base64 encoded bytes.
*
* Example:
* @code
* std::string s{"Zm9vYg=="};
* bytes::Buffer decoded;
* encoding::base64::Decode(bytes::View(s), decoded);
*
* assert(decoded.View().String() == "foob");
* @endcode
*
* @param src The base64 encoded bytes that will be decoded.
* @param decoded The decoded bytes.
*/
void Decode(bytes::View src, bytes::Buffer& decoded);
} // vereign::encoding::base64
......
#include <vereign/encoding/binary.hh>
#include <vereign/encoding/errors.hh>
namespace vereign::encoding::binary {
void EncodeUint8(bytes::Buffer& out, uint8_t v) {
......@@ -10,7 +12,7 @@ void EncodeUint8(bytes::Buffer& out, uint8_t v) {
auto DecodeUint8(bytes::View& b) -> uint8_t {
if (b.Size() < 1) {
throw std::runtime_error("decoding failed the input size is less than 1 bytes");
throw encoding::Error("decoding failed the input size is less than 1 bytes");
}
return b.Data()[0];
......@@ -46,8 +48,7 @@ auto DecodeUint64(const uint8_t* b) -> uint64_t {
auto DecodeUint64(bytes::View b) -> uint64_t {
if (b.Size() < 8) {
// FIXME: add encoding::Error exception
throw std::runtime_error("decoding failed the input size is less than 8 bytes");
throw encoding::Error("decoding failed the input size is less than 8 bytes");
}
return DecodeUint64(b.Data());
......@@ -61,7 +62,7 @@ void EncodeBytes(bytes::Buffer& out, bytes::View bytes) {
auto DecodeBytes(bytes::View b, bytes::View& out) -> std::size_t {
auto size = DecodeUint64(b);
if (size < 0 || size + 8 > b.Size()) {
throw std::runtime_error("decode bytes failed: invalid size");
throw encoding::Error("decode bytes failed: invalid size");
}
out = b.Slice(8, size + 8);
......
......@@ -4,19 +4,96 @@
#include <stdexcept>
#include <vereign/bytes/buffer.hh>
// FIXME: add docs
/**
* Binary encoding of integers, bytes.
*
* The integers are encoded in little endian.
*/
namespace vereign::encoding::binary {
/**
* Encodes a byte.
*
* On success the out buffer size will be incremented with 1.
*
* @param out buffer where the byte will be written.
* @param v the byte that will be encoded.
*/
void EncodeUint8(bytes::Buffer& out, uint8_t v);
/**
* Decodes a byte.
*
* @param b the source bytes from where the single byte will be decoded.
*/
auto DecodeUint8(bytes::View& b) -> uint8_t;
/**
* Encodes a uint64.
*
* The number is encoded in little endian.
*
* @param out where the value will be written.
* @param v the number that will be encoded.
*/
void EncodeUint64(uint8_t* out, uint64_t v);
/**
* Encodes a uint64.
*
* On success the out buffer size will be incremented with 8.
* The number is encoded in little endian.
*
* @param out where the value will be written.
* @param v the number that will be encoded.
*/
void EncodeUint64(bytes::Buffer& out, uint64_t v);
/**
* Decodes a uint64.
*
* The number is expected to be encoded in little endian.
*
* @param b the source number.
* @returns the decoded number.
*/
auto DecodeUint64(const uint8_t* b) -> uint64_t;
/**
* Decodes a uint64.
*
* The number is expected to be encoded in little endian.
*
* @param b the source bytes where the encoded number will be read from.
* @returns the decoded number.
*
* @throws encoding::Error when the source bytes size is less than 8.
*/
auto DecodeUint64(bytes::View b) -> uint64_t;
/**
* Encodes bytes.
*
* The result is 8 bytes for the size, followed by the bytes them self.
* The out buffer size is incremented with out.Size() + 8.
*
* @param out the where the encoded value will be written.
* @param bytes the source bytes that will be encoded.
*/
void EncodeBytes(bytes::Buffer& out, bytes::View bytes);
/**
* Decode bytes.
*
* Note that the decoded bytes reference to the source buffer.
* In other words it is only a view, there is no copying involved.
*
* @param b the source with the encoded bytes.
* @param out the decoded bytes.
* @returns the bytes read during the decoding (8 + out.Size()).
*
* @throws encoding::Error when the decoding fails.
*/
auto DecodeBytes(bytes::View b, bytes::View& out) -> std::size_t;
} // namespace vereign::encoding::binary
......
#ifndef __VEREIGN_ENCODING_ERRORS_HH
#define __VEREIGN_ENCODING_ERRORS_HH
#include <stdexcept>
namespace vereign::encoding {
/**
* Base error for namespace vereign::encoding.
*/
class Error : public std::runtime_error {
public:
Error(const std::string& what)
: std::runtime_error(what)
{
}
};
} // namespace vereign::encoding::binary
#endif // __VEREIGN_ENCODING_ERRORS_HH
......@@ -39,16 +39,16 @@ void Encode(bytes::View src, bytes::Buffer& encoded) {
encoded.IncSize(src.Size() * 2);
}
void Decode(std::string_view src, bytes::Buffer& decoded) {
if (src.size() == 0) {
void Decode(bytes::View src, bytes::Buffer& decoded) {
if (src.Size() == 0) {
return ;
}
decoded.Reserve(src.size() / 2);
for (int i = 0, len = (int) src.size() - 1; i < len; i += 2) {
decoded.Reserve(src.Size() / 2);
for (int i = 0, len = (int) src.Size() - 1; i < len; i += 2) {
decoded[i/2] = detail::charToInt(src[i]) * 16 + detail::charToInt(src[i + 1]);
}
decoded.IncSize(src.size() / 2);
decoded.IncSize(src.Size() / 2);
}
void EncodeReverse(bytes::View src, bytes::Buffer& encoded) {
......
......@@ -6,9 +6,52 @@
namespace vereign::encoding::hex {
/**
* Encodes bytes in hexadecimal.
*
* Example:
* @code
* bytes::Buffer encoded;
* encoding::hex::Decode(bytes::View("foobar"), encoded);
*
* assert(encoded.View().String() == "666f6f626172");
* @endcode
*
* @param src The bytes that will be encoded.
* @param encoded The buffer where the encoded bytes will be written.
*/
void Encode(bytes::View src, bytes::Buffer& encoded);
/**
* Decodes hexadecimal encoded bytes.
*
* Example:
* @code
* bytes::Buffer decoded;
* encoding::hex::Decode(bytes::View("666f6f626172"), decoded);
*
* assert(decoded.View().String() == "foobar");
* @endcode
*
* @param src The bytes that will be decoded.
* @param encoded The buffer where the decoded bytes will be written.
*/
void Decode(bytes::View src, bytes::Buffer& decoded);
/**
* Encodes bytes in hexadecimal in reverse order.
*
* Example:
* @code
* bytes::Buffer encoded;
* encoding::hex::Decode(bytes::View("foobar"), encoded);
*
* assert(encoded.View().String() == "7261626f6f66");
* @endcode
*
* @param src The bytes that will be encoded.
* @param encoded The buffer where the encoded bytes will be written.
*/
void EncodeReverse(bytes::View src, bytes::Buffer& encoded);
} // vereign::encoding::hex
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment