dashhive / bls

MIT License
0 stars 0 forks source link

Create a standalone CLI tool that generates BLS keys #1

Closed wmerfalen closed 1 year ago

wmerfalen commented 1 year ago

Repos

Summary (from Walker Ford)

The dash-cli tool is implemented in the dash monolith repo. The bls generate function calls into the Chia network's BLS implementation (bls-signatures), which is a wrapper over the relic crypto toolkit.

Calling dash-cli bls generate will invoke the function ble_generate() in the dash repo, which will generate a new key with MakeNewKey() and then output the secret and public keys.

MakeNewKey() creates a random seed with GetStrongRandBytes().

From the seed, the private key is generated through bls::PrivateKey::FromBytes(), in the bls-signatures repo.

FromBytes() calls into the relic repo with several functions, inlcuding bn_read_bin(), bn_new(), g1_get_ord().

The public key is derived from the private key with the function CBLSSecretKey::GetPublicKey() in the dash repo, which calls PrivateKey::GetG1Element() in the bls-signatures repo.

GetG1Eleent() calls into the relic repo with several functions, including g1_mul_gen(), which creates the public key.

All of the BLS magic is happening in those relic functions.

Once those functions are understood, it might be worth comparing against the pure JS or pure GO BLS libraries:

Implmeentation

Implementation of dash-cli bls generate.

Relevant code has been copy/pasted from each of the three reposistories. Most, but not every, relic function is present.

// rpcevo.cpp (dash)
// line 1206
UniValue bls_generate(const JSONRPCRequest& request)
{
    if (request.fHelp || request.params.size() != 1) {
        bls_generate_help();
    }

    CBLSSecretKey sk;
    sk.MakeNewKey();

    UniValue ret(UniValue::VOBJ);
    ret.pushKV("secret", sk.ToString());
    ret.pushKV("public", sk.GetPublicKey().ToString());
    return ret;
}
// bls.cpp (dash)
// line 59
void CBLSSecretKey::MakeNewKey()
{
    unsigned char buf[32];
    while (true) {
        GetStrongRandBytes(buf, sizeof(buf));
        try {
            impl = bls::PrivateKey::FromBytes(bls::Bytes((const uint8_t*)buf, SerSize));
            break;
        } catch (...) {
        }
    }
    fValid = true;
    cachedHash.SetNull();
}
// random.cpp (dash)
// line 318
void GetStrongRandBytes(unsigned char* out, int num)
{
    assert(num <= 32);
    CSHA512 hasher;
    unsigned char buf[64];

    // First source: OpenSSL's RNG
    RandAddSeedPerfmon();
    GetRandBytes(buf, 32);
    hasher.Write(buf, 32);

    // Second source: OS RNG
    GetOSRand(buf);
    hasher.Write(buf, 32);

    // Third source: HW RNG, if available.
    if (GetHWRand(buf)) {
        hasher.Write(buf, 32);
    }

    // Combine with and update state
    {
        std::unique_lock<std::mutex> lock(cs_rng_state);
        hasher.Write(rng_state, sizeof(rng_state));
        hasher.Write((const unsigned char*)&rng_counter, sizeof(rng_counter));
        ++rng_counter;
        hasher.Finalize(buf);
        memcpy(rng_state, buf + 32, 32);
    }

    // Produce output
    memcpy(out, buf, num);
    memory_cleanse(buf, 64);
}
// util.hpp
// line 27
namespace bls {

class BLS;

class Bytes {
    const uint8_t* pData;
    const size_t nSize;

public:
    explicit Bytes(const uint8_t* pDataIn, const size_t nSizeIn)
        : pData(pDataIn), nSize(nSizeIn)
    {
    }
    explicit Bytes(const std::vector<uint8_t>& vecBytes)
        : pData(vecBytes.data()), nSize(vecBytes.size())
    {
    }

    inline const uint8_t* begin() const { return pData; }
    inline const uint8_t* end() const { return pData + nSize; }

    inline size_t size() const { return nSize; }

    const uint8_t& operator[](const int nIndex) const { return pData[nIndex]; }
};
// ...
}
// privatekey.cpp (bls-signatures)
// line 21
PrivateKey PrivateKey::FromBytes(const Bytes& bytes, bool modOrder)
{
    if (bytes.size() != PRIVATE_KEY_SIZE) {
        throw std::invalid_argument("PrivateKey::FromBytes: Invalid size");
    }

    PrivateKey k;
    bn_read_bin(k.keydata, bytes.begin(), PrivateKey::PRIVATE_KEY_SIZE);
    bn_t ord;
    bn_new(ord);
    g1_get_ord(ord);
    if (modOrder) {
        bn_mod_basic(k.keydata, k.keydata, ord);
    } else {
        if (bn_cmp(k.keydata, ord) > 0) {
            throw std::invalid_argument(
                "PrivateKey byte data must be less than the group order");
        }
    }
    return k;
}
// bls.h (dash)
// line 225
class CBLSSecretKey : public CBLSWrapper<bls::PrivateKey, BLS_CURVE_SECKEY_SIZE, CBLSSecretKey>

// line 38
template <typename ImplType, size_t _SerSize, typename C>
class CBLSWrapper
{
    friend class CBLSSecretKey;
    friend class CBLSPublicKey;
    friend class CBLSSignature;

    bool fLegacy;

protected:
    ImplType impl;
    bool fValid{false};
    mutable uint256 cachedHash;
// ...
}

// line 187
inline std::string ToString() const
{
    std::vector<uint8_t> buf = ToByteVector();
    return HexStr(buf.begin(), buf.end());
}
// utilstrencodings.h (dash)
// line 111
template<typename T>
std::string HexStr(const T itbegin, const T itend, bool fSpaces=false)
{
    std::string rv;
    static const char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
                                     '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
    rv.reserve((itend-itbegin)*3);
    for(T it = itbegin; it < itend; ++it)
    {
        unsigned char val = (unsigned char)(*it);
        if(fSpaces && it != itbegin)
            rv.push_back(' ');
        rv.push_back(hexmap[val>>4]);
        rv.push_back(hexmap[val&15]);
    }

    return rv;
}
// bls.cpp (dash)
// line 104
CBLSPublicKey CBLSSecretKey::GetPublicKey() const
{
    if (!IsValid()) {
        return CBLSPublicKey();
    }

    CBLSPublicKey pubKey;
    pubKey.impl = impl.GetG1Element(); // CBLSSecretKey::impl is a templated type: bls::PrivateKey
    pubKey.fValid = true;
    pubKey.cachedHash.SetNull();
    return pubKey;
}
// privatekey.cpp (bls-signatures)
// line 103
const G1Element& PrivateKey::GetG1Element() const
{
    if (!fG1CacheValid) {
        CheckKeyData();
        g1_st *p = Util::SecAlloc<g1_st>(1);
        g1_mul_gen(p, keydata);

        g1Cache = G1Element::FromNative(p);
        Util::SecFree(p);
        fG1CacheValid = true;
    }
    return g1Cache;
}
// elements.cpp (bls-signatures)
// line 83
G1Element G1Element::FromNative(const g1_t element)
{
    G1Element ele;
    g1_copy(ele.p, element);
    ele.CheckValid();
    return ele;
}
// relic_bin_util.c (relic)
// line 442
void bn_read_bin(bn_t a, const uint8_t *bin, int len) {
    int i, j;
    dig_t d = (RLC_DIG / 8);
    int digs = (len % d == 0 ? len / d : len / d + 1);

    bn_grow(a, digs);
    bn_zero(a);
    a->used = digs;

    for (i = 0; i < digs - 1; i++) {
        d = 0;
        for (j = (RLC_DIG / 8) - 1; j >= 0; j--) {
            d = d << 8;
            d |= bin[len - 1 - (i * (RLC_DIG / 8) + j)];
        }
        a->dp[i] = d;
    }
    d = 0;
    for (j = (RLC_DIG / 8) - 1; j >= 0; j--) {
        if ((int)(i * (RLC_DIG / 8) + j) < len) {
            d = d << 8;
            d |= bin[len - 1 - (i * (RLC_DIG / 8) + j)];
        }
    }
    a->dp[i] = d;

    a->sign = RLC_POS;
    bn_trim(a);
}
// relic_bn.h (relic)
// line 139
/**
 * Calls a function to allocate and initialize a multiple precision integer.
 *
 * @param[in,out] A         - the multiple precision integer to initialize.
 * @throw ERR_NO_MEMORY     - if there is no available memory.
 */
#if ALLOC == DYNAMIC
#define bn_new(A)                                                           \
    A = (bn_t)calloc(1, sizeof(bn_st));                                     \
    if ((A) == NULL) {                                                      \
        RLC_THROW(ERR_NO_MEMORY);                                           \
    }                                                                       \
    bn_make(A, RLC_BN_SIZE);                                                \

#elif ALLOC == AUTO
#define bn_new(A)                                                           \
    bn_make(A, RLC_BN_SIZE);                                                \

#endif
// relic_bn_mem.c (relic)
// line 44
void bn_make(bn_t a, int digits) {
    if (digits < 0) {
        RLC_THROW(ERR_NO_VALID);
    }
    /* Allocate at least one digit. */
    digits = RLC_MAX(digits, 1);

#if ALLOC == DYNAMIC
    if (digits % RLC_BN_SIZE != 0) {
        /* Pad the number of digits to a multiple of the block. */
        digits += (RLC_BN_SIZE - digits % RLC_BN_SIZE);
    }

    if (a != NULL) {
        a->dp = NULL;
#if ALIGN == 1
        a->dp = (dig_t *)malloc(digits * sizeof(dig_t));
#elif OPSYS == WINDOWS
        a->dp = _aligned_malloc(digits * sizeof(dig_t), ALIGN);
#else
        int r = posix_memalign((void **)&a->dp, ALIGN, digits * sizeof(dig_t));
        if (r == ENOMEM) {
            RLC_THROW(ERR_NO_MEMORY);
        }
        if (r == EINVAL) {
            RLC_THROW(ERR_NO_VALID);
        }
#endif /* ALIGN */
    }

    if (a->dp == NULL) {
        free((void *)a);
        RLC_THROW(ERR_NO_MEMORY);
    }
#else
    /* Verify if the number of digits is sane. */
    if (digits > RLC_BN_SIZE) {
        RLC_THROW(ERR_NO_PRECI);
        return;
    } else {
        digits = RLC_BN_SIZE;
    }
#endif
    if (a != NULL) {
        a->used = 1;
        a->dp[0] = 0;
        a->alloc = digits;
        a->sign = RLC_POS;
    }
}
// relic_pc.h (relic)
// line 224
/**
 * Old macros to preserve interface.
 * @{
 */
#define g1_get_ord          pc_get_ord

// line 217
/**
 * Returns the order of the groups G_1, G_2 and G_T.
 *
 * @param[out] N            0 the returned order.
 */
#define pc_get_ord(N)       RLC_CAT(RLC_G1_LOWER, curve_get_ord)(N)

// line 58
#define RLC_G1_LOWER            ep_
#define RLC_G1_UPPER            EP

// relic_util.h
// 154
/**
 * Concatenates two tokens.
 */
/** @{ */
#define RLC_CAT(A, B)           _RLC_CAT(A, B)
#define _RLC_CAT(A, B)          A ## B

// relic_ep_curve.c
// line 358
void ep_curve_get_ord(bn_t n) {
    bn_copy(n, &core_get()->ep_r);
}

// relic_bn_util.c
// line 49
void bn_copy(bn_t c, const bn_t a) {
    if (c->dp == a->dp) {
        return;
    }

    bn_grow(c, a->used);
    dv_copy(c->dp, a->dp, a->used);

    c->used = a->used;
    c->sign = a->sign;
    bn_trim(c);
}

// relic_core.c
// line 197
ctx_t *core_get(void) {
#if defined(MULTI)
    if (core_ctx == NULL && core_thread_initializer != NULL) {
        core_thread_initializer(core_init_ptr);
    }
#endif
    return core_ctx;
}

// line 96
#if MULTI
rlc_thread ctx_t *core_ctx = NULL;
#else
static ctx_t *core_ctx = NULL;
#endif

// relic_core.h
// line 135
typedef struct _ctx_t {
    /** The value returned by the last call, can be RLC_OK or RLC_ERR. */
    int code;

#ifdef CHECK
    /** The state of the last error caught. */
    sts_t *last;
    /** Error state to be used outside try-catch blocks. */
    sts_t error;
    /** Error number to be used outside try-catch blocks. */
    err_t number;
    /** The error message respective to the last error. */
    char *reason[ERR_MAX];
    /** A flag to indicate if the last error was already caught. */
    int caught;
#endif /* CHECK */

#ifdef WITH_FB
    /** Identifier of the currently configured binary field. */
    int fb_id;
    /** Irreducible binary polynomial. */
    fb_st fb_poly;
    /** Non-zero coefficients of a trinomial or pentanomial. */
    int fb_pa, fb_pb, fb_pc;
    /** Positions of the non-zero coefficients of trinomials or pentanomials. */
    int fb_na, fb_nb, fb_nc;
#if FB_TRC == QUICK || !defined(STRIP)
    /** Powers of z with non-zero traces. */
    int fb_ta, fb_tb, fb_tc;
#endif /* FB_TRC == QUICK */
#if FB_SLV == QUICK || !defined(STRIP)
    /** Table of precomputed half-traces. */
    fb_st fb_half[(RLC_DIG / 8 + 1) * RLC_FB_DIGS][16];
#endif /* FB_SLV == QUICK */
#if FB_SRT == QUICK || !defined(STRIP)
    /** Square root of z. */
    fb_st fb_srz;
#ifdef FB_PRECO
    /** Multiplication table for the polynomial z^(1/2). */
    fb_st fb_tab_srz[256];
#endif /* FB_PRECO */
#endif /* FB_SRT == QUICK */
#if FB_INV == ITOHT || !defined(STRIP)
    /** Stores an addition chain for (RLC_FB_BITS - 1). */
    int chain[RLC_TERMS + 1];
    /** Stores the length of the addition chain. */
    int chain_len;
    /** Tables for repeated squarings. */
    fb_st fb_tab_sqr[RLC_TERMS][RLC_FB_TABLE];
#endif /* FB_INV == ITOHT */
#endif /* WITH_FB */

#ifdef WITH_EB
    /** Identifier of the currently configured binary elliptic curve. */
    int eb_id;
    /** The a-coefficient of the elliptic curve. */
    fb_st eb_a;
    /** The b-coefficient of the elliptic curve. */
    fb_st eb_b;
    /** Optimization identifier for the a-coefficient. */
    int eb_opt_a;
    /** Optimization identifier for the b-coefficient. */
    int eb_opt_b;
    /** The generator of the elliptic curve. */
    eb_st eb_g;
    /** The order of the group of points in the elliptic curve. */
    bn_st eb_r;
    /** The cofactor of the group order in the elliptic curve. */
    bn_st eb_h;
    /** Flag that stores if the binary curve has efficient endomorphisms. */
    int eb_is_kbltz;
#ifdef EB_PRECO
    /** Precomputation table for generator multiplication. */
    eb_st eb_pre[RLC_EB_TABLE];
    /** Array of pointers to the precomputation table. */
    eb_st *eb_ptr[RLC_EB_TABLE];
#endif /* EB_PRECO */
#endif /* WITH_EB */

#ifdef WITH_FP
    /** Identifier of the currently configured prime field. */
    int fp_id;
    /** Prime modulus. */
    bn_st prime;
    /** Parameter for generating prime. */
    bn_st par;
    /** Parameter in sparse form. */
    int par_sps[RLC_TERMS + 1];
    /** Length of sparse prime representation. */
    int par_len;
#if FP_RDC == MONTY || !defined(STRIP)
    /** Value (R^2 mod p) for converting small integers to Montgomery form. */
    bn_st conv;
    /** Value of constant one in Montgomery form. */
    bn_st one;
#endif /* FP_RDC == MONTY */
#if FP_INV == JUMPDS || !defined(STRIP)
    /** Value of constant for divstep-based inversion. */
    bn_st inv;
#endif /* FP_INV */
    /** Prime modulus modulo 8. */
    dig_t mod8;
    /** Value derived from the prime used for modular reduction. */
    dig_t u;
    /** Quadratic non-residue. */
    int qnr;
    /** Cubic non-residue. */
    int cnr;
    /** 2-adicity. */
    int ad2;
#if FP_RDC == QUICK || !defined(STRIP)
    /** Sparse representation of prime modulus. */
    int sps[RLC_TERMS + 1];
    /** Length of sparse prime representation. */
    int sps_len;
#endif /* FP_RDC == QUICK */
#endif /* WITH_FP */

#ifdef WITH_EP
    /** Identifier of the currently configured prime elliptic curve. */
    int ep_id;
    /** The a-coefficient of the elliptic curve. */
    fp_st ep_a;
    /** The b-coefficient of the elliptic curve. */
    fp_st ep_b;
    /** The value 3b used in elliptic curve arithmetic. */
    fp_st ep_b3;
    /** The generator of the elliptic curve. */
    ep_st ep_g;
    /** The order of the group of points in the elliptic curve. */
    bn_st ep_r;
    /** The cofactor of the group order in the elliptic curve. */
    bn_st ep_h;
    /** The distinguished non-square used by the mapping function */
    fp_st ep_map_u;
    /** Precomputed constants for hashing. */
    fp_st ep_map_c[4];
#ifdef EP_ENDOM
#if EP_MUL == LWNAF || EP_FIX == COMBS || EP_FIX == LWNAF || EP_SIM == INTER || !defined(STRIP)
    /** Parameters required by the GLV method. @{ */
    fp_st beta;
    bn_st ep_v1[3];
    bn_st ep_v2[3];
    /** @} */
#endif /* EP_MUL */
#endif /* EP_ENDOM */
    /** Optimization identifier for the a-coefficient. */
    int ep_opt_a;
    /** Optimization identifier for the b-coefficient. */
    int ep_opt_b;
    /** Optimization identifier for the b3 value. */
    int ep_opt_b3;
    /** Flag that stores if the prime curve has efficient endomorphisms. */
    int ep_is_endom;
    /** Flag that stores if the prime curve is supersingular. */
    int ep_is_super;
    /** Flag that stores if the prime curve is pairing-friendly. */
    int ep_is_pairf;
    /** Flag that indicates whether this curve uses an isogeny for the SSWU mapping. */
    int ep_is_ctmap;
#ifdef EP_PRECO
    /** Precomputation table for generator multiplication. */
    ep_st ep_pre[RLC_EP_TABLE];
    /** Array of pointers to the precomputation table. */
    ep_st *ep_ptr[RLC_EP_TABLE];
#endif /* EP_PRECO */
#ifdef EP_CTMAP
    /** The isogeny map coefficients for the SSWU mapping. */
    iso_st ep_iso;
#endif /* EP_CTMAP */
#endif /* WITH_EP */

#ifdef WITH_EPX
    /** The generator of the elliptic curve. */
    ep2_t ep2_g;
    /** The 'a' coefficient of the curve. */
    fp2_t ep2_a;
    /** The 'b' coefficient of the curve. */
    fp2_t ep2_b;
    /** The order of the group of points in the elliptic curve. */
    bn_st ep2_r;
    /** The cofactor of the group order in the elliptic curve. */
    bn_st ep2_h;
    /** The distinguished non-square used by the mapping function */
    fp2_t ep2_map_u;
    /** The constants needed for hashing. */
    fp2_t ep2_map_c[4];
    /** The constants needed for Frobenius. */
    fp2_t ep2_frb[2];
    /** Optimization identifier for the a-coefficient. */
    int ep2_opt_a;
    /** Optimization identifier for the b-coefficient. */
    int ep2_opt_b;
    /** Flag that stores if the prime curve is a twist. */
    int ep2_is_twist;
    /** Flag that indicates whether this curve uses an isogeny for the SSWU mapping. */
    int ep2_is_ctmap;
#ifdef EP_PRECO
    /** Precomputation table for generator multiplication.*/
    ep2_st ep2_pre[RLC_EP_TABLE];
    /** Array of pointers to the precomputation table. */
    ep2_st *ep2_ptr[RLC_EP_TABLE];
#endif /* EP_PRECO */
#ifdef EP_CTMAP
    /** The isogeny map coefficients for the SSWU mapping. */
    iso2_st ep2_iso;
#endif /* EP_CTMAP */
    /** The generator of the elliptic curve. */
    ep4_t ep4_g;
    /** The 'a' coefficient of the curve. */
    fp4_t ep4_a;
    /** The 'b' coefficient of the curve. */
    fp4_t ep4_b;
    /** The order of the group of points in the elliptic curve. */
    bn_st ep4_r;
    /** The cofactor of the group order in the elliptic curve. */
    bn_st ep4_h;
    /** Optimization identifier for the a-coefficient. */
    int ep4_opt_a;
    /** Optimization identifier for the b-coefficient. */
    int ep4_opt_b;
    /** Flag that stores if the prime curve is a twist. */
    int ep4_is_twist;
#ifdef EP_PRECO
    /** Precomputation table for generator multiplication.*/
    ep4_st ep4_pre[RLC_EP_TABLE];
    /** Array of pointers to the precomputation table. */
    ep4_st *ep4_ptr[RLC_EP_TABLE];
    #endif /* EP_PRECO */
#endif /* WITH_EPX */

#ifdef WITH_ED
    /** Identifier of the currently configured Edwards elliptic curve. */
    int ed_id;
    /** The 'a' coefficient of the Edwards elliptic curve. */
    fp_st ed_a;
    /** The 'd' coefficient of the Edwards elliptic curve. */
    fp_st ed_d;
    /** Precomputed constants for hashing. */
    fp_st ed_map_c[4];
    /** The generator of the Edwards elliptic curve. */
    ed_st ed_g;
    /** The order of the group of points in the Edwards elliptic curve. */
    bn_st ed_r;
    /** The cofactor of the Edwards elliptic curve. */
    bn_st ed_h;

#ifdef ED_PRECO
    /** Precomputation table for generator multiplication. */
    ed_st ed_pre[RLC_ED_TABLE];
    /** Array of pointers to the precomputation table. */
    ed_st *ed_ptr[RLC_ED_TABLE];
#endif /* ED_PRECO */
#endif

#if defined(WITH_FPX) || defined(WITH_PP)
    /** Integer part of the quadratic non-residue. */
    dis_t qnr2;
    /** Constants for computing Frobenius maps in higher extensions. @{ */
    fp2_st fp2_p1[5];
    fp2_st fp2_p2[3];
    fp2_st fp4_p1;
    /** @} */
    /** Constants for computing Frobenius maps in higher extensions. @{ */
    int frb3[3];
    fp_st fp3_p0[2];
    fp_st fp3_p1[5];
    fp_st fp3_p2[2];
    /** @} */
#endif /* WITH_PP */

#if defined(WITH_PC)
    gt_t gt_g;
#endif

#if BENCH > 0
    /** Stores the time measured before the execution of the benchmark. */
    ben_t before;
    /** Stores the time measured after the execution of the benchmark. */
    ben_t after;
    /** Stores the sum of timings for the current benchmark. */
    long long total;
#ifdef OVERH
    /** Benchmarking overhead to be measured and subtracted from benchmarks. */
    long long over;
#endif
#endif

#if RAND != CALL
    /** Internal state of the PRNG. */
    uint8_t rand[RLC_RAND_SIZE];
#else
    void (*rand_call)(uint8_t *, int, void *);
    void *rand_args;
#endif
    /** Flag to indicate if PRNG is seed. */
    int seeded;
    /** Counter to keep track of number of calls since last seeding. */
    int counter;

#if TIMER == PERF
    /** File descriptor for perf system call. */
    int perf_fd;
    /** Buffer for storing perf data, */
    struct perf_event_mmap_page *perf_buf;
#endif
} ctx_t;