void lisk_sign_message(const HDNode *node, const LiskSignMessage *msg, LiskMessageSignature *resp) { layoutSignMessage(msg->message.bytes, msg->message.size); if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); layoutHome(); return; } layoutProgressSwipe(_("Signing"), 0); uint8_t signature[64]; uint8_t hash[32]; lisk_message_hash(msg->message.bytes, msg->message.size, hash); ed25519_sign(hash, 32, node->private_key, &node->public_key[1], signature); memcpy(resp->signature.bytes, signature, sizeof(signature)); memcpy(resp->public_key.bytes, &node->public_key[1], 32); resp->has_signature = true; resp->signature.size = 64; resp->has_public_key = true; resp->public_key.size = 32; }
secure_vector<uint8_t> sign(RandomNumberGenerator&) override { secure_vector<uint8_t> sig(64); ed25519_sign(sig.data(), m_msg.data(), m_msg.size(), m_key.get_private_key().data()); m_msg.clear(); return sig; }
int pairing_session_get_signature(pairing_session_t *session, unsigned char signature[64]) { unsigned char sig_msg[64]; unsigned char key[16]; unsigned char iv[16]; AES_CTR_CTX aes_ctx; assert(session); if (session->status != STATUS_HANDSHAKE) { return -1; } /* First sign the public ECDH keys of both parties */ memcpy(&sig_msg[0], session->ecdh_ours, 32); memcpy(&sig_msg[32], session->ecdh_theirs, 32); ed25519_sign(signature, sig_msg, sizeof(sig_msg), session->ed_ours, session->ed_private); /* Then encrypt the result with keys derived from the shared secret */ derive_key_internal(session, (const unsigned char *) SALT_KEY, strlen(SALT_KEY), key, sizeof(key)); derive_key_internal(session, (const unsigned char *) SALT_IV, strlen(SALT_IV), iv, sizeof(key)); AES_ctr_set_key(&aes_ctx, key, iv, AES_MODE_128); AES_ctr_encrypt(&aes_ctx, signature, signature, 64); return 0; }
static void bench_ed25519_impl(void) { uint64_t start, end; const int iters = 1<<12; int i; const uint8_t msg[] = "but leaving, could not tell what they had heard"; ed25519_signature_t sig; ed25519_keypair_t kp; curve25519_keypair_t curve_kp; ed25519_public_key_t pubkey_tmp; ed25519_secret_key_generate(&kp.seckey, 0); start = perftime(); for (i = 0; i < iters; ++i) { ed25519_public_key_generate(&kp.pubkey, &kp.seckey); } end = perftime(); printf("Generate public key: %.2f usec\n", MICROCOUNT(start, end, iters)); start = perftime(); for (i = 0; i < iters; ++i) { ed25519_sign(&sig, msg, sizeof(msg), &kp); } end = perftime(); printf("Sign a short message: %.2f usec\n", MICROCOUNT(start, end, iters)); start = perftime(); for (i = 0; i < iters; ++i) { ed25519_checksig(&sig, msg, sizeof(msg), &kp.pubkey); } end = perftime(); printf("Verify signature: %.2f usec\n", MICROCOUNT(start, end, iters)); curve25519_keypair_generate(&curve_kp, 0); start = perftime(); for (i = 0; i < iters; ++i) { ed25519_public_key_from_curve25519_public_key(&pubkey_tmp, &curve_kp.pubkey, 1); } end = perftime(); printf("Convert public point from curve25519: %.2f usec\n", MICROCOUNT(start, end, iters)); curve25519_keypair_generate(&curve_kp, 0); start = perftime(); for (i = 0; i < iters; ++i) { ed25519_public_blind(&pubkey_tmp, &kp.pubkey, msg); } end = perftime(); printf("Blind a public key: %.2f usec\n", MICROCOUNT(start, end, iters)); }
void Page::resign(const std::array<uint8_t, Const::ED25519_KEY_LEN>& sk) { ed25519_public_key pk; ed25519_publickey(sk.data(), pk); Json::FastWriter writer; std::string data = writer.write(getCommonData()); const uint8_t* bytes = reinterpret_cast<const uint8_t*>(data.c_str()); ed25519_sign(bytes, data.size(), sk.data(), pk, pageSig_.data()); }
int main() { int i, res; ed25519_public_key pk; ed25519_signature sig; unsigned char forge[1024] = {'x'}; curved25519_key csk[2] = {{255}}; uint64_t ticks, pkticks = maxticks, signticks = maxticks, openticks = maxticks, curvedticks = maxticks; for (i = 0; i < 1024; i++) { ed25519_publickey(dataset[i].sk, pk); edassertequal(dataset[i].pk, pk, sizeof(pk), i, "public key didn't match"); ed25519_sign((unsigned char *)dataset[i].m, i, dataset[i].sk, pk, sig); edassertequal(dataset[i].sig, sig, sizeof(sig), i, "signature didn't match"); edassert(!ed25519_sign_open((unsigned char *)dataset[i].m, i, pk, sig), i, "failed to open message"); memcpy(forge, dataset[i].m, i); if (i) forge[i - 1] += 1; edassert(ed25519_sign_open(forge, (i) ? i : 1, pk, sig), i, "opened forged message"); } for (i = 0; i < 1024; i++) curved25519_scalarmult_basepoint(csk[(i & 1) ^ 1], csk[i & 1]); edassertequal(curved25519_expected, csk[0], sizeof(curved25519_key), 0, "curve25519 failed to generate correct value"); printf("success\n"); for (i = 0; i < 2048; i++) { timeit(ed25519_publickey(dataset[0].sk, pk), pkticks) edassertequal(dataset[0].pk, pk, sizeof(pk), i, "public key didn't match"); timeit(ed25519_sign((unsigned char *)dataset[0].m, 0, dataset[0].sk, pk, sig), signticks) edassertequal(dataset[0].sig, sig, sizeof(sig), i, "signature didn't match"); timeit(res = ed25519_sign_open((unsigned char *)dataset[0].m, 0, pk, sig), openticks) edassert(!res, 0, "failed to open message"); timeit(curved25519_scalarmult_basepoint(csk[1], csk[0]), curvedticks); } printf("%.0f ticks/public key generation\n", (double)pkticks); printf("%.0f ticks/signature\n", (double)signticks); printf("%.0f ticks/signature verification\n", (double)openticks); printf("%.0f ticks/curve25519 basepoint scalarmult\n", (double)curvedticks); return 0; }
/* * Visitor that signs an ed25519 condition if it has a matching public key */ static int ed25519Sign(CC *cond, CCVisitor visitor) { if (cond->type->typeId != CC_Ed25519Type.typeId) return 1; CCEd25519SigningData *signing = (CCEd25519SigningData*) visitor.context; if (0 != memcmp(cond->publicKey, signing->pk, 32)) return 1; if (!cond->signature) cond->signature = calloc(1,64); ed25519_sign(cond->signature, visitor.msg, visitor.msgLength, signing->pk, signing->skpk); signing->nSigned++; return 1; }
/** * @brief Derive an organizational signet from the corresponding private key structures. */ prime_org_signet_t * org_signet_generate(prime_org_key_t *org) { prime_org_signet_t *signet = NULL; stringer_t *signing = NULL, *encryption = NULL, *cryptographic = MANAGEDBUF(69); // Ensure the org structure contains the necessary private keys. if (!org || !org->encryption || !org->signing || org->signing->type != ED25519_PRIV) { return NULL; } else if (!(signet = mm_alloc(sizeof(prime_org_signet_t)))) { return NULL; } // Store the public singing, and encryption keys. else if (!(signing = ed25519_public_get(org->signing, NULL)) || !(encryption = secp256k1_public_get(org->encryption, NULL))) { log_pedantic("PRIME organizational signet generation failed, the public keys could not be derived from the provided private keys."); org_signet_free(signet); st_cleanup(signing); return NULL; } // Generate a serialized signet with the cryptographic fields. else if (st_write(cryptographic, prime_field_write(PRIME_ORG_SIGNET, 1, ED25519_KEY_PUB_LEN, signing, MANAGEDBUF(34)), prime_field_write(PRIME_ORG_SIGNET, 3, SECP256K1_KEY_PUB_LEN, encryption, MANAGEDBUF(35))) != 69) { log_pedantic("PRIME organizational signet generation failed, the serialized cryptographic signet could not be derived."); org_signet_free(signet); st_free(encryption); st_free(signing); return NULL; } // Generate a signature using the serialized cryptographic fields. else if (!(signet->signature = ed25519_sign(org->signing, cryptographic, NULL))) { log_pedantic("PRIME organizational signet generation failed, the cryptographic signet signature could not be derived."); org_signet_free(signet); st_free(encryption); st_free(signing); return NULL; } // Finally, convert the serialized public keys into usable structures. else if (!(signet->signing = ed25519_public_set(signing)) || !(signet->encryption = secp256k1_public_set(encryption))) { log_pedantic("PRIME organizational signet generation failed, the serialized public keys could not be parsed."); org_signet_free(signet); st_free(encryption); st_free(signing); return NULL; } // We no longer need the serialized public keys. st_free(encryption); st_free(signing); return signet; }
// given the bencoded buffer ``v``, the salt (which is optional and may have // a length of zero to be omitted), sequence number ``seq``, public key (32 // bytes ed25519 key) ``pk`` and a secret/private key ``sk`` (64 bytes ed25519 // key) a signature ``sig`` is produced. The ``sig`` pointer must point to // at least 64 bytes of available space. This space is where the signature is // written. signature sign_mutable_item( span<char const> v , span<char const> salt , sequence_number const seq , public_key const& pk , secret_key const& sk) { char str[1200]; int const len = canonical_string(v, seq, salt, str); return ed25519_sign({str, size_t(len)}, pk, sk); }
/** Create a fulfillment given a secret key and the message */ Ed25519::Ed25519 ( SecretKey const& secretKey, Slice message) { // First derive the public key, and place it in the // payload: ed25519_publickey ( secretKey.data(), payload_.data()); ed25519_sign ( message.data(), message.size(), secretKey.data(), payload_.data(), payload_.data() + pubkey_size_); }
//not modify std::string PrivateKey::Sign(const std::string &input) const { unsigned char sig[10240]; unsigned int sig_len = 0; if (type_ == SIGNTYPE_ED25519) { /* ed25519_signature sig;*/ ed25519_sign((unsigned char *)input.c_str(), input.size(), (const unsigned char*)raw_priv_key_.c_str(), (unsigned char*)pub_key_.GetRawPublicKey().c_str(), sig); sig_len = 64; } else if (type_ == SIGNTYPE_CFCASM2) { utils::EccSm2 key(utils::EccSm2::GetCFCAGroup()); key.From(raw_priv_key_); std::string r, s; return key.Sign("1234567812345678", input); } else{ LOG_ERROR("Unknown signature type(%d)", type_); } std::string output; output.append((const char *)sig, sig_len); return output; }
// given the bencoded buffer ``v``, the salt (which is optional and may have // a length of zero to be omitted), sequence number ``seq``, public key (32 // bytes ed25519 key) ``pk`` and a secret/private key ``sk`` (64 bytes ed25519 // key) a signature ``sig`` is produced. The ``sig`` pointer must point to // at least 64 bytes of available space. This space is where the signature is // written. void sign_mutable_item( std::pair<char const*, int> v, std::pair<char const*, int> salt, boost::uint64_t seq, char const* pk, char const* sk, char* sig) { #ifdef TORRENT_USE_VALGRIND VALGRIND_CHECK_MEM_IS_DEFINED(v.first, v.second); VALGRIND_CHECK_MEM_IS_DEFINED(sk, item_sk_len); VALGRIND_CHECK_MEM_IS_DEFINED(pk, item_pk_len); #endif char str[canonical_length]; int len = canonical_string(v, seq, salt, str); ed25519_sign((unsigned char*)sig, (unsigned char const*)str, len, (unsigned char const*)pk, (unsigned char const*)sk ); }
Ed25519::Ed25519 ( SecretKey const& secretKey, PublicKey const& publicKey, Slice message) { if (publicKeyType (publicKey) != KeyType::ed25519) LogicError ("An Ed25519 public key is required."); // When PublicKey wraps an Ed25519 key it prefixes // the key itself with a 0xED byte. We carefully // skip that byte. std::memcpy ( payload_.data(), publicKey.data() + 1, publicKey.size() - 1); // Now sign: ed25519_sign ( message.data(), message.size(), secretKey.data(), payload_.data(), payload_.data() + pubkey_size_); }
void c_crypto_ed25519::sign (const string &msg, unsigned char *signature) const { ed25519_sign(signature, reinterpret_cast<const unsigned char *>(msg.c_str()), msg.length(), public_key, private_key); }
int main(int argc, char *argv[]) { unsigned char public_key[32], private_key[64], seed[32], scalar[32]; unsigned char other_public_key[32], other_private_key[64]; unsigned char shared_secret[32], other_shared_secret[32]; unsigned char signature[64]; clock_t start; clock_t end; int i; /* create a random seed, and a keypair out of that seed */ ed25519_create_seed(seed); ed25519_create_keypair(public_key, private_key, seed); /* create signature on the message with the keypair */ ed25519_sign(signature, message, strlen(message), public_key, private_key); /* verify the signature */ if (ed25519_verify(signature, message, strlen(message), public_key)) { printf("valid signature\n"); } else { printf("invalid signature\n"); } /* create scalar and add it to the keypair */ ed25519_create_seed(scalar); ed25519_add_scalar(public_key, private_key, scalar); /* create signature with the new keypair */ ed25519_sign(signature, message, strlen(message), public_key, private_key); /* verify the signature with the new keypair */ if (ed25519_verify(signature, message, strlen(message), public_key)) { printf("valid signature\n"); } else { printf("invalid signature\n"); } /* make a slight adjustment and verify again */ signature[44] ^= 0x10; if (ed25519_verify(signature, message, strlen(message), public_key)) { printf("did not detect signature change\n"); } else { printf("correctly detected signature change\n"); } /* generate two keypairs for testing key exchange */ ed25519_create_seed(seed); ed25519_create_keypair(public_key, private_key, seed); ed25519_create_seed(seed); ed25519_create_keypair(other_public_key, other_private_key, seed); /* create two shared secrets - from both perspectives - and check if they're equal */ ed25519_key_exchange(shared_secret, other_public_key, private_key); ed25519_key_exchange(other_shared_secret, public_key, other_private_key); for (i = 0; i < 32; ++i) { if (shared_secret[i] != other_shared_secret[i]) { printf("key exchange was incorrect\n"); break; } } if (i == 32) { printf("key exchange was correct\n"); } /* test performance */ printf("testing seed generation performance: "); start = clock(); for (i = 0; i < 10000; ++i) { ed25519_create_seed(seed); } end = clock(); printf("%fus per seed\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); printf("testing key generation performance: "); start = clock(); for (i = 0; i < 10000; ++i) { ed25519_create_keypair(public_key, private_key, seed); } end = clock(); printf("%fus per keypair\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); printf("testing sign performance: "); start = clock(); for (i = 0; i < 10000; ++i) { ed25519_sign(signature, message, strlen(message), public_key, private_key); } end = clock(); printf("%fus per signature\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); printf("testing verify performance: "); start = clock(); for (i = 0; i < 10000; ++i) { ed25519_verify(signature, message, strlen(message), public_key); } end = clock(); printf("%fus per signature\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); printf("testing keypair scalar addition performance: "); start = clock(); for (i = 0; i < 10000; ++i) { ed25519_add_scalar(public_key, private_key, scalar); } end = clock(); printf("%fus per keypair\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); printf("testing public key scalar addition performance: "); start = clock(); for (i = 0; i < 10000; ++i) { ed25519_add_scalar(public_key, NULL, scalar); } end = clock(); printf("%fus per key\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); printf("testing key exchange performance: "); start = clock(); for (i = 0; i < 10000; ++i) { ed25519_key_exchange(shared_secret, other_public_key, private_key); } end = clock(); printf("%fus per shared secret\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); return 0; }
/** Helper for tor_cert_create(): signs any 32 bytes, not just an ed25519 * key. */ static tor_cert_t * tor_cert_sign_impl(const ed25519_keypair_t *signing_key, uint8_t cert_type, uint8_t signed_key_type, const uint8_t signed_key_info[32], time_t now, time_t lifetime, uint32_t flags) { tor_cert_t *torcert = NULL; ed25519_cert_t *cert = ed25519_cert_new(); cert->cert_type = cert_type; cert->exp_field = (uint32_t) CEIL_DIV(now + lifetime, 3600); cert->cert_key_type = signed_key_type; memcpy(cert->certified_key, signed_key_info, 32); if (flags & CERT_FLAG_INCLUDE_SIGNING_KEY) { ed25519_cert_extension_t *ext = ed25519_cert_extension_new(); ext->ext_type = CERTEXT_SIGNED_WITH_KEY; memcpy(ext->un_signing_key, signing_key->pubkey.pubkey, 32); ed25519_cert_add_ext(cert, ext); ++cert->n_extensions; } const ssize_t alloc_len = ed25519_cert_encoded_len(cert); tor_assert(alloc_len > 0); uint8_t *encoded = tor_malloc(alloc_len); const ssize_t real_len = ed25519_cert_encode(encoded, alloc_len, cert); if (real_len < 0) goto err; tor_assert(real_len == alloc_len); tor_assert(real_len > ED25519_SIG_LEN); uint8_t *sig = encoded + (real_len - ED25519_SIG_LEN); tor_assert(tor_mem_is_zero((char*)sig, ED25519_SIG_LEN)); ed25519_signature_t signature; if (ed25519_sign(&signature, encoded, real_len-ED25519_SIG_LEN, signing_key)<0) { log_warn(LD_BUG, "Can't sign certificate"); goto err; } memcpy(sig, signature.sig, ED25519_SIG_LEN); torcert = tor_cert_parse(encoded, real_len); if (! torcert) { log_warn(LD_BUG, "Generated a certificate we cannot parse"); goto err; } if (tor_cert_checksig(torcert, &signing_key->pubkey, now) < 0) { log_warn(LD_BUG, "Generated a certificate whose signature we can't check"); goto err; } tor_free(encoded); goto done; err: tor_cert_free(torcert); torcert = NULL; done: ed25519_cert_free(cert); tor_free(encoded); return torcert; }
static void test_crypto_ed25519_fuzz_donna(void *arg) { const unsigned iters = 1024; uint8_t msg[1024]; unsigned i; (void)arg; tt_uint_op(iters, OP_EQ, sizeof(msg)); crypto_rand((char*) msg, sizeof(msg)); /* Fuzz Ed25519-donna vs ref10, alternating the implementation used to * generate keys/sign per iteration. */ for (i = 0; i < iters; ++i) { const int use_donna = i & 1; uint8_t blinding[32]; curve25519_keypair_t ckp; ed25519_keypair_t kp, kp_blind, kp_curve25519; ed25519_public_key_t pk, pk_blind, pk_curve25519; ed25519_signature_t sig, sig_blind; int bit = 0; crypto_rand((char*) blinding, sizeof(blinding)); /* Impl. A: * 1. Generate a keypair. * 2. Blinded the keypair. * 3. Sign a message (unblinded). * 4. Sign a message (blinded). * 5. Generate a curve25519 keypair, and convert it to Ed25519. */ ed25519_set_impl_params(use_donna); tt_int_op(0, OP_EQ, ed25519_keypair_generate(&kp, i&1)); tt_int_op(0, OP_EQ, ed25519_keypair_blind(&kp_blind, &kp, blinding)); tt_int_op(0, OP_EQ, ed25519_sign(&sig, msg, i, &kp)); tt_int_op(0, OP_EQ, ed25519_sign(&sig_blind, msg, i, &kp_blind)); tt_int_op(0, OP_EQ, curve25519_keypair_generate(&ckp, i&1)); tt_int_op(0, OP_EQ, ed25519_keypair_from_curve25519_keypair( &kp_curve25519, &bit, &ckp)); /* Impl. B: * 1. Validate the public key by rederiving it. * 2. Validate the blinded public key by rederiving it. * 3. Validate the unblinded signature (and test a invalid signature). * 4. Validate the blinded signature. * 5. Validate the public key (from Curve25519) by rederiving it. */ ed25519_set_impl_params(!use_donna); tt_int_op(0, OP_EQ, ed25519_public_key_generate(&pk, &kp.seckey)); tt_mem_op(pk.pubkey, OP_EQ, kp.pubkey.pubkey, 32); tt_int_op(0, OP_EQ, ed25519_public_blind(&pk_blind, &kp.pubkey, blinding)); tt_mem_op(pk_blind.pubkey, OP_EQ, kp_blind.pubkey.pubkey, 32); tt_int_op(0, OP_EQ, ed25519_checksig(&sig, msg, i, &pk)); sig.sig[0] ^= 15; tt_int_op(-1, OP_EQ, ed25519_checksig(&sig, msg, sizeof(msg), &pk)); tt_int_op(0, OP_EQ, ed25519_checksig(&sig_blind, msg, i, &pk_blind)); tt_int_op(0, OP_EQ, ed25519_public_key_from_curve25519_public_key( &pk_curve25519, &ckp.pubkey, bit)); tt_mem_op(pk_curve25519.pubkey, OP_EQ, kp_curve25519.pubkey.pubkey, 32); } done: ; }
void lisk_sign_tx(const HDNode *node, LiskSignTx *msg, LiskSignedTx *resp) { lisk_update_raw_tx(node, msg); if (msg->has_transaction) { SHA256_CTX ctx; sha256_Init(&ctx); switch (msg->transaction.type) { case LiskTransactionType_Transfer: layoutRequireConfirmTx(msg->transaction.recipient_id, msg->transaction.amount); break; case LiskTransactionType_RegisterDelegate: layoutRequireConfirmDelegateRegistration(&msg->transaction.asset); break; case LiskTransactionType_CastVotes: layoutRequireConfirmCastVotes(&msg->transaction.asset); break; case LiskTransactionType_RegisterSecondPassphrase: layoutLiskPublicKey(msg->transaction.asset.signature.public_key.bytes); break; case LiskTransactionType_RegisterMultisignatureAccount: layoutRequireConfirmMultisig(&msg->transaction.asset); break; default: fsm_sendFailure(FailureType_Failure_DataError, _("Invalid transaction type")); layoutHome(); break; } if (!protectButton((msg->transaction.type == LiskTransactionType_RegisterSecondPassphrase ? ButtonRequestType_ButtonRequest_PublicKey : ButtonRequestType_ButtonRequest_SignTx), false)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled"); layoutHome(); return; } layoutRequireConfirmFee(msg->transaction.fee, msg->transaction.amount); if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled"); layoutHome(); return; } layoutProgressSwipe(_("Signing transaction"), 0); sha256_Update(&ctx, (const uint8_t *)&msg->transaction.type, 1); lisk_hashupdate_uint32(&ctx, msg->transaction.timestamp); sha256_Update(&ctx, msg->transaction.sender_public_key.bytes, 32); if (msg->transaction.has_requester_public_key) { sha256_Update(&ctx, msg->transaction.requester_public_key.bytes, msg->transaction.requester_public_key.size); } uint64_t recipient_id = 0; if (msg->transaction.has_recipient_id && msg->transaction.recipient_id[0] != 0) { // parse integer from lisk address ("123L" -> 123) for (size_t i = 0; i < strlen(msg->transaction.recipient_id) - 1; i++) { if (msg->transaction.recipient_id[i] < '0' || msg->transaction.recipient_id[i] > '9') { fsm_sendFailure(FailureType_Failure_DataError, _("Invalid recipient_id")); layoutHome(); return; } recipient_id *= 10; recipient_id += (msg->transaction.recipient_id[i] - '0'); } } lisk_hashupdate_uint64_be(&ctx, recipient_id); lisk_hashupdate_uint64_le(&ctx, msg->transaction.amount); lisk_hashupdate_asset(&ctx, msg->transaction.type, &msg->transaction.asset); // if signature exist calculate second signature if (msg->transaction.has_signature) { sha256_Update(&ctx, msg->transaction.signature.bytes, msg->transaction.signature.size); } uint8_t hash[32]; sha256_Final(&ctx, hash); ed25519_sign(hash, 32, node->private_key, &node->public_key[1], resp->signature.bytes); resp->has_signature = true; resp->signature.size = 64; } }