-
Notifications
You must be signed in to change notification settings - Fork 8
/
rtmfp.hpp
536 lines (435 loc) · 26.3 KB
/
rtmfp.hpp
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
#pragma once
// Copyright © 2021 Michael Thornburgh
// SPDX-License-Identifier: MIT
// Implementation of the Secure Real-Time Media Flow Protocol (RTMFP) as
// described in RFC 7016. Section references ("§") refer to that document.
#include <cmath>
#include <map>
#include "Address.hpp"
#include "IndexSet.hpp"
#include "List.hpp"
#include "WriteReceipt.hpp"
#include "Priority.hpp"
namespace com { namespace zenomt { namespace rtmfp {
class SendFlow; class RecvFlow;
class IPlatformAdapter;
class ICryptoAdapter; class SessionCryptoKey; class CryptoCert;
class Interface; class ISession; class Session; class StartupSession;
struct PacketAssembler;
using Bytes = std::vector<uint8_t>;
enum ReceiveOrder {
RO_SEQUENCE, // Original queuing order
RO_NETWORK, // Network arrival order
RO_HOLD // Suspend delivery
};
enum FIHelloResponseMode { FI_SEND_RHELLO, FI_SEND_REDIRECT, FI_IGNORE };
const int INTERFACE_ID_ALL = -1;
class RTMFP : public Object {
public:
RTMFP(IPlatformAdapter *platformAdapter, ICryptoAdapter *cryptoAdapter);
RTMFP() = delete;
~RTMFP();
// Open a new flow to Endpoint Discriminator epd having metadata with initial priority pri.
// Returns an empty shared_ptr on error.
std::shared_ptr<SendFlow> openFlow(const void *epd, size_t epdLen, const void *metadataBytes, size_t metadataLen, Priority pri = PRI_ROUTINE);
std::shared_ptr<SendFlow> openFlow(const Bytes &epd, const Bytes &metadata, Priority pri = PRI_ROUTINE);
// Called when a new non-associated receiving flow starts. flow must
// be accepted during this callback or it will be rejected automatically.
std::function<void(std::shared_ptr<RecvFlow> flow)> onRecvFlow;
// Shut down this RTMFP. If immediately is false, attempt an orderly close of all
// open sessions. If immediately is true, abruptly close all sessions.
// platform->onShutdownComplete() will be called when all sessions have been closed.
void shutdown(bool immediately = false);
// Called when an IHello (§2.3.2) is received having an Endpoint Discriminator that does
// not select this endpoint according to cryptoAdapter->isSelectedByEPD(). Used by
// Forwarders/Redirectors §3.5.1.4, §3.5.1.5, §3.5.1.6.
std::function<void(const void *epd, size_t epdLen, const void *tag, size_t tagLen, int interfaceID, const struct sockaddr *srcAddr)> onUnmatchedIHello;
// Send a Responder Redirect to dstAddr via interfaceID. §2.3.5, §3.5.1.1.1, §3.5.1.1.2, §3.5.1.4.
// use INTERFACE_ID_ALL to send the Redirect on all interfaces.
void sendResponderRedirect(const void *tag, size_t tagLen, const std::vector<Address> &addrs, int interfaceID, const struct sockaddr *dstAddr);
// Called when an RHello (§2.3.4) is received having a tag echo that does not match
// the tag for any opening session. Used by connectivity checkers or other applications
// that send synthetic IHellos outside of normal session startup.
std::function<void(const uint8_t *tag, size_t tagLen, const uint8_t *cookie, size_t cookieLen, const uint8_t *cert, size_t certLen, int interfaceID, const struct sockaddr *srcAddr)> onUnmatchedRHello;
// Manually send an IHello outside of a normal session startup. Used by connectivity
// checkers or other applications to probe far endpoints to elicit an RHello. Use
// INTERFACE_ID_ALL to send the IHello on all interfaces.
void sendIHello(const void *epd, size_t epdLen, const void *tag, size_t tagLen, int interfaceID, const struct sockaddr *dstAddr);
// New sessions inherit these values when transitioning to S_OPEN.
void setDefaultSessionKeepalivePeriod(Time keepalive);
Time getDefaultSessionKeepalivePeriod() const;
void setDefaultSessionRetransmitLimit(Time limit);
Time getDefaultSessionRetransmitLimit() const;
void setDefaultSessionIdleLimit(Time limit);
Time getDefaultSessionIdleLimit() const;
Time getCurrentTime() const; // Convenience method calls the platform adapter.
Time getInstanceAge() const; // Answer how many seconds since this RTMFP was created.
// --- Used by the Platform Adapter
// Answers how long until doTimerWork() should be called.
Time howLongToSleep() const;
// The platform must call this function ASAP after howLongToSleep() seconds.
void doTimerWork();
// Add a new platform interface to this RTMFP. An RTMFP must have at least one interface.
// interfaceID is a non-negative integer meaningful to the Platform Adapter. It need not
// be a socket file descriptor.
void addInterface(int interfaceID);
// void removeInterface(int interfaceID); // TODO: someday. what do we do with sessions bound to the interface?
// The platform calls this method when a new packet for this RTMFP is received.
// Answers true if this packet was for this instance, false if not recognized (for further processing)
bool onReceivePacket(const void *bytes, size_t len, int interfaceID, const struct sockaddr *addr, int tos);
protected:
friend class Interface;
friend class StartupSession;
friend class Session;
friend class ISession;
friend class SendFlow;
friend class RecvFlow;
bool scheduleWrite(int interfaceID, std::shared_ptr<ISession> session, int pri);
unsigned long getNextThreadNum();
std::shared_ptr<Timer> scheduleRel(Time delta, Time recurInterval = 0);
uint16_t getCurrentTimestamp() const;
Bytes makeCookie(const struct sockaddr *addr) const;
enum CookieCheck { COOKIE_BAD, COOKIE_MISMATCH, COOKIE_OK };
CookieCheck checkCookie(const uint8_t *cookie, size_t cookieLen, const struct sockaddr *addr) const;
void sendCookieChange(uint32_t sessionID, const uint8_t *cookie, size_t cookieLen, int interfaceID, const struct sockaddr *addr);
std::shared_ptr<Session> makeSession(std::shared_ptr<SessionCryptoKey>, unsigned role);
std::shared_ptr<Session> findOpeningSessionByAddress(const Address &addr);
std::shared_ptr<Session> findOpeningSessionByEPD(const Bytes &epd, const Session *exclude);
std::shared_ptr<Session> findOpeningSessionByTag(const Bytes &epd);
std::shared_ptr<Session> findOpenSessionByAddress(const Address &addr);
std::shared_ptr<Session> findOpenSessionByEPD(const Bytes &epd);
void makeRIKeyingChunk(Bytes &dst, uint32_t rsid, const Bytes &skrc, Bytes &skic, std::shared_ptr<CryptoCert> recipient);
bool tryRIKeyingRetransmit(int interfaceID, const Address &addr, uint32_t isid, const Bytes &skic);
void onSessionDidOpen(std::shared_ptr<Session> session);
void onSessionWillOpen(std::shared_ptr<Session> session);
void onSessionDidClose(std::shared_ptr<Session> session, bool releaseSessionID);
void onSessionAddressDidChange(std::shared_ptr<Session> session, const Address &oldAddr);
void onSessionReceivedTC(const void *session);
bool shouldSessionReportTCR(const void *session) const;
void sendFlowIsNotOpening(const std::shared_ptr<SendFlow> &flow);
void checkShutdownComplete();
void onIHello(const uint8_t *epd, size_t epdLen, const uint8_t *tag, size_t tagLen, int interfaceID, const struct sockaddr *addr, FIHelloResponseMode mode);
void onRHello(const uint8_t *tag, size_t tagLen, const uint8_t *cookie, size_t cookieLen, const uint8_t *cert, size_t certLen, int interfaceID, const struct sockaddr *addr);
void onIIKeying(uint32_t initiatorSessionID, const uint8_t *cookie, size_t cookieLen, const uint8_t *cert, size_t certLen, const uint8_t *skic, size_t skicLen, const uint8_t *signedParams, size_t signedParamsLen, const uint8_t *sig, size_t sigLen, int interfaceID, const struct sockaddr *addr);
void onRedirect(const uint8_t *tag, size_t tagLen, const std::vector<Address> &redirectDestinations);
IPlatformAdapter *m_platform;
ICryptoAdapter *m_crypto;
Time m_epoch;
TimerList m_timers;
uint8_t *m_plaintextBuf;
uint8_t *m_ciphertextBuf;
uint8_t m_secret[512/8]; // block size of SHA256
unsigned long m_nextThreadNum;
std::atomic_long m_waitingPerformCount;
uintptr_t m_sessionReportingTC;
Time m_sessionTC_time;
Time m_previous_sessionTC_time;
Time m_tc_sent_time;
Time m_default_session_keepalive_period;
Time m_default_session_retransmit_limit;
Time m_default_session_idle_limit;
bool m_shutdown;
bool m_shutdownComplete;
std::map<int, std::shared_ptr<Interface> > m_interfaces;
List<std::shared_ptr<Session> > m_sessions;
std::map<Address, std::shared_ptr<Session> > m_openSessionsByAddress;
std::map<Bytes, std::shared_ptr<Session> > m_openSessionsByCanonicalEPD;
List<std::shared_ptr<Session> > m_openingSessions;
List<std::shared_ptr<SendFlow> > m_openingFlows;
std::shared_ptr<StartupSession> m_startupSession;
};
class Flow : public Object {
public:
// Open a new flow in the same S_OPEN session. Returns an empty shared_ptr on error.
std::shared_ptr<SendFlow> openFlow(const void *metadataBytes, size_t metadataLen, Priority pri = PRI_ROUTINE);
std::shared_ptr<SendFlow> openFlow(const Bytes &metadata, Priority pri = PRI_ROUTINE);
virtual bool isOpen() const; // Answers true if this flow hasn't been closed (manually/exception/error).
virtual void close(); // Close this flow. No callbacks on this flow will be called after this.
void closeSession(); // Close the session to which this flow belongs. Does not count as closing this flow.
// These are all properties of the session.
Bytes getNearNonce() const; // §3.5
Bytes getFarNonce() const;
Bytes getFarCertificate() const;
Bytes getFarCanonicalEPD() const; // §3.2
Address getFarAddress() const; // §3.5 DESTADDR. Is there any need for the interface ID?
Time getSRTT() const; // Smoothed round-trip time per §3.5.2.2.
Time getSafeSRTT() const; // Returns a default value if no RTT measurement has been made yet.
Time getRTTVariance() const; // Round-trip time variance §3.5.2.2.
Time getLastRTT() const; // Most recent RTT measurement.
Time getBaseRTT() const; // Baseline (minimum recently observed) RTT.
size_t getCongestionWindow() const;
Time getERTO() const; // Effective Retransmission Timeout §3.5.2.2
Time getMRTO() const; // Measured Retransmission Timeout §3.5.2.2
void setSessionKeepalivePeriod(Time keepalive); // Idle time before a keepalive check is performed.
Time getSessionKeepalivePeriod() const;
void setSessionRetransmitLimit(Time limit); // Time after which a retransmitting session will terminate.
Time getSessionRetransmitLimit() const;
void setSessionIdleLimit(Time limit); // Time after which a quiescent session with no flows will terminate.
Time getSessionIdleLimit() const;
void setSessionTrafficClass(int tos); // Set Traffic Class (aka Type of Service) for IPlatformAdapter::writePacket()
int getSessionTrafficClass() const; // Note: ECN (2 LSB) is masked off
// Send an FIHello §2.3.3, §3.5.1.5
bool forwardIHello(const void *epd, size_t epdLen, const Address &replyAddress, const void *tag, size_t tagLen);
void setSessionFIHelloMode(FIHelloResponseMode mode); // §3.5.1.1.2. Default is FI_IGNORE.
FIHelloResponseMode getSessionFIHelloMode() const;
Task onFarAddressDidChange; // Called after DESTADDR change verified §3.5.4.2.
// Delay-based congestion detection
void setSessionCongestionDelay(Time delay); // Treat rtt > baseRTT + delay as congestion. Default INFINITY.
Time getSessionCongestionDelay() const;
protected:
Flow(RTMFP *rtmfp);
Flow() = delete;
std::shared_ptr<SendFlow> basicOpenFlow(const Bytes &metadata, RecvFlow *assoc, Priority pri);
RTMFP *m_rtmfp; // weak ref
std::shared_ptr<Session> m_session;
bool m_base_isOpen;
};
class SendFlow : public Flow {
public:
// If this flow triggered a new opening session, use these methods to add a
// candidate endpoint address for the responder.
void addCandidateAddress(const Address &addr, Time delay = 0);
void addCandidateAddress(const struct sockaddr *addr, Time delay = 0);
// Answers true if the flow is open, bound to an S_OPEN session, and buffered size is less than capacity.
bool isWritable() const;
void notifyWhenWritable(); // Trigger callbacks to onWritable.
void setBufferCapacity(size_t bufferLengthInBytes); // Advisory maximum size of the send buffer.
size_t getBufferCapacity() const;
size_t getBufferedSize() const; // How many bytes (including overhead) are in the send buffer.
size_t getRecvBufferBytesAvailable() const; // Latest buffer advertised by the receiver §3.6.3.5.
size_t getOutstandingBytes() const; // Count of unacknowledged bytes in the network §3.5.
// Queue new data for transmission. Note the buffer capacity is advisory and writes are limited only by
// available system memory. Writes are not allowed if the flow is closed. Writes are allowed before the
// flow is bound to a session.
// Answers a WriteReceipt on success or an empty shared_ptr on error.
// startWithin and finishWithin are the initial values for the WriteReceipt, which can be changed later.
std::shared_ptr<WriteReceipt> write(const void *message, size_t len, Time startWithin = INFINITY, Time finishWithin = INFINITY);
std::shared_ptr<WriteReceipt> write(const Bytes &message, Time startWithin = INFINITY, Time finishWithin = INFINITY);
void close() override;
Priority getPriority() const;
void setPriority(Priority pri);
// Called after notifyWhenWritable() while the flow is writable (open, in an S_OPEN session,
// and getBufferedSize() < getBufferCapacity()) and while this function answers true. Answer
// false to stop being called until after the next call to notifyWhenWritable().
std::function<bool(void)> onWritable;
// Called when the flow is rejected or if the session closes or times out.
std::function<void(uintmax_t reason)> onException;
// Called when a new associated return flow starts. flow must be accepted during
// this callback or it will be rejected automatically.
std::function<void(std::shared_ptr<RecvFlow> flow)> onRecvFlow;
protected:
friend class RTMFP;
friend class Session;
friend class Flow;
struct SendFrag;
SendFlow(RTMFP *rtmfp, const Bytes &epd, const Bytes &metadata, RecvFlow *assoc, Priority pri);
~SendFlow();
std::shared_ptr<WriteReceipt> basicWrite(const void *message, size_t len, Time startWithin, Time finishWithin);
void onSessionDidOpen(std::shared_ptr<SendFlow> myself, std::shared_ptr<Session> session);
void onSessionWillOpen(std::shared_ptr<Session> session);
void onSessionDidClose(std::shared_ptr<Session> session);
void queueWritableNotify();
void doWritable();
void scheduleForTransmission();
bool isMaybeReadyForTransmission();
void trimSendQueue(Time now);
void scheduleTrimSendQueue();
uintmax_t findForwardSequenceNumber(Time now);
bool assembleData(PacketAssembler *packet, int pri);
void ackRange(long &name, uintmax_t ackFrom, uintmax_t ackTo);
void onAck(uint8_t chunkType, size_t bufferBytesAvailable, uintmax_t cumulativeAck, const uint8_t *disjointed, const uint8_t *limit);
void onExceptionReport(uintmax_t exceptionCode);
void setPersistTimer();
void onPersistTimer(const std::shared_ptr<Timer> &sender, Time now);
void onLoss(size_t amount);
void gotoStateClosed();
enum State { F_OPEN, F_CLOSING, F_COMPLETE_LINGER, F_CLOSED };
long m_flow_id;
Bytes m_epd;
bool m_writablePending;
bool m_trimPending;
bool m_shouldNotifyWhenWritable;
Bytes m_startup_options;
Priority m_priority;
size_t m_buffer_capacity;
size_t m_outstanding_bytes;
size_t m_rx_buffer_size;
uintmax_t m_next_sn;
uintmax_t m_final_sn;
bool m_exception;
long m_last_send_queue_name;
State m_state;
std::shared_ptr<Session> m_openingSession;
std::shared_ptr<RecvFlow> m_tmp_association; // only needed until flow is established
SumList<std::shared_ptr<SendFrag> > m_send_queue;
std::shared_ptr<Timer> m_persistTimer;
};
class RecvFlow : public Flow {
public:
bool isOpen() const override;
void accept(); // Call this during the onRecvFlow callback to accept a new receiving flow.
void setReceiveOrder(ReceiveOrder order); // Default RO_SEQUENCE.
ReceiveOrder getReceiveOrder() const;
void setBufferCapacity(size_t bufferLengthInBytes); // §3.6.3
size_t getBufferCapacity() const;
size_t getBufferedSize() const; // §3.6.3
void setWindowLimit(size_t windowLengthInBytes); // Limit inflight regardless of buffer.
size_t getWindowLimit() const;
void close() override; // Reject this flow with default (0) reason §3.6.3.7.
void close(uintmax_t reason); // Reject this flow with reason EXCEPTION_CODE.
Bytes getMetadata() const;
uintmax_t getForwardSequenceNumber() const; // Less than or equal to this are not coming §3.6.2.3 ¶4
uintmax_t getCumulativeAckSequenceNumber() const; // Highest contiguously received, §3.6.3.3 ¶2
// Answer the sending flow to which this flow is a return/response, or empty if
// this is an unassociated flow.
std::shared_ptr<SendFlow> getAssociatedSendFlow() const;
// Open a new flow in return/response to this flow.
std::shared_ptr<SendFlow> openReturnFlow(const void *metadataBytes, size_t metadataLen, Priority pri = PRI_ROUTINE);
std::shared_ptr<SendFlow> openReturnFlow(const Bytes &metadata, Priority pri = PRI_ROUTINE);
// This function is called as complete messages are received according to the ReceiveOrder.
// Messages are discarded if this callback is not set.
std::function<void(const uint8_t *bytes, size_t len, uintmax_t sequenceNumber, size_t fragmentCount)> onMessage;
// Called when the flow concludes or has an error (such as the session closing while the flow is still open).
std::function<void(bool error)> onComplete;
// This function is called when the receive order is RO_NETWORK and the Cumulative Ack Sequence Number (CSN)
// advances to close a sequence number gap. This condition can indicate to an external reorder buffer to
// advance sequenced delivery through the CSN.
Task onCumulativeAckDidMerge;
protected:
friend class RTMFP;
friend class Session;
friend class SendFlow;
struct RecvFrag;
RecvFlow(std::shared_ptr<Session> session, uintmax_t flowID, const uint8_t *metadata, size_t metadataLen, std::shared_ptr<SendFlow> associatedFlow);
~RecvFlow();
void abort();
void closeAndNotify(bool error);
void onData(uint8_t flags, uintmax_t sequenceNumber, uintmax_t fsn, const uint8_t *data, size_t len);
long insertFragment(uintmax_t sequenceNumber, uint8_t flags, const uint8_t *data, size_t len);
void deliverMessage(long name);
void tryDelivery(long hint);
void scheduleAck(bool now);
bool assembleAck(PacketAssembler *packet, bool truncateAllowed, bool sendEcnReport);
enum State { RF_OPEN, RF_REJECTED, RF_COMPLETE_LINGER, RF_CLOSED };
uintmax_t m_flow_id;
Bytes m_metadata;
bool m_accepted;
ReceiveOrder m_rxOrder;
IndexSet m_sequence_set;
uintmax_t m_forward_sn;
uintmax_t m_final_sn;
bool m_final_sn_seen;
SumList<std::shared_ptr<RecvFrag> > m_recv_buffer;
size_t m_buffer_capacity;
size_t m_window_limit;
size_t m_prev_rwnd;
bool m_should_ack;
uintmax_t m_exception_code;
State m_state;
std::shared_ptr<SendFlow> m_associatedFlow;
std::shared_ptr<Timer> m_complete_linger_alarm;
};
class IPlatformAdapter {
public:
virtual ~IPlatformAdapter() {}
virtual Time getCurrentTime() = 0; // Answer the current time (seconds).
// Called if the deadline for calling RTMFP::doTimerWork() changes. This can be
// used for re-setting a platform alarm for calling doTimerWork().
virtual void onHowLongToSleepDidChange() {}
// Start calling onwritable when interface is writable until onwritable answers false.
virtual bool notifyWhenInterfaceWritable(int interfaceID, const std::function<bool(void)> &onwritable) = 0;
// Write a datagram via interfaceID.
virtual bool writePacket(const void *bytes, size_t len, int interfaceID, const struct sockaddr *addr, socklen_t addrLen, int tos) = 0;
// Invoke task as soon as possible after this method completes (for example, by putting it
// on a queue to run at the end of the current trip through the run loop). This is the ONLY
// synchronization primitive used by the implementation (that is, there are no mutexes). Tasks
// for the same thread MUST be invoked in the order this method is called. Thread "0" MUST be synchronized
// with, or the same as, the main RTMFP thread. Non-zero threads can be the same or different platform
// threads. The intent is, if the platform supports it, for time consuming public key crypto operations
// to happen on a different thread/core so that RTMFP will remain responsive, and otherwise for RTMFP to
// be single-threaded. See PosixPlatformAdapter and PerformerPosixPlatformAdapter for illustrative examples.
virtual bool perform(unsigned long thread, const Task &task) = 0;
virtual void onShutdownComplete() {} // See RTMFP::shutdown().
};
class ICryptoAdapter {
public:
virtual ~ICryptoAdapter() {};
// Answer a new SessionCryptoKey. Until it is completed, its encrypt and
// decrypt methods should use the Default Session Key.
virtual std::shared_ptr<SessionCryptoKey> getKeyForNewSession() = 0;
// Answer this end's certificate, potentially customized for epd.
virtual Bytes getNearEncodedCertForEPD(const uint8_t *epd, size_t epdLen) = 0;
// Answer true if this end is selected by epd, false if not.
virtual bool isSelectedByEPD(const uint8_t *epd, size_t epdLen) = 0;
// Sign msg according to my identity, customized (if appropriate) to recipient.
virtual Bytes sign(const uint8_t *msg, size_t msgLen, std::shared_ptr<CryptoCert> recipient) = 0;
// Answer true if the near end wins glare §3.5.1.3.
virtual bool checkNearWinsGlare(std::shared_ptr<CryptoCert> farCert) = 0;
// Answer a CryptoCert object for the encoded certificate, or empty if
// bytes does not encode a valid certificate.
virtual std::shared_ptr<CryptoCert> decodeCertificate(const uint8_t *bytes, size_t len) = 0;
// Place len ostensibly cryptographically strong pseudorandom bytes at dst.
// Note that answering not-stronly-random bytes has security implications.
virtual void pseudoRandomBytes(uint8_t *dst, size_t len) = 0;
// Compute an ostensibly cryptographic 256-bit hash (such as SHA-256) of msg and place it
// at dst. Used internally by the RTMFP implementation for e.g. cookies, mobility
// checks, etc. Factored out here to allow re-use of existing/optimized crypto libraries.
// Note that using a non-cryptographic hash here has security implications.
virtual void cryptoHash256(uint8_t *dst, const uint8_t *msg, size_t len) = 0;
};
class SessionCryptoKey : public Object {
public:
// Answer number of bytes to leave at the front of the source (cleartext) buffer
// for e.g. a checksum or sequence number, so encryption can potentially avoid
// a copy and keep the source buffer conveniently aligned.
virtual size_t getEncryptSrcFrontMargin() = 0;
// Encrypt src[srcFronMargin..srcLen-1] to dst. On input ioDstLen is the capacity
// of dst, not to be exceeded. On success, answer true and set ioDstLen to the number
// of bytes written at dst. On failure answer false. This method is allowed to
// modify src[0..srcFrontMargin-1] to e.g. insert a checksum or sequence number
// or something, and to modify src[srcLen..srcLen+63] to e.g. pad a cipher block.
// Until the key handshake is completed, encrypt with the Default Session Key.
virtual bool encrypt(uint8_t *dst, size_t &ioDstLen, uint8_t *src, size_t srcLen, size_t srcFrontMargin) = 0;
// Decrypt src[src..srcLen - 1] to dst. On input, ioDstLen is the capacity of dst. On
// success, answer true, set ioDstLen to the number of bytes written to dst, including
// the front margin if any, and set dstFrontMargin to the number of bytes past dst
// where the clear packet begins, for example to skip over a decrypted checksum or
// sequence number and avoid a copy. On failure, answer false (for example, if a
// checksum check fails or there is a syntax error in the packet, indicating this
// packet may not belong to this key).
// Until the key handshake is completed, decrypt with the Default Session Key.
virtual bool decrypt(uint8_t *dst, size_t &ioDstLen, size_t &dstFrontMargin, const uint8_t *src, size_t srcLen) = 0;
// Answer the session near/far nonces §3.5.
virtual Bytes getNearNonce() = 0;
virtual Bytes getFarNonce() = 0;
// Set this key to initiator mode and generate a Session Key Initiator Component (SKIC) §2.3.7
// appropriate to responder. On success, place the SKIC into outComponent and answer true.
// On failure answer false.
virtual bool generateInitiatorKeyingComponent(std::shared_ptr<CryptoCert> responder, Bytes *outComponent) = 0;
// Combine the state from the SKIC generated above with the Session Key Responder Component (SKRC)
// at responderComponent received from the responder, completing this key. Answer true if the SKRC
// is successfully combined to complete the key, false otherwise.
virtual bool initiatorCombineResponderKeyingComponent(const uint8_t *responderComponent, size_t len) = 0;
// Set this key to responder mode. Take the initiatorComponent SKIC from initiator, and if acceptable,
// generate the SKRC to outComponent, complete the key, and answer true. If unacceptable, answer false.
virtual bool generateResponderKeyingComponent(std::shared_ptr<CryptoCert> initiator, const uint8_t *initiatorComponent, size_t len, Bytes *outComponent) = 0;
};
class CryptoCert : public Object {
public:
// Test if this certificate is authentic (for example, a signature/endorsement chain checks out).
// Call onauthentic (sync or async, and from any thread so long as the platform adapter's perform
// method can be called from any thread) if authentic, otherwise do nothing.
virtual void isAuthentic(const Task &onauthentic) = 0;
// Answer true if this certificate is selected by epd, false otherwise.
virtual bool isSelectedByEPD(const uint8_t *epd, size_t epdLen) = 0;
// Answer the Canonical Endpoint Discriminator for this certificate.
virtual Bytes getCanonicalEPD() = 0;
// Test if sig(nature) over msg was issued by this certificate's owner. Call ongood
// (sync or async, from any thread with above caveat) if sig is good, otherwise do nothing.
virtual void checkSignature(const uint8_t *msg, size_t msgLen, const uint8_t *sig, size_t sigLen, const Task &ongood) = 0;
// Answer true if other overrides a session with this certificate §3.2.
virtual bool doesCertOverrideSession(std::shared_ptr<CryptoCert> other) = 0;
// Answer the raw encoded certificate (implies the object must keep a copy of it).
virtual Bytes encode() = 0;
};
} } } // namespace com::zenomt::rtmfp