Packages
elixium_core
0.2.13
0.6.3
0.6.2
0.6.1
0.6.0
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.1
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.4
0.1.3
The core package for the Elixium blockchain, containing all the modules needed to run the chain
Current section
Files
Jump to
Current section
Files
lib/p2p/authentication.ex
defmodule Elixium.P2P.Authentication do
alias Elixium.P2P.GhostProtocol.Message
alias Elixium.Store.Oracle
@moduledoc """
SRP authentication handshakes and generation of credentials
"""
@spec load_credentials(String.t(), integer, pid) :: {bitstring, bitstring}
def load_credentials(ip, port, peer_oracle) do
ip = List.to_string(ip) <> ":" <> Integer.to_string(port)
case Oracle.inquire(peer_oracle, {:load_self, [ip]}) do
:not_found -> generate_and_store_credentials(ip, peer_oracle)
{identifier, password} -> {identifier, password}
end
end
@spec generate_and_store_credentials(String.t(), pid) :: {bitstring, bitstring}
defp generate_and_store_credentials(ip, peer_oracle) do
{identifier, password} = {:crypto.strong_rand_bytes(32), :crypto.strong_rand_bytes(32)}
Oracle.inquire(peer_oracle, {:save_self, [identifier, password, ip]})
{identifier, password}
end
# If this node has never communicated with a given peer, it will first
# need to identify itself.
@spec outbound_new_peer(reference, {bitstring, bitstring}) :: bitstring
def outbound_new_peer(peer, {identifier, password}) do
{prime, generator} = Strap.prime_group(1024)
prime = Base.encode64(prime)
salt =
32
|> :crypto.strong_rand_bytes()
|> Base.encode64()
client =
:srp6a
|> Strap.protocol(prime, generator)
|> Strap.client(identifier, password, salt)
verifier =
client
|> Strap.verifier()
|> Base.encode64()
public_value =
client
|> Strap.public_value()
|> Base.encode64()
identifier = Base.encode64(identifier)
"HANDSHAKE"
|> Message.build(%{
prime: prime,
generator: generator,
salt: salt,
verifier: verifier,
public_value: public_value,
identifier: identifier
})
|> Message.send(peer)
%{public_value: peer_public_value} = Message.read(peer)
{:ok, peer_public_value} = Base.decode64(peer_public_value)
{:ok, shared_master_key} = Strap.session_key(client, peer_public_value)
shared_master_key
end
# Handle incoming authentication messages from peers, and save their
# identity to the database for later
@spec outbound_peer(reference, {bitstring, bitstring}) :: bitstring
def outbound_peer(peer, {identifier, password}) do
encoded_id = Base.encode64(identifier)
"HANDSHAKE"
|> Message.build(%{identifier: encoded_id})
|> Message.send(peer)
%{prime: prime, generator: generator, salt: salt, public_value: peer_public_value} =
Message.read(peer)
{:ok, peer_public_value} = Base.decode64(peer_public_value)
client =
Strap.protocol(:srp6a, prime, generator)
|> Strap.client(identifier, password, salt)
public_value =
client
|> Strap.public_value()
|> Base.encode64()
"HANDSHAKE"
|> Message.build(%{public_value: public_value})
|> Message.send(peer)
{:ok, shared_master_key} = Strap.session_key(client, peer_public_value)
shared_master_key
end
@spec inbound_new_peer(map, reference, pid) :: bitstring
def inbound_new_peer(message, socket, peer_oracle) do
%{
public_value: peer_public_value,
generator: generator,
prime: prime,
verifier: peer_verifier,
salt: salt,
identifier: peer_identifier
} = message
{:ok, peer_verifier} = Base.decode64(peer_verifier)
{:ok, peer_public_value} = Base.decode64(peer_public_value)
server =
:srp6a
|> Strap.protocol(prime, generator)
|> Strap.server(peer_verifier)
server_public_value =
server
|> Strap.public_value()
|> Base.encode64()
"HANDSHAKE_AUTH"
|> Message.build(%{public_value: server_public_value})
|> Message.send(socket)
{:ok, shared_master_key} = Strap.session_key(server, peer_public_value)
# Now that we've successfully authenticated the peer, we save this data for use
# in future authentications
Oracle.inquire(peer_oracle, {:register_peer, [{peer_identifier, salt, prime, generator, peer_verifier}]})
shared_master_key
end
# Using the identifier, find the verifier, generator, and prime for a peer we know
# and then communicate back and forth with them until we've verified them
@spec inbound_peer(String.t(), reference, pid) :: bitstring
def inbound_peer(identifier, socket, peer_oracle) do
{salt, prime, generator, peer_verifier} = Oracle.inquire(peer_oracle, {:load_peer, [identifier]})
# Necesarry in order to generate the public value & session key
server =
Strap.protocol(:srp6a, prime, generator)
|> Strap.server(peer_verifier)
# Our public value. The peer will need this in order to generate the derived
# session key
public_value =
server
|> Strap.public_value()
|> Base.encode64()
"HANDSHAKE_CHALLENGE"
|> Message.build(%{
salt: salt,
prime: prime,
generator: generator,
public_value: public_value
})
|> Message.send(socket)
%{public_value: peer_public_value} = Message.read(socket)
{:ok, peer_public_value} = Base.decode64(peer_public_value)
{:ok, shared_master_key} = Strap.session_key(server, peer_public_value)
shared_master_key
end
end