summaryrefslogtreecommitdiff
path: root/include/erebos/pairing.h
blob: 71c9288e6ace6ddf7e7bb09b10b9e5dd6e74947a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#pragma once

#include <erebos/identity.h>
#include <erebos/network.h>
#include <erebos/service.h>

#include <future>
#include <map>
#include <mutex>
#include <string>
#include <variant>
#include <vector>

namespace erebos {

using std::function;
using std::future;
using std::map;
using std::mutex;
using std::promise;
using std::string;
using std::variant;
using std::vector;

/**
 * Template-less base class for the paring functionality that does not depend
 * on the result parameter.
 */
class PairingServiceBase : public Service
{
public:
	enum class Outcome
	{
		Success,
		PeerRejected,
		UserRejected,
		UnexpectedMessage,
		NonceMismatch,
		Stale,
	};

	using RequestInitHook = function<void(const Peer &)>;
	using ConfirmHook = function<future<bool>(const Peer &, string, future<Outcome> &&)>;
	using RequestNonceFailedHook = function<void(const Peer &)>;

	class Config
	{
	public:
		Config & onRequestInit(RequestInitHook);
		Config & onResponse(PairingServiceBase::ConfirmHook);
		Config & onRequest(PairingServiceBase::ConfirmHook);
		Config & onRequestNonceFailed(RequestNonceFailedHook);

	private:
		friend class PairingServiceBase;
		RequestInitHook requestInitHook;
		ConfirmHook responseHook;
		ConfirmHook requestHook;
		RequestNonceFailedHook requestNonceFailedHook;
	};

	PairingServiceBase(Config &&);
	virtual ~PairingServiceBase();

protected:
	void requestPairing(UUID serviceId, const Peer & peer);
	virtual void handle(Context &) override;
	virtual Ref handlePairingCompleteRef(const Peer &) = 0;
	virtual void handlePairingResult(Context &) = 0;

private:
	static vector<uint8_t> nonceDigest(const Identity & id1, const Identity & id2,
			const vector<uint8_t> & nonce1, const vector<uint8_t> & nonce2);
	static string confirmationNumber(const vector<uint8_t> &);

	const Config config;
	optional<Ref> result;

	enum class StatePhase {
		NoPairing,
		OurRequest,
		OurRequestConfirm,
		OurRequestReady,
		PeerRequest,
		PeerRequestConfirm,
		PairingDone,
		PairingFailed
	};

	struct State {
		mutex lock;
		StatePhase phase;
		optional<Identity> idReq;
		optional<Identity> idRsp;
		vector<uint8_t> nonce;
		vector<uint8_t> peerCheck;
		promise<Outcome> outcome;
	};

	map<Peer, shared_ptr<State>> peerStates;
	mutex stateLock;

	void waitForConfirmation(Peer peer, weak_ptr<State> state, string confirm, ConfirmHook hook);
};

template<class Result>
class PairingService : public PairingServiceBase
{
public:
	PairingService(Config && config):
		PairingServiceBase(move(config)) {}

protected:
	virtual Stored<Result> handlePairingComplete(const Peer &) = 0;
	virtual void handlePairingResult(Context &, Stored<Result>) = 0;

	virtual Ref handlePairingCompleteRef(const Peer &) override final;
	virtual void handlePairingResult(Context &) override final;
};

template<class Result>
Ref PairingService<Result>::handlePairingCompleteRef(const Peer & peer)
{
	return handlePairingComplete(peer).ref();
}

template<class Result>
void PairingService<Result>::handlePairingResult(Context & ctx)
{
	handlePairingResult(ctx, Stored<Result>::load(ctx.ref()));
}

}