|
@@ -0,0 +1,178 @@ |
|
|
|
|
|
/** |
|
|
|
|
|
* @file decaf/crypto.hxx |
|
|
|
|
|
* @author Mike Hamburg |
|
|
|
|
|
* |
|
|
|
|
|
* @copyright |
|
|
|
|
|
* Copyright (c) 2015 Cryptography Research, Inc. \n |
|
|
|
|
|
* Released under the MIT License. See LICENSE.txt for license information. |
|
|
|
|
|
* |
|
|
|
|
|
* @brief Example cryptography using Decaf |
|
|
|
|
|
*/ |
|
|
|
|
|
#ifndef __DECAF_CRYPTO_HXX__ |
|
|
|
|
|
#define __DECAF_CRYPTO_HXX__ 1 |
|
|
|
|
|
|
|
|
|
|
|
#include <decaf.hxx> |
|
|
|
|
|
#include <decaf/shake.hxx> |
|
|
|
|
|
|
|
|
|
|
|
/** @cond internal */ |
|
|
|
|
|
#if __cplusplus >= 201103L |
|
|
|
|
|
#define NOEXCEPT noexcept |
|
|
|
|
|
#else |
|
|
|
|
|
#define NOEXCEPT throw() |
|
|
|
|
|
#endif |
|
|
|
|
|
/** @endcond */ |
|
|
|
|
|
|
|
|
|
|
|
/* TODO: decide on copy vs reference */ |
|
|
|
|
|
|
|
|
|
|
|
namespace decaf { |
|
|
|
|
|
|
|
|
|
|
|
template <typename Group> class PrivateKey; |
|
|
|
|
|
|
|
|
|
|
|
/** @brief A public key using a particular EC group */ |
|
|
|
|
|
template <typename Group> class PublicKey { |
|
|
|
|
|
private: |
|
|
|
|
|
/** @cond internal */ |
|
|
|
|
|
friend class PrivateKey<Group>; |
|
|
|
|
|
//const typename Group::Point p; |
|
|
|
|
|
const FixedArrayBuffer<Group::Point::SER_BYTES> ser; |
|
|
|
|
|
static const size_t CHALLENGE_BYTES = Group::Scalar::SER_BYTES; |
|
|
|
|
|
/** @endcond */ |
|
|
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
|
/** SHAKE instance size for sigs etc */ |
|
|
|
|
|
static const size_t SHAKE_BITS = 256; |
|
|
|
|
|
|
|
|
|
|
|
/** Size of a signature */ |
|
|
|
|
|
static const size_t SIG_BYTES = Group::Point::SER_BYTES + Group::Scalar::SER_BYTES; |
|
|
|
|
|
|
|
|
|
|
|
/** @brief Return a pointer to the serialized version of the point. */ |
|
|
|
|
|
inline operator FixedBlock<Group::Point::SER_BYTES>() const NOEXCEPT { |
|
|
|
|
|
return ser; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** @brief Set the public key to a point */ |
|
|
|
|
|
inline explicit PublicKey(const typename Group::Point &p) NOEXCEPT : ser(SecureBuffer(p)) {} |
|
|
|
|
|
|
|
|
|
|
|
/** @brief Get the private key for a given public key */ |
|
|
|
|
|
inline explicit PublicKey(const PrivateKey<Group> &priv) NOEXCEPT; |
|
|
|
|
|
|
|
|
|
|
|
/** @brief Read a private key from a string*/ |
|
|
|
|
|
inline explicit PublicKey(const FixedBlock<Group::Point::SER_BYTES> b) NOEXCEPT : ser(b) {} |
|
|
|
|
|
|
|
|
|
|
|
/** @brief Return the corresponding EC point */ |
|
|
|
|
|
inline typename Group::Point point() const throw(CryptoException) { |
|
|
|
|
|
return typename Group::Point(ser); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** @brief Verify a sig. TODO: nothrow version? */ |
|
|
|
|
|
inline void verify_shake(const SHAKE<SHAKE_BITS> &ctx_, const FixedBlock<SIG_BYTES> &sig) throw(CryptoException) { |
|
|
|
|
|
SHAKE<SHAKE_BITS> ctx(ctx_); |
|
|
|
|
|
ctx << ser << sig.slice(0,Group::Point::SER_BYTES); |
|
|
|
|
|
FixedBuffer<CHALLENGE_BYTES> challenge(ctx.output(CHALLENGE_BYTES)); |
|
|
|
|
|
challenge.debug_print("ch ver "); |
|
|
|
|
|
|
|
|
|
|
|
const typename Group::Point combo = point().non_secret_combo_with_base( |
|
|
|
|
|
challenge, |
|
|
|
|
|
sig.slice(Group::Point::SER_BYTES, Group::Scalar::SER_BYTES) |
|
|
|
|
|
); |
|
|
|
|
|
if (combo != typename Group::Point(sig.slice(0,Group::Point::SER_BYTES))) |
|
|
|
|
|
throw CryptoException(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** @brief Sign from a message. */ |
|
|
|
|
|
inline void verify(const Block &message, const FixedBlock<SIG_BYTES> &sig) throw(CryptoException) { |
|
|
|
|
|
SHAKE<SHAKE_BITS> ctx; |
|
|
|
|
|
ctx << message; |
|
|
|
|
|
verify_shake(ctx,sig); |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** @brief A private key using a particular EC group */ |
|
|
|
|
|
template <typename Group> class PrivateKey { |
|
|
|
|
|
public: |
|
|
|
|
|
/** Size of associated symmetric key */ |
|
|
|
|
|
static const size_t SYM_BYTES = 32; |
|
|
|
|
|
|
|
|
|
|
|
/** SHAKE instance size for sigs etc */ |
|
|
|
|
|
static const size_t SHAKE_BITS = PublicKey<Group>::SHAKE_BITS; |
|
|
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
|
/** @cond internal */ |
|
|
|
|
|
static const size_t SCALAR_HASH_BYTES = Group::Scalar::SER_BYTES + 8; |
|
|
|
|
|
friend class PublicKey<Group>; |
|
|
|
|
|
const FixedArrayBuffer<SYM_BYTES> sym; |
|
|
|
|
|
const typename Group::Scalar scalar; |
|
|
|
|
|
const PublicKey<Group> pub_; |
|
|
|
|
|
/** @endcond */ |
|
|
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
|
/** @brief Construct at random */ |
|
|
|
|
|
inline PrivateKey(Rng &r) : |
|
|
|
|
|
sym(r), |
|
|
|
|
|
scalar(SHAKE<SHAKE_BITS>::hash(sym, SCALAR_HASH_BYTES)), |
|
|
|
|
|
pub_(SecureBuffer(Group::Precomputed::base() * scalar)) {} |
|
|
|
|
|
|
|
|
|
|
|
/** @brief Construct from buffer */ |
|
|
|
|
|
inline PrivateKey(const FixedBlock<SYM_BYTES> &sym_) : |
|
|
|
|
|
sym(sym_), |
|
|
|
|
|
scalar(SHAKE<SHAKE_BITS>::hash(sym, SCALAR_HASH_BYTES)), |
|
|
|
|
|
pub_(SecureBuffer(Group::Precomputed::Base * scalar)) {} |
|
|
|
|
|
|
|
|
|
|
|
/** @brief Compressed representation */ |
|
|
|
|
|
inline const FixedBlock<SYM_BYTES> &ser_compressed() const NOEXCEPT { |
|
|
|
|
|
return sym; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** @brief Uncompressed representation */ |
|
|
|
|
|
inline SecureBuffer ser_uncompressed() const throw(std::bad_alloc) { |
|
|
|
|
|
SecureBuffer b(SYM_BYTES + Group::Scalar::SER_BYTES + Group::Point::SER_BYTES); |
|
|
|
|
|
b.slice(0,SYM_BYTES).assign(sym); |
|
|
|
|
|
b.slice(SYM_BYTES,Group::Scalar::SER_BYTES).assign(scalar); |
|
|
|
|
|
b.slice(SYM_BYTES+Group::Scalar::SER_BYTES,Group::Point::SER_BYTES).assign(pub_.ser); |
|
|
|
|
|
return b; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** @brief Sign from a SHAKE context. TODO: double check random oracle eval of this; destructive version? */ |
|
|
|
|
|
inline SecureBuffer sign_shake(const SHAKE<SHAKE_BITS> &ctx_) NOEXCEPT { |
|
|
|
|
|
SHAKE<SHAKE_BITS> ctx(ctx_); |
|
|
|
|
|
ctx << sym << "decaf_255_sign_shake"; |
|
|
|
|
|
typename Group::Scalar nonce(ctx.output(SCALAR_HASH_BYTES)); |
|
|
|
|
|
SecureBuffer g_nonce(Group::Precomputed::base() * nonce); /* FIXME: make output fixed size, avoid std::bad_alloc */ |
|
|
|
|
|
|
|
|
|
|
|
ctx = ctx_; |
|
|
|
|
|
ctx << pub_.ser << g_nonce; |
|
|
|
|
|
FixedBuffer<PublicKey<Group>::CHALLENGE_BYTES> challenge(ctx.output(PublicKey<Group>::CHALLENGE_BYTES)); |
|
|
|
|
|
challenge.debug_print("ch sign"); |
|
|
|
|
|
SecureBuffer response(nonce - scalar * challenge); |
|
|
|
|
|
|
|
|
|
|
|
SecureBuffer ret(PublicKey<Group>::SIG_BYTES); |
|
|
|
|
|
ret.slice(0,Group::Point::SER_BYTES).assign(g_nonce); |
|
|
|
|
|
ret.slice(Group::Point::SER_BYTES, Group::Scalar::SER_BYTES).assign(response); |
|
|
|
|
|
return ret; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** @brief Sign from a message. */ |
|
|
|
|
|
inline SecureBuffer sign(const Block &message) { |
|
|
|
|
|
SHAKE<SHAKE_BITS> ctx; |
|
|
|
|
|
ctx << message; |
|
|
|
|
|
return sign_shake(ctx); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** @brief Get the corresponding public key */ |
|
|
|
|
|
inline const PublicKey<Group> &pub() const { return pub_; } |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** @cond internal */ |
|
|
|
|
|
template <typename Group> |
|
|
|
|
|
inline PublicKey<Group>::PublicKey( |
|
|
|
|
|
const PrivateKey<Group> &priv |
|
|
|
|
|
) NOEXCEPT : ser(priv.pub_.ser){} |
|
|
|
|
|
/** @endcond */ |
|
|
|
|
|
|
|
|
|
|
|
#undef NOEXCEPT |
|
|
|
|
|
} /* namespace decaf */ |
|
|
|
|
|
|
|
|
|
|
|
#endif /* __DECAF_CRYPTO_HXX__ */ |
|
|
|
|
|
|