void ECDHEKeyExchange::exchange(bytes& o_exchange) { if (!m_ephemeralSecret) // didn't agree on public remote BOOST_THROW_EXCEPTION(InvalidState()); // The key exchange payload is in two parts and is encrypted // using ephemeral keypair. // // The first part is the 'prefix' which is a zero-knowledge proof // allowing the remote to resume or emplace a previous session. // If a session previously exists: // prefix is sha3(token) // todo: ephemeral entropy from both sides // If a session doesn't exist: // prefix is sha3(m_ephemeralSecret) // // The second part is encrypted using the public key which relates to the prefix. Public encpk = m_known.first ? m_known.first : m_remoteEphemeral; bytes exchange(encpk.asBytes()); // This is the public key which we would like the remote to use, // which maybe different than the previously-known public key. // // Here we should pick an appropriate alias or generate a new one, // but for now, we use static alias passed to constructor. // Public p = toPublic(m_alias.m_secret); exchange.resize(exchange.size() + sizeof(p)); memcpy(&exchange[exchange.size() - sizeof(p)], p.data(), sizeof(p)); // protocol parameters; should be fixed size bytes v(1, 0x80); exchange.resize(exchange.size() + v.size()); memcpy(&exchange[exchange.size() - v.size()], v.data(), v.size()); h256 auth; sha3mac(m_alias.m_secret.ref(), m_ephemeralSecret.ref(), auth.ref()); Signature sig = s_secp256k1.sign(m_alias.m_secret, auth); exchange.resize(exchange.size() + sizeof(sig)); memcpy(&exchange[exchange.size() - sizeof(sig)], sig.data(), sizeof(sig)); aes::AuthenticatedStream aes(aes::Encrypt, m_ephemeralSecret, 0); h256 prefix(sha3(m_known.second ? m_known.second : (h256)m_remoteEphemeral)); aes.update(prefix.ref()); s_secp256k1.encrypt(encpk, exchange); aes.update(&exchange); aes.streamOut(o_exchange); }
bool ecdh::agree(Secret const& _s, Public const& _r, Secret& o_s) noexcept { auto* ctx = getCtx(); static_assert(sizeof(Secret) == 32, "Invalid Secret type size"); secp256k1_pubkey rawPubkey; std::array<byte, 65> serializedPubKey{{0x04}}; std::copy(_r.asArray().begin(), _r.asArray().end(), serializedPubKey.begin() + 1); if (!secp256k1_ec_pubkey_parse(ctx, &rawPubkey, serializedPubKey.data(), serializedPubKey.size())) return false; // Invalid public key. // FIXME: We should verify the public key when constructed, maybe even keep // secp256k1_pubkey as the internal data of Public. std::array<byte, 33> compressedPoint; if (!secp256k1_ecdh_raw(ctx, compressedPoint.data(), &rawPubkey, _s.data())) return false; // Invalid secret key. std::copy(compressedPoint.begin() + 1, compressedPoint.end(), o_s.writable().data()); return true; }
void Secp256k1PP::agree(Secret const& _s, Public const& _r, Secret& o_s) { // TODO: mutex ASN1::secp256k1() singleton // Creating Domain is non-const for m_oid and m_oid is not thread-safe ECDH<ECP>::Domain d(ASN1::secp256k1()); assert(d.AgreedValueLength() == sizeof(o_s)); byte remote[65] = {0x04}; memcpy(&remote[1], _r.data(), 64); d.Agree(o_s.writable().data(), _s.data(), remote); }
Public Secp256k1PP::recover(Signature _signature, bytesConstRef _message) { Public recovered; Integer r(_signature.data(), 32); Integer s(_signature.data()+32, 32); // cryptopp encodes sign of y as 0x02/0x03 instead of 0/1 or 27/28 byte encodedpoint[33]; encodedpoint[0] = _signature[64] | 2; memcpy(&encodedpoint[1], _signature.data(), 32); ECP::Element x; { m_curve.DecodePoint(x, encodedpoint, 33); if (!m_curve.VerifyPoint(x)) return recovered; } // if (_signature[64] & 2) // { // r += m_q; // Guard l(x_params); // if (r >= m_params.GetMaxExponent()) // return recovered; // } Integer z(_message.data(), 32); Integer rn = r.InverseMod(m_q); Integer u1 = m_q - (rn.Times(z)).Modulo(m_q); Integer u2 = (rn.Times(s)).Modulo(m_q); ECP::Point p; byte recoveredbytes[65]; { // todo: make generator member p = m_curve.CascadeMultiply(u2, x, u1, m_params.GetSubgroupGenerator()); if (p.identity) return Public(); m_curve.EncodePoint(recoveredbytes, p, false); } memcpy(recovered.data(), &recoveredbytes[1], 64); return recovered; }
void Secp256k1PP::exportPublicKey(CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> const& _k, Public& o_p) { bytes prefixedKey(_k.GetGroupParameters().GetEncodedElementSize(true)); { Guard l(x_params); m_params.GetCurve().EncodePoint(prefixedKey.data(), _k.GetPublicElement(), false); assert(Public::size + 1 == _k.GetGroupParameters().GetEncodedElementSize(true)); } memcpy(o_p.data(), &prefixedKey[1], Public::size); }
void NodeTable::noteActiveNode(Public const& _pubk, bi::udp::endpoint const& _endpoint) { if (_pubk == m_node.address()) return; clog(NodeTableNote) << "Noting active node:" << _pubk.abridged() << _endpoint.address().to_string() << ":" << _endpoint.port(); shared_ptr<NodeEntry> node(addNode(_pubk, _endpoint, bi::tcp::endpoint(_endpoint.address(), _endpoint.port()))); // TODO p2p: old bug (maybe gone now) sometimes node is nullptr here if (!!node) { shared_ptr<NodeEntry> contested; { Guard l(x_state); NodeBucket& s = bucket_UNSAFE(node.get()); s.nodes.remove_if([&node](weak_ptr<NodeEntry> n) { if (n.lock() == node) return true; return false; }); if (s.nodes.size() >= s_bucketSize) { // It's only contested iff nodeentry exists contested = s.nodes.front().lock(); if (!contested) { s.nodes.pop_front(); s.nodes.push_back(node); s.touch(); } } else { s.nodes.push_back(node); s.touch(); } } if (contested) evict(contested, node); } }
Address dev::toAddress(Public const& _public) { return right160(sha3(_public.ref())); }