diff options
Diffstat (limited to 'include/hotstuff/crypto.h')
-rw-r--r-- | include/hotstuff/crypto.h | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/include/hotstuff/crypto.h b/include/hotstuff/crypto.h new file mode 100644 index 0000000..2fbf745 --- /dev/null +++ b/include/hotstuff/crypto.h @@ -0,0 +1,386 @@ +#ifndef _HOTSTUFF_CRYPTO_H +#define _HOTSTUFF_CRYPTO_H + +#include "salticidae/crypto.h" +#include "salticidae/ref.h" +#include "secp256k1.h" +#include <openssl/rand.h> +#include "type.h" + +using salticidae::RcObj; +using salticidae::BoxObj; + +namespace hotstuff { + +using salticidae::SHA256; + +class PubKey: public Serializable, Cloneable { + public: + virtual ~PubKey() = default; + virtual PubKey *clone() override = 0; +}; + +using pubkey_bt = BoxObj<PubKey>; + +class PrivKey: public Serializable { + public: + virtual ~PrivKey() = default; + virtual pubkey_bt get_pubkey() const = 0; + virtual void from_rand() = 0; +}; + +using privkey_bt = BoxObj<PrivKey>; + +class PartCert: public Serializable, public Cloneable { + public: + virtual ~PartCert() = default; + virtual bool verify(const PubKey &pubkey) = 0; + virtual const uint256_t &get_blk_hash() const = 0; + virtual PartCert *clone() override = 0; +}; + +class ReplicaConfig; + +class QuorumCert: public Serializable, public Cloneable { + public: + virtual ~QuorumCert() = default; + virtual void add_part(ReplicaID replica, const PartCert &pc) = 0; + virtual void compute() = 0; + virtual bool verify(const ReplicaConfig &config) = 0; + virtual const uint256_t &get_blk_hash() const = 0; + virtual QuorumCert *clone() override = 0; +}; + +using part_cert_bt = BoxObj<PartCert>; +using quorum_cert_bt = BoxObj<QuorumCert>; + +class PubKeyDummy: public PubKey { + PubKeyDummy *clone() override { return new PubKeyDummy(*this); } + void serialize(DataStream &) const override {} + void unserialize(DataStream &) override {} +}; + +class PrivKeyDummy: public PrivKey { + pubkey_bt get_pubkey() const override { return new PubKeyDummy(); } + void serialize(DataStream &) const override {} + void unserialize(DataStream &) override {} + void from_rand() override {} +}; + +class PartCertDummy: public PartCert { + uint256_t blk_hash; + public: + PartCertDummy() {} + PartCertDummy(const uint256_t &blk_hash): + blk_hash(blk_hash) {} + + void serialize(DataStream &s) const override { + s << (uint32_t)0 << blk_hash; + } + + void unserialize(DataStream &s) override { + uint32_t tmp; + s >> tmp >> blk_hash; + } + + PartCert *clone() override { + return new PartCertDummy(blk_hash); + } + + bool verify(const PubKey &) override { return true; } + + const uint256_t &get_blk_hash() const override { return blk_hash; } +}; + +class QuorumCertDummy: public QuorumCert { + uint256_t blk_hash; + public: + QuorumCertDummy() {} + QuorumCertDummy(const ReplicaConfig &, const uint256_t &blk_hash): + blk_hash(blk_hash) {} + + void serialize(DataStream &s) const override { + s << (uint32_t)1 << blk_hash; + } + + void unserialize(DataStream &s) override { + uint32_t tmp; + s >> tmp >> blk_hash; + } + + QuorumCert *clone() override { + return new QuorumCertDummy(*this); + } + + void add_part(ReplicaID, const PartCert &) override {} + void compute() override {} + bool verify(const ReplicaConfig &) override { return true; } + + const uint256_t &get_blk_hash() const override { return blk_hash; } +}; + + +class Secp256k1Context { + secp256k1_context *ctx; + friend class PubKeySecp256k1; + friend class SigSecp256k1; + public: + Secp256k1Context(bool sign = false): + ctx(secp256k1_context_create( + sign ? SECP256K1_CONTEXT_SIGN : + SECP256K1_CONTEXT_VERIFY)) {} + + Secp256k1Context(const Secp256k1Context &) = delete; + + Secp256k1Context(Secp256k1Context &&other): ctx(other.ctx) { + other.ctx = nullptr; + } + + ~Secp256k1Context() { + if (ctx) secp256k1_context_destroy(ctx); + } +}; + +using secp256k1_context_t = RcObj<Secp256k1Context>; + +extern secp256k1_context_t secp256k1_default_sign_ctx; +extern secp256k1_context_t secp256k1_default_verify_ctx; + +class PrivKeySecp256k1; + +class PubKeySecp256k1: public PubKey { + static const auto _olen = 33; + friend class SigSecp256k1; + secp256k1_pubkey data; + secp256k1_context_t ctx; + + public: + PubKeySecp256k1(const secp256k1_context_t &ctx = + secp256k1_default_sign_ctx): + PubKey(), ctx(ctx) {} + + PubKeySecp256k1(const bytearray_t &raw_bytes, + const secp256k1_context_t &ctx = + secp256k1_default_sign_ctx): + PubKeySecp256k1(ctx) { from_bytes(raw_bytes); } + + inline PubKeySecp256k1(const PrivKeySecp256k1 &priv_key, + const secp256k1_context_t &ctx = + secp256k1_default_sign_ctx); + + void serialize(DataStream &s) const override { + static uint8_t output[_olen]; + size_t olen = _olen; + (void)secp256k1_ec_pubkey_serialize( + ctx->ctx, (unsigned char *)output, + &olen, &data, SECP256K1_EC_COMPRESSED); + s.put_data(output, output + _olen); + } + + void unserialize(DataStream &s) override { + static const auto _exc = std::invalid_argument("ill-formed public key"); + try { + if (!secp256k1_ec_pubkey_parse( + ctx->ctx, &data, s.get_data_inplace(_olen), _olen)) + throw _exc; + } catch (std::ios_base::failure &) { + throw _exc; + } + } + + PubKeySecp256k1 *clone() override { + return new PubKeySecp256k1(*this); + } +}; + +class PrivKeySecp256k1: public PrivKey { + static const auto nbytes = 32; + friend class PubKeySecp256k1; + friend class SigSecp256k1; + uint8_t data[nbytes]; + secp256k1_context_t ctx; + + public: + PrivKeySecp256k1(const secp256k1_context_t &ctx = + secp256k1_default_sign_ctx): + PrivKey(), ctx(ctx) {} + + PrivKeySecp256k1(const bytearray_t &raw_bytes, + const secp256k1_context_t &ctx = + secp256k1_default_sign_ctx): + PrivKeySecp256k1(ctx) { from_bytes(raw_bytes); } + + void serialize(DataStream &s) const override { + s.put_data(data, data + nbytes); + } + + void unserialize(DataStream &s) override { + static const auto _exc = std::invalid_argument("ill-formed private key"); + try { + memmove(data, s.get_data_inplace(nbytes), nbytes); + } catch (std::ios_base::failure &) { + throw _exc; + } + } + + void from_rand() override { + if (!RAND_bytes(data, nbytes)) + throw std::runtime_error("cannot get rand bytes from openssl"); + } + + inline pubkey_bt get_pubkey() const override; +}; + +pubkey_bt PrivKeySecp256k1::get_pubkey() const { + return new PubKeySecp256k1(*this, ctx); +} + +PubKeySecp256k1::PubKeySecp256k1( + const PrivKeySecp256k1 &priv_key, + const secp256k1_context_t &ctx): PubKey(), ctx(ctx) { + if (!secp256k1_ec_pubkey_create(ctx->ctx, &data, priv_key.data)) + throw std::invalid_argument("invalid secp256k1 private key"); +} + +class SigSecp256k1: public Serializable { + secp256k1_ecdsa_signature data; + secp256k1_context_t ctx; + + void check_msg_length(const bytearray_t &msg) { + if (msg.size() != 32) + throw std::invalid_argument("the message should be 32-bytes"); + } + + public: + SigSecp256k1(const secp256k1_context_t &ctx = + secp256k1_default_sign_ctx): + Serializable(), ctx(ctx) {} + SigSecp256k1(const uint256_t &digest, + const PrivKeySecp256k1 &priv_key, + secp256k1_context_t &ctx = + secp256k1_default_sign_ctx): + Serializable(), ctx(ctx) { + sign(digest, priv_key); + } + + void serialize(DataStream &s) const override { + static uint8_t output[64]; + (void)secp256k1_ecdsa_signature_serialize_compact( + ctx->ctx, (unsigned char *)output, + &data); + s.put_data(output, output + 64); + } + + void unserialize(DataStream &s) override { + static const auto _exc = std::invalid_argument("ill-formed signature"); + try { + if (!secp256k1_ecdsa_signature_parse_compact( + ctx->ctx, &data, s.get_data_inplace(64))) + throw _exc; + } catch (std::ios_base::failure &) { + throw _exc; + } + } + + void sign(const bytearray_t &msg, const PrivKeySecp256k1 &priv_key) { + check_msg_length(msg); + if (!secp256k1_ecdsa_sign( + ctx->ctx, &data, + (unsigned char *)&*msg.begin(), + (unsigned char *)priv_key.data, + NULL, // default nonce function + NULL)) + throw std::invalid_argument("failed to create secp256k1 signature"); + } + + bool verify(const bytearray_t &msg, const PubKeySecp256k1 &pub_key, + const secp256k1_context_t &_ctx) { + check_msg_length(msg); + return secp256k1_ecdsa_verify( + _ctx->ctx, &data, + (unsigned char *)&*msg.begin(), + &pub_key.data) == 1; + } + + bool verify(const bytearray_t &msg, const PubKeySecp256k1 &pub_key) { + return verify(msg, pub_key, ctx); + } +}; + +class PartCertSecp256k1: public SigSecp256k1, public PartCert { + uint256_t blk_hash; + + public: + PartCertSecp256k1() = default; + PartCertSecp256k1(const PrivKeySecp256k1 &priv_key, const uint256_t &blk_hash): + SigSecp256k1(blk_hash, priv_key), + PartCert(), + blk_hash(blk_hash) {} + + bool verify(const PubKey &pub_key) override { + return SigSecp256k1::verify(blk_hash, + static_cast<const PubKeySecp256k1 &>(pub_key), + secp256k1_default_verify_ctx); + } + + const uint256_t &get_blk_hash() const override { return blk_hash; } + + PartCertSecp256k1 *clone() override { + return new PartCertSecp256k1(*this); + } + + void serialize(DataStream &s) const override { + s << blk_hash; + this->SigSecp256k1::serialize(s); + } + + void unserialize(DataStream &s) override { + s >> blk_hash; + this->SigSecp256k1::unserialize(s); + } +}; + +class QuorumCertSecp256k1: public QuorumCert { + uint256_t blk_hash; + salticidae::Bits rids; + std::vector<SigSecp256k1> sigs; + + public: + QuorumCertSecp256k1() = default; + QuorumCertSecp256k1(const ReplicaConfig &config, const uint256_t &blk_hash); + + void add_part(ReplicaID rid, const PartCert &pc) override { + if (pc.get_blk_hash() != blk_hash) + throw std::invalid_argument("PartCert does match the block hash"); + if (!rids.get(rid)) + { + rids.set(rid); + sigs.push_back(static_cast<const PartCertSecp256k1 &>(pc)); + } + } + + void compute() override {} + + bool verify(const ReplicaConfig &config) override; + + const uint256_t &get_blk_hash() const override { return blk_hash; } + + QuorumCertSecp256k1 *clone() override { + return new QuorumCertSecp256k1(*this); + } + + void serialize(DataStream &s) const override { + s << blk_hash << rids; + for (const auto &sig: sigs) s << sig; + } + + void unserialize(DataStream &s) override { + s >> blk_hash >> rids; + sigs.resize(rids.size()); + for (auto &sig: sigs) s >> sig; + } +}; + +} + +#endif |