mirror of
https://github.com/matrix-construct/construct
synced 2024-11-25 16:22:35 +01:00
ircd::openssl: Add several suites of utils for OpenSSL/X.509/RSA/BIGNUM et al.
This commit is contained in:
parent
bdf696b91f
commit
6b57387cc2
2 changed files with 886 additions and 24 deletions
|
@ -25,9 +25,16 @@
|
|||
#pragma once
|
||||
#define HAVE_IRCD_OPENSSL_H
|
||||
|
||||
// Forward declarations for OpenSSL because it is not included here.
|
||||
// Forward declarations for OpenSSL because it is not included here. Note
|
||||
// that these are declared in the extern namespace outside of ircd:: but
|
||||
// match those in the OpenSSL headers and should not be too much trouble.
|
||||
struct ssl_st;
|
||||
struct rsa_st;
|
||||
struct x509_st;
|
||||
struct bignum_st;
|
||||
struct bignum_ctx;
|
||||
struct bio_st;
|
||||
struct evp_pkey_st;
|
||||
|
||||
/// OpenSSL library interface. Provides things we need to expose from OpenSSL
|
||||
/// to the rest of the project. Anything that employs forward declared types
|
||||
|
@ -37,12 +44,20 @@ struct x509_st;
|
|||
namespace ircd::openssl
|
||||
{
|
||||
IRCD_EXCEPTION(ircd::error, error)
|
||||
IRCD_EXCEPTION(error, buffer_error)
|
||||
|
||||
struct init;
|
||||
struct bignum;
|
||||
|
||||
using SSL = ::ssl_st;
|
||||
using RSA = ::rsa_st;
|
||||
using X509 = ::x509_st;
|
||||
using BIGNUM = ::bignum_st;
|
||||
using BN_CTX = ::bignum_ctx;
|
||||
using EVP_PKEY = ::evp_pkey_st;
|
||||
using BIO = ::bio_st;
|
||||
|
||||
// Library general
|
||||
string_view version();
|
||||
|
||||
// Observers
|
||||
|
@ -53,20 +68,97 @@ namespace ircd::openssl
|
|||
ulong get_error();
|
||||
void clear_error();
|
||||
|
||||
// Convert PEM string to X509
|
||||
X509 &read(X509 *out, const string_view &cert);
|
||||
// Envelope suite
|
||||
EVP_PKEY &read_pem_pub(EVP_PKEY &out, const string_view &pem);
|
||||
EVP_PKEY &read_pem_priv(EVP_PKEY &out, const string_view &pem);
|
||||
string_view write_pem_pub(const mutable_buffer &out, const EVP_PKEY &);
|
||||
string_view write_pem_priv(const mutable_buffer &out, const EVP_PKEY &);
|
||||
|
||||
// Convert X509 certificate to DER encoding
|
||||
// RSA suite
|
||||
void check(const RSA &);
|
||||
bool check(const RSA &, std::nothrow_t);
|
||||
size_t size(const RSA &); // RSA_size() / mod size in bytes
|
||||
string_view print(const mutable_buffer &buf, const RSA &, const off_t &offset = 0);
|
||||
RSA &genrsa(RSA &out, const uint &bits = 2048, const uint &e = 0x10001);
|
||||
void genrsa(const string_view &skfile, const string_view &pkfile, const json::object &opts = {});
|
||||
void set(EVP_PKEY &out, RSA &in);
|
||||
|
||||
// X.509 suite
|
||||
const_raw_buffer i2d(const mutable_raw_buffer &out, const X509 &);
|
||||
|
||||
// Convert PEM string to DER
|
||||
const_raw_buffer cert2d(const mutable_raw_buffer &out, const string_view &cert);
|
||||
|
||||
// Get X509 certificate from struct SSL (get SSL from a socket)
|
||||
const_raw_buffer cert2d(const mutable_raw_buffer &out, const string_view &pem);
|
||||
X509 &read_pem(X509 &out, const string_view &pem);
|
||||
string_view write_pem(const mutable_buffer &out, const X509 &);
|
||||
string_view print(const mutable_buffer &buf, const X509 &);
|
||||
string_view genX509(const mutable_buffer &out, const json::object &opts);
|
||||
const X509 &get_peer_cert(const SSL &);
|
||||
X509 &get_peer_cert(SSL &);
|
||||
}
|
||||
|
||||
/// OpenSSL BIO convenience utils and wraps; also secure file IO closures
|
||||
namespace ircd::openssl::bio
|
||||
{
|
||||
// Presents a memory BIO closure hiding boilerplate
|
||||
using closure = std::function<void (BIO *const &)>;
|
||||
string_view write(const mutable_buffer &, const closure &);
|
||||
void read(const const_buffer &, const closure &);
|
||||
|
||||
// Presents a secure buffer file IO closure for writing to path
|
||||
using mb_closure = std::function<string_view (const mutable_buffer &)>;
|
||||
void write_file(const string_view &path, const mb_closure &closure, const size_t &bufsz = 64_KiB);
|
||||
|
||||
// Presents a secure buffer file IO closure with data read from path
|
||||
using cb_closure = std::function<void (const string_view &)>;
|
||||
void read_file(const string_view &path, const cb_closure &closure);
|
||||
}
|
||||
|
||||
// OpenSSL BIGNUM convenience utils and wraps.
|
||||
namespace ircd::openssl
|
||||
{
|
||||
size_t size(const BIGNUM *const &); // bytes binary
|
||||
mutable_raw_buffer data(const mutable_raw_buffer &out, const BIGNUM *const &); // le
|
||||
string_view u2a(const mutable_buffer &out, const BIGNUM *const &);
|
||||
}
|
||||
|
||||
/// Light semantic-complete wrapper for BIGNUM.
|
||||
class ircd::openssl::bignum
|
||||
{
|
||||
BIGNUM *a;
|
||||
|
||||
public:
|
||||
const BIGNUM *get() const;
|
||||
BIGNUM *get();
|
||||
BIGNUM *release();
|
||||
|
||||
size_t bits() const;
|
||||
size_t bytes() const;
|
||||
|
||||
explicit operator uint128_t() const;
|
||||
operator const BIGNUM *() const;
|
||||
operator const BIGNUM &() const;
|
||||
operator BIGNUM *const &();
|
||||
operator BIGNUM **();
|
||||
operator BIGNUM &();
|
||||
|
||||
// default constructor does not BN_new()
|
||||
bignum()
|
||||
:a{nullptr}
|
||||
{}
|
||||
|
||||
// acquisitional constructor for OpenSSL API return values
|
||||
explicit bignum(BIGNUM *const &a)
|
||||
:a{a}
|
||||
{}
|
||||
|
||||
explicit bignum(const uint128_t &val);
|
||||
bignum(const const_raw_buffer &bin); // le
|
||||
explicit bignum(const BIGNUM &a);
|
||||
bignum(const bignum &);
|
||||
bignum(bignum &&) noexcept;
|
||||
bignum &operator=(const bignum &);
|
||||
bignum &operator=(bignum &&) noexcept;
|
||||
~bignum() noexcept;
|
||||
};
|
||||
|
||||
struct ircd::openssl::init
|
||||
{
|
||||
init();
|
||||
|
|
800
ircd/openssl.cc
800
ircd/openssl.cc
|
@ -22,7 +22,9 @@
|
|||
#include <openssl/err.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
namespace ircd::openssl
|
||||
{
|
||||
|
@ -44,9 +46,199 @@ namespace ircd::openssl
|
|||
// openssl.h
|
||||
//
|
||||
|
||||
//
|
||||
// X509
|
||||
//
|
||||
|
||||
namespace ircd::openssl
|
||||
{
|
||||
void append(X509_NAME &name, const string_view &key, const string_view &val);
|
||||
void append(X509_NAME &name, const json::object &entries);
|
||||
void append_entries(X509 &cert, const json::object &opts);
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::openssl::genX509(const mutable_buffer &out,
|
||||
const json::object &opts)
|
||||
{
|
||||
const custom_ptr<RSA> rsa
|
||||
{
|
||||
RSA_new(),
|
||||
[](RSA *const rsa) { RSA_free(rsa); }
|
||||
};
|
||||
|
||||
const custom_ptr<EVP_PKEY> pk
|
||||
{
|
||||
EVP_PKEY_new(),
|
||||
[](EVP_PKEY *const pk) { EVP_PKEY_free(pk); }
|
||||
};
|
||||
|
||||
set(*pk, *rsa);
|
||||
|
||||
const auto public_key_path
|
||||
{
|
||||
unquote(opts.at("tls_public_key_path"))
|
||||
};
|
||||
|
||||
bio::read_file(public_key_path, [&pk]
|
||||
(const string_view &pem)
|
||||
{
|
||||
read_pem_pub(*pk, pem);
|
||||
});
|
||||
|
||||
const auto private_key_path
|
||||
{
|
||||
unquote(opts.at("tls_private_key_path"))
|
||||
};
|
||||
|
||||
bio::read_file(private_key_path, [&pk]
|
||||
(const string_view &pem)
|
||||
{
|
||||
read_pem_priv(*pk, pem);
|
||||
});
|
||||
|
||||
check(*pk->pkey.rsa);
|
||||
|
||||
const custom_ptr<X509> x509
|
||||
{
|
||||
X509_new(),
|
||||
[](X509 *const x509) { X509_free(x509); }
|
||||
};
|
||||
|
||||
call(::X509_set_pubkey, x509.get(), pk.get());
|
||||
call(::X509_set_version, x509.get(), 2);
|
||||
append_entries(*x509, opts);
|
||||
|
||||
call(::X509_sign, x509.get(), pk.get(), EVP_sha256());
|
||||
|
||||
return write_pem(out, *x509);
|
||||
}
|
||||
|
||||
void
|
||||
ircd::openssl::append_entries(X509 &cert,
|
||||
const json::object &opts)
|
||||
{
|
||||
// version
|
||||
call(::X509_set_version, &cert, opts.get<long>("version", 2));
|
||||
|
||||
// notBefore
|
||||
{
|
||||
const long value
|
||||
{
|
||||
opts.get<long>("notBefore", 0)
|
||||
};
|
||||
|
||||
ASN1_TIME *const notBefore{X509_get_notBefore(&cert)};
|
||||
assert(notBefore != nullptr);
|
||||
X509_gmtime_adj(notBefore, value);
|
||||
}
|
||||
|
||||
// notAfter
|
||||
{
|
||||
const long value
|
||||
{
|
||||
opts.get<long>("notAfter", 0)?:
|
||||
60 * 60 * 24 * opts.get<long>("days", 7L)
|
||||
};
|
||||
|
||||
ASN1_TIME *const notAfter{X509_get_notAfter(&cert)};
|
||||
assert(notAfter != nullptr);
|
||||
X509_gmtime_adj(notAfter, value);
|
||||
}
|
||||
|
||||
// subject
|
||||
if(opts.has("subject"))
|
||||
{
|
||||
const json::object subject_opts
|
||||
{
|
||||
opts["subject"]
|
||||
};
|
||||
|
||||
X509_NAME *const subject
|
||||
{
|
||||
X509_get_subject_name(&cert)
|
||||
};
|
||||
|
||||
assert(subject != nullptr);
|
||||
append(*subject, subject_opts);
|
||||
}
|
||||
|
||||
// issuer
|
||||
if(opts.has("issuer"))
|
||||
{
|
||||
const json::object issuer_opts
|
||||
{
|
||||
opts["issuer"]
|
||||
};
|
||||
|
||||
X509_NAME *const issuer
|
||||
{
|
||||
X509_get_issuer_name(&cert)
|
||||
};
|
||||
|
||||
assert(issuer != nullptr);
|
||||
append(*issuer, issuer_opts);
|
||||
}
|
||||
else if(opts.has("subject")) // self-signed; issuer is subject
|
||||
{
|
||||
X509_NAME *const subject
|
||||
{
|
||||
X509_get_subject_name(&cert)
|
||||
};
|
||||
|
||||
assert(subject != nullptr);
|
||||
call(::X509_set_issuer_name, &cert, subject);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ircd::openssl::append(X509_NAME &name,
|
||||
const json::object &entries)
|
||||
{
|
||||
for(const auto &member : entries)
|
||||
append(name, unquote(member.first), unquote(member.second));
|
||||
}
|
||||
|
||||
void
|
||||
ircd::openssl::append(X509_NAME &name,
|
||||
const string_view &key,
|
||||
const string_view &val)
|
||||
try
|
||||
{
|
||||
call(::X509_NAME_add_entry_by_txt,
|
||||
&name,
|
||||
std::string{key}.c_str(), // key (has to be null terminated)
|
||||
MBSTRING_ASC, // type
|
||||
(const uint8_t *)val.data(), // data
|
||||
val.size(), // len
|
||||
-1, // loc (-1 = append)
|
||||
0); // set (0 = new RDN created)
|
||||
}
|
||||
catch(const error &e)
|
||||
{
|
||||
throw error
|
||||
{
|
||||
"Failed to append X509 NAME entry '%s' (%zu bytes): %s",
|
||||
key,
|
||||
val.size(),
|
||||
e.what()
|
||||
};
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::openssl::print(const mutable_buffer &buf,
|
||||
const X509 &cert)
|
||||
{
|
||||
return bio::write(buf, [&cert]
|
||||
(BIO *const &bio)
|
||||
{
|
||||
X509_print(bio, const_cast<X509 *>(&cert));
|
||||
});
|
||||
}
|
||||
|
||||
ircd::const_raw_buffer
|
||||
ircd::openssl::cert2d(const mutable_raw_buffer &out,
|
||||
const string_view &cert)
|
||||
const string_view &pem)
|
||||
{
|
||||
const custom_ptr<X509> x509
|
||||
{
|
||||
|
@ -54,30 +246,39 @@ ircd::openssl::cert2d(const mutable_raw_buffer &out,
|
|||
[](X509 *const x509) { X509_free(x509); }
|
||||
};
|
||||
|
||||
return i2d(out, read(x509.get(), cert));
|
||||
return i2d(out, read_pem(*x509, pem));
|
||||
}
|
||||
|
||||
X509 &
|
||||
ircd::openssl::read(X509 *out,
|
||||
const string_view &cert)
|
||||
ircd::openssl::read_pem(X509 &out_,
|
||||
const string_view &pem)
|
||||
{
|
||||
const custom_ptr<BIO> bp
|
||||
X509 *ret{nullptr}, *out{&out_};
|
||||
bio::read(pem, [&ret, &out]
|
||||
(BIO *const &bio)
|
||||
{
|
||||
BIO_new_mem_buf(data(cert), size(cert)),
|
||||
[](BIO *const bp) { BIO_free(bp); }
|
||||
};
|
||||
|
||||
X509 *const ret
|
||||
{
|
||||
PEM_read_bio_X509(bp.get(), &out, nullptr, nullptr)
|
||||
};
|
||||
ret = PEM_read_bio_X509(bio, &out, nullptr, nullptr);
|
||||
});
|
||||
|
||||
if(unlikely(ret != out))
|
||||
throw_error();
|
||||
throw error("Failed to read X509 PEM @ %p (len: %zu)",
|
||||
pem.data(),
|
||||
pem.length());
|
||||
|
||||
return *ret;
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::openssl::write_pem(const mutable_buffer &out,
|
||||
const X509 &cert)
|
||||
{
|
||||
return bio::write(out, [&cert]
|
||||
(BIO *const &bio)
|
||||
{
|
||||
call(::PEM_write_bio_X509, bio, const_cast<X509 *>(&cert));
|
||||
});
|
||||
}
|
||||
|
||||
ircd::const_raw_buffer
|
||||
ircd::openssl::i2d(const mutable_raw_buffer &buf,
|
||||
const X509 &_cert)
|
||||
|
@ -138,6 +339,277 @@ ircd::openssl::get_peer_cert(const SSL &ssl)
|
|||
return *ret;
|
||||
}
|
||||
|
||||
//
|
||||
// RSA
|
||||
//
|
||||
|
||||
void
|
||||
ircd::openssl::genrsa(const string_view &skfile,
|
||||
const string_view &pkfile,
|
||||
const json::object &opts)
|
||||
{
|
||||
const auto bits
|
||||
{
|
||||
opts.get<uint>("bits", 2048)
|
||||
};
|
||||
|
||||
const auto e
|
||||
{
|
||||
opts.get<uint>("e", 65537)
|
||||
};
|
||||
|
||||
const custom_ptr<RSA> rsa
|
||||
{
|
||||
RSA_new(),
|
||||
[](RSA *const rsa) { RSA_free(rsa); }
|
||||
};
|
||||
|
||||
const custom_ptr<EVP_PKEY> pk
|
||||
{
|
||||
EVP_PKEY_new(),
|
||||
[](EVP_PKEY *const pk) { EVP_PKEY_free(pk); }
|
||||
};
|
||||
|
||||
genrsa(*rsa, bits, e);
|
||||
check(*rsa);
|
||||
set(*pk, *rsa);
|
||||
|
||||
bio::write_file(skfile, [&pk]
|
||||
(const mutable_buffer &out)
|
||||
{
|
||||
return write_pem_priv(out, *pk);
|
||||
});
|
||||
|
||||
bio::write_file(pkfile, [&pk]
|
||||
(const mutable_buffer &out)
|
||||
{
|
||||
return write_pem_pub(out, *pk);
|
||||
});
|
||||
}
|
||||
|
||||
namespace ircd::openssl
|
||||
{
|
||||
static int genrsa_cb(const int, const int, BN_GENCB *const);
|
||||
}
|
||||
|
||||
RSA &
|
||||
ircd::openssl::genrsa(RSA &out,
|
||||
const uint &bits,
|
||||
const uint &exp)
|
||||
{
|
||||
BN_GENCB gencb{0};
|
||||
void *const arg{nullptr}; // privdata passed to cb
|
||||
BN_GENCB_set(&gencb, &ircd::openssl::genrsa_cb, arg);
|
||||
|
||||
bignum e{exp};
|
||||
call(::RSA_generate_key_ex, &out, bits, e, &gencb);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// This callback can be used to integrate generating with ircd::ctx
|
||||
// or ctx::offload/thread or some status update. For now we just eat
|
||||
// the milliseconds of prime generation on main.
|
||||
// return false causes call(RSA_generate_key_ex) to throw
|
||||
int
|
||||
ircd::openssl::genrsa_cb(const int stat,
|
||||
const int ith,
|
||||
BN_GENCB *const ctx)
|
||||
{
|
||||
assert(ctx != nullptr);
|
||||
auto &arg{ctx->arg};
|
||||
switch(stat)
|
||||
{
|
||||
case 0: // generating i-th potential prime
|
||||
return true;
|
||||
|
||||
case 1: // testing i-th potential prime
|
||||
return true;
|
||||
|
||||
case 2: // found i-th potential prime but rejected for RSA
|
||||
return true;
|
||||
|
||||
case 3: switch(ith) // found for RSA...
|
||||
{
|
||||
case 0: // found P
|
||||
return true;
|
||||
|
||||
case 1: // found Q
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::openssl::print(const mutable_buffer &buf,
|
||||
const RSA &rsa,
|
||||
const off_t &offset)
|
||||
{
|
||||
return bio::write(buf, [&rsa, &offset]
|
||||
(BIO *const &bio)
|
||||
{
|
||||
RSA_print(bio, const_cast<RSA *>(&rsa), offset);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
ircd::openssl::set(EVP_PKEY &out,
|
||||
RSA &in)
|
||||
{
|
||||
call(::EVP_PKEY_set1_RSA, &out, &in);
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::openssl::size(const RSA &key)
|
||||
{
|
||||
assert(key.n != nullptr);
|
||||
return RSA_size(&key);
|
||||
}
|
||||
|
||||
void
|
||||
ircd::openssl::check(const RSA &key)
|
||||
{
|
||||
if(call<error, -1>(::RSA_check_key, const_cast<RSA *>(&key)) == 0)
|
||||
throw error("Invalid RSA");
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::openssl::check(const RSA &key,
|
||||
const std::nothrow_t)
|
||||
{
|
||||
return RSA_check_key(const_cast<RSA *>(&key)) == 1;
|
||||
}
|
||||
|
||||
//
|
||||
// Envelope
|
||||
//
|
||||
|
||||
ircd::string_view
|
||||
ircd::openssl::write_pem_priv(const mutable_buffer &out,
|
||||
const EVP_PKEY &evp)
|
||||
{
|
||||
EVP_CIPHER *const enc{nullptr};
|
||||
|
||||
uint8_t *const kstr{nullptr};
|
||||
const int klen{0};
|
||||
|
||||
pem_password_cb *const pwcb{nullptr};
|
||||
void *const u{nullptr};
|
||||
|
||||
auto *const p{const_cast<EVP_PKEY *>(&evp)};
|
||||
return bio::write(out, [&p, &enc, &kstr, &klen, &pwcb, &u]
|
||||
(BIO *const &bio)
|
||||
{
|
||||
switch(p->type)
|
||||
{
|
||||
case EVP_PKEY_RSA:
|
||||
call(::PEM_write_bio_RSAPrivateKey, bio, p->pkey.rsa, enc, kstr, klen, pwcb, u);
|
||||
break;
|
||||
|
||||
default:
|
||||
call(::PEM_write_bio_PrivateKey, bio, p, enc, kstr, klen, pwcb, u);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::openssl::write_pem_pub(const mutable_buffer &out,
|
||||
const EVP_PKEY &evp)
|
||||
{
|
||||
auto *const p{const_cast<EVP_PKEY *>(&evp)};
|
||||
return bio::write(out, [&p]
|
||||
(BIO *const &bio)
|
||||
{
|
||||
switch(p->type)
|
||||
{
|
||||
case EVP_PKEY_RSA:
|
||||
call(::PEM_write_bio_RSAPublicKey, bio, p->pkey.rsa);
|
||||
break;
|
||||
|
||||
default:
|
||||
call(::PEM_write_bio_PUBKEY, bio, p);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
EVP_PKEY &
|
||||
ircd::openssl::read_pem_priv(EVP_PKEY &out_,
|
||||
const string_view &pem)
|
||||
{
|
||||
void *ret{nullptr};
|
||||
EVP_PKEY *out{&out_};
|
||||
|
||||
pem_password_cb *const pwcb{nullptr};
|
||||
void *const u{nullptr};
|
||||
|
||||
bio::read(pem, [&ret, &out, &pwcb, &u]
|
||||
(BIO *const &bio)
|
||||
{
|
||||
switch(out->type)
|
||||
{
|
||||
case EVP_PKEY_RSA:
|
||||
ret = PEM_read_bio_RSAPrivateKey(bio, &out->pkey.rsa, pwcb, u);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = PEM_read_bio_PrivateKey(bio, &out, pwcb, u);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
if(unlikely(!ret))
|
||||
throw error("Failed to read Private Key PEM @ %p (len: %zu)",
|
||||
pem.data(),
|
||||
pem.length());
|
||||
|
||||
return *out;
|
||||
}
|
||||
|
||||
EVP_PKEY &
|
||||
ircd::openssl::read_pem_pub(EVP_PKEY &out_,
|
||||
const string_view &pem)
|
||||
{
|
||||
void *ret{nullptr};
|
||||
EVP_PKEY *out{&out_};
|
||||
|
||||
pem_password_cb *const pwcb{nullptr};
|
||||
void *const u{nullptr};
|
||||
|
||||
bio::read(pem, [&ret, &out, &pwcb, &u]
|
||||
(BIO *const &bio)
|
||||
{
|
||||
switch(out->type)
|
||||
{
|
||||
case EVP_PKEY_RSA:
|
||||
ret = PEM_read_bio_RSAPublicKey(bio, &out->pkey.rsa, pwcb, u);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = PEM_read_bio_PUBKEY(bio, &out, pwcb, u);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
if(unlikely(!ret))
|
||||
throw error("Failed to read Public Key PEM @ %p (len: %zu)",
|
||||
pem.data(),
|
||||
pem.length());
|
||||
|
||||
return *out;
|
||||
}
|
||||
|
||||
//
|
||||
// lib generale
|
||||
//
|
||||
|
||||
void
|
||||
ircd::openssl::clear_error()
|
||||
{
|
||||
|
@ -170,6 +642,304 @@ ircd::openssl::version()
|
|||
return SSLeay_version(SSLEAY_VERSION);
|
||||
}
|
||||
|
||||
//
|
||||
// bio
|
||||
//
|
||||
|
||||
void
|
||||
ircd::openssl::bio::read_file(const string_view &path,
|
||||
const cb_closure &closure)
|
||||
{
|
||||
const size_t size
|
||||
{
|
||||
fs::size(path)
|
||||
};
|
||||
|
||||
const custom_ptr<void> buf
|
||||
{
|
||||
OPENSSL_malloc_locked(size),
|
||||
[&size](void *const buf)
|
||||
{
|
||||
OPENSSL_cleanse(buf, size);
|
||||
OPENSSL_free_locked(buf);
|
||||
}
|
||||
};
|
||||
|
||||
const mutable_buffer mb
|
||||
{
|
||||
reinterpret_cast<char *>(buf.get()), size
|
||||
};
|
||||
|
||||
closure(fs::read(path, mb));
|
||||
}
|
||||
|
||||
void
|
||||
ircd::openssl::bio::write_file(const string_view &path,
|
||||
const mb_closure &closure,
|
||||
const size_t &size)
|
||||
{
|
||||
const custom_ptr<void> buf
|
||||
{
|
||||
OPENSSL_malloc_locked(size),
|
||||
[&size](void *const buf)
|
||||
{
|
||||
OPENSSL_cleanse(buf, size);
|
||||
OPENSSL_free_locked(buf);
|
||||
}
|
||||
};
|
||||
|
||||
const mutable_buffer mb
|
||||
{
|
||||
reinterpret_cast<char *>(buf.get()), size
|
||||
};
|
||||
|
||||
fs::overwrite(path, closure(mb));
|
||||
}
|
||||
|
||||
void
|
||||
ircd::openssl::bio::read(const const_buffer &buf,
|
||||
const closure &closure)
|
||||
{
|
||||
const custom_ptr<BIO> bp
|
||||
{
|
||||
BIO_new_mem_buf(data(buf), size(buf)),
|
||||
[](BIO *const bp) { BIO_free(bp); }
|
||||
};
|
||||
|
||||
closure(bp.get());
|
||||
}
|
||||
|
||||
ircd::string_view
|
||||
ircd::openssl::bio::write(const mutable_buffer &buf,
|
||||
const closure &closure)
|
||||
{
|
||||
const custom_ptr<BIO> bp
|
||||
{
|
||||
BIO_new(BIO_s_mem()),
|
||||
[](BIO *const bp) { BIO_free(bp); }
|
||||
};
|
||||
|
||||
//TODO: XXX: BAD: if the buffer is too small:
|
||||
// I saw this try to realloc() our buffer. It did not respect
|
||||
// the max size. I'd expect either truncation or error, so wtf?
|
||||
|
||||
BUF_MEM bm {0};
|
||||
bm.data = data(buf);
|
||||
bm.max = size(buf);
|
||||
call(::BIO_ctrl, bp.get(), BIO_C_SET_BUF_MEM, BIO_NOCLOSE, &bm);
|
||||
|
||||
closure(bp.get());
|
||||
|
||||
assert(size_t(bm.length) <= size(buf));
|
||||
return { data(buf), size_t(bm.length) };
|
||||
}
|
||||
|
||||
//
|
||||
// bignum
|
||||
//
|
||||
|
||||
ircd::string_view
|
||||
ircd::openssl::u2a(const mutable_buffer &out,
|
||||
const BIGNUM *const &a)
|
||||
{
|
||||
const unique_buffer<mutable_raw_buffer> tmp
|
||||
{
|
||||
size(a)
|
||||
};
|
||||
|
||||
return ircd::u2a(out, data(tmp, a));
|
||||
}
|
||||
|
||||
ircd::mutable_raw_buffer
|
||||
ircd::openssl::data(const mutable_raw_buffer &out,
|
||||
const BIGNUM *const &a)
|
||||
{
|
||||
if(!a)
|
||||
return { data(out), 0UL };
|
||||
|
||||
if(unlikely(size(out) < size(a)))
|
||||
throw buffer_error
|
||||
{
|
||||
"buffer size %zu short for BIGNUM of size %zu", size(out), size(a)
|
||||
};
|
||||
|
||||
const auto len
|
||||
{
|
||||
BN_bn2bin(a, data(out))
|
||||
};
|
||||
|
||||
reverse(out);
|
||||
assert(len <= ssize_t(size(out)));
|
||||
return { data(out), size_t(len) };
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::openssl::size(const BIGNUM *const &a)
|
||||
{
|
||||
return BN_num_bytes(a);
|
||||
}
|
||||
|
||||
//
|
||||
// bignum::bignum
|
||||
//
|
||||
|
||||
ircd::openssl::bignum::bignum(const uint128_t &val)
|
||||
:bignum
|
||||
{
|
||||
const_raw_buffer
|
||||
{
|
||||
reinterpret_cast<const uint8_t *>(&val), sizeof(val)
|
||||
}
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::openssl::bignum::bignum(const const_raw_buffer &bin)
|
||||
:a{[&bin]
|
||||
{
|
||||
// Our binary buffer is little endian. We use
|
||||
const ctx::critical_assertion ca;
|
||||
static thread_local uint8_t tmp[64_KiB];
|
||||
const mutable_raw_buffer buf{tmp, size(bin)};
|
||||
if(unlikely(size(buf) > sizeof(tmp)))
|
||||
throw buffer_error
|
||||
{
|
||||
"buffer input of %zu for bignum > tmp %zu", size(bin), sizeof(tmp)
|
||||
};
|
||||
|
||||
reverse(buf, bin);
|
||||
return BN_bin2bn(data(buf), size(buf), nullptr);
|
||||
}()}
|
||||
{
|
||||
if(unlikely(!a))
|
||||
throw error("Error creating bignum from binary buffer...");
|
||||
}
|
||||
|
||||
ircd::openssl::bignum::bignum(const BIGNUM &a)
|
||||
:a{BN_dup(&a)}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::openssl::bignum::bignum(const bignum &o)
|
||||
:a{BN_dup(o.a)}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::openssl::bignum::bignum(bignum &&o)
|
||||
noexcept
|
||||
:a{std::move(o.a)}
|
||||
{
|
||||
o.a = nullptr;
|
||||
}
|
||||
|
||||
ircd::openssl::bignum &
|
||||
ircd::openssl::bignum::operator=(const bignum &o)
|
||||
{
|
||||
if(unlikely(!BN_copy(a, o.a)))
|
||||
throw error("Failed to copy bignum from %p to %p", &o, this);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ircd::openssl::bignum &
|
||||
ircd::openssl::bignum::operator=(bignum &&o)
|
||||
noexcept
|
||||
{
|
||||
this->~bignum();
|
||||
a = std::move(o.a);
|
||||
o.a = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ircd::openssl::bignum::~bignum()
|
||||
noexcept
|
||||
{
|
||||
BN_free(a);
|
||||
}
|
||||
|
||||
ircd::openssl::bignum::operator
|
||||
ircd::uint128_t()
|
||||
const
|
||||
{
|
||||
uint128_t ret{0};
|
||||
const mutable_raw_buffer buf
|
||||
{
|
||||
reinterpret_cast<uint8_t *>(&ret), sizeof(ret)
|
||||
};
|
||||
|
||||
data(buf, a);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ircd::openssl::bignum::operator
|
||||
BIGNUM &()
|
||||
{
|
||||
assert(a != nullptr);
|
||||
return *a;
|
||||
}
|
||||
|
||||
ircd::openssl::bignum::operator
|
||||
BIGNUM *const &()
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
ircd::openssl::bignum::operator
|
||||
BIGNUM **()
|
||||
{
|
||||
return &a;
|
||||
}
|
||||
|
||||
ircd::openssl::bignum::operator
|
||||
const BIGNUM &()
|
||||
const
|
||||
{
|
||||
assert(a != nullptr);
|
||||
return *a;
|
||||
}
|
||||
|
||||
ircd::openssl::bignum::operator
|
||||
const BIGNUM *()
|
||||
const
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::openssl::bignum::bytes()
|
||||
const
|
||||
{
|
||||
return BN_num_bytes(get());
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::openssl::bignum::bits()
|
||||
const
|
||||
{
|
||||
return BN_num_bits(get());
|
||||
}
|
||||
|
||||
BIGNUM *
|
||||
ircd::openssl::bignum::release()
|
||||
{
|
||||
BIGNUM *const a{this->a};
|
||||
this->a = nullptr;
|
||||
return a;
|
||||
}
|
||||
|
||||
BIGNUM *
|
||||
ircd::openssl::bignum::get()
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
const BIGNUM *
|
||||
ircd::openssl::bignum::get()
|
||||
const
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
//
|
||||
// init
|
||||
//
|
||||
|
@ -436,7 +1206,7 @@ static int
|
|||
ircd::openssl::call(function&& f,
|
||||
args&&... a)
|
||||
{
|
||||
const int ret
|
||||
const auto ret
|
||||
{
|
||||
f(std::forward<args>(a)...)
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue