Main Page | Class Hierarchy | Class List | File List | Class Members

/Users/baford/proj/netsteria/sst/lib/key.h

00001 #ifndef SST_KEY_H
00002 #define SST_KEY_H
00003 
00004 #include <QHash>
00005 #include <QPointer>
00006 
00007 #include <openssl/dh.h>
00008 
00009 #include "sock.h"
00010 #include "timer.h"
00011 
00012 
00013 namespace SST {
00014 
00015 // Control chunk magic value for the Netsteria key exchange protocol.
00016 // The upper byte is zero to distinguish control packets from flow packets.
00017 // 0x4e6b78 = 'Nkx': 'Netsteria key exchange'
00018 //#define KEY_MAGIC     (qint32)0x004e6b78
00019 
00020 
00021 // Bit mask of allowable key security methods
00022 #define KEYMETH_NONE            0x0001  // No security at all
00023 #define KEYMETH_CHK             0x0002  // Weak 32-bit keyed checksum
00024 #define KEYMETH_SHA256          0x0010  // HMAC-SHA256 auth, DH key agreement
00025 #define KEYMETH_AES             0x0020  // AES enc, HMAC-SHA256 auth, DH
00026 #define KEYMETH_DEFAULT         KEYMETH_AES     // Secure by default
00027 
00028 
00029 // Well-known control chunk types for keying
00030 #define KEYCHUNK_NI             0x0001  // Multi-cyphersuite initiator nonce
00031 #define KEYCHUNK_JFDH_R0        0x0010  // DH-based JFK key agreement
00032 #define KEYCHUNK_JFDH_I1        0x0011
00033 #define KEYCHUNK_JFDH_R1        0x0012
00034 #define KEYCHUNK_JFDH_I2        0x0013
00035 #define KEYCHUNK_JFDH_R2        0x0014
00036 
00037 
00038 class Host;
00039 class Flow;
00040 class KeyInitiator;
00041 class KeyResponder;
00042 class KeyHostState;
00043 class DHKey;
00044 
00045 class KeyChunkChkI1Data;
00046 class KeyChunkChkR1Data;
00047 class KeyChunkDhI1Data;
00048 class KeyChunkDhR1Data;
00049 class KeyChunkDhI2Data;
00050 class KeyChunkDhR2Data;
00051 
00052 
00053 // This class manages the initiator side of the key exchange.
00054 // We create one instance for each outgoing connection we attempt.
00055 //
00056 // XXX we should really have a separate Idle state,
00057 // so that clients can hookup signals before starting key exchange.
00058 // XXX make KeyInitiator an abstract base class like KeyResponder,
00059 // calling a newFlow() method when it needs to set up a flow
00060 // rather than requiring the flow to be passed in at the outset.
00061 class KeyInitiator : public QObject
00062 {
00063         Q_OBJECT
00064         friend class KeyResponder;
00065 
00066         enum State {
00067                 I1,
00068                 I2,
00069                 Done
00070         };
00071 
00072         // Basic parameters
00073         Host *const h;          // Our per-host state
00074         Flow *const fl;         // Flow we're trying to set up
00075         SocketEndpoint sepr;    // Remote endpoint we're trying to contact
00076         QByteArray idr;         // Target's host ID (empty if unspecified)
00077         QByteArray ulpi;        // Opaque info block to pass to responder
00078         const quint32 magic;    // Magic identifier for upper-layer protocol
00079         quint32 methods;        // Security methods allowed
00080 
00081 
00083 
00084         quint32 chkkey;         // Checksum key
00085         QByteArray cookie;      // Responder's cookie
00086 
00087 
00089 
00090         quint8 dhgroup;         // DH group to use
00091         quint8 keylen;          // AES key length to use
00092 
00093         // Protocol state set up before sending I1
00094         State state;
00095         QByteArray ni, nhi;     // Initiator's nonce, and hashed nonce
00096         QByteArray dhi;         // Initiator's DH public key
00097 
00098         // Set on receipt of R1 response
00099         QByteArray nr;          // Responder's nonce
00100         QByteArray dhr;         // Responder's DH public key
00101         QByteArray hhkr;        // Responder's challenge cookie
00102         QByteArray master;      // Shared master secret
00103         QByteArray encidi;      // Encrypted and authenticated identity info
00104 
00105         // Retransmit state
00106         Timer txtimer;
00107 
00108 
00109 public:
00110         // Start key negotiation for a Flow
00111         // that has been bound to a socket but not yet activated.
00112         // If 'idr' is non-empty, only connect to specified host ID.
00113         // The KeyInitiator makes itself the parent of the provided Flow,
00114         // so that if it is deleted the incomplete Flow will be too.
00115         // The client must therefore re-parent the flow
00116         // after successful key exchange before deleting the KeyInitiator.
00117         KeyInitiator(Flow *flow, quint32 magic,
00118                         const QByteArray &idr = QByteArray(),
00119                         quint8 dhgroup = 0);
00120         ~KeyInitiator();
00121 
00122         inline Host *host() { return h; }
00123 
00124         inline Flow *flow() { return fl; }
00125         inline bool isDone() { return state == Done; }
00126 
00127         inline SocketEndpoint remoteEndpoint() { return sepr; }
00128 
00129         // Set/get the opaque information block to pass to the responder.
00130         // the info block is passed encrypted and authenticated in our I2;
00131         // the responder can use it to decide whether to accept the connection
00132         // and to setup any upper-layer protocol parameters for the new flow.
00133         inline QByteArray info() { return ulpi; }
00134         inline void setInfo(const QByteArray &info) { ulpi = info; }
00135 
00136 signals:
00137         void completed(bool success);
00138 
00139 private:
00140         void sendI1();
00141         void sendDhI2();
00142 
00143         // Called by KeyResponder::receive() when we get a response packet.
00144         static void gotR0(Host *h, const Endpoint &src);
00145         static void gotChkR1(Host *h, KeyChunkChkR1Data &r1,
00146                                 const SocketEndpoint &ep);
00147         static void gotDhR1(Host *h, KeyChunkDhR1Data &r1);
00148         static void gotDhR2(Host *h, KeyChunkDhR2Data &r2);
00149 
00150 private slots:
00151         void retransmit(bool fail);
00152 };
00153 
00154 
00155 // This abstract base class manages the responder side of the key exchange.
00156 class KeyResponder : public SocketReceiver
00157 {
00158         friend class KeyInitiator;
00159 
00160 private:
00161         Host *const h;
00162 
00163 public:
00164         // Create a KeyResponder and set it listening on a particular Socket
00165         // for control messages with the specified magic protocol identifier.
00166         // The new KeyResponder becomes a child of the Socket.
00167         KeyResponder(Host *host, quint32 magic, QObject *parent = NULL);
00168 
00169         inline Host *host() { return h; }
00170 
00171         // Socket module calls this with control messages intended for us
00172         void receive(QByteArray &msg, XdrStream &rs,
00173                         const SocketEndpoint &src);
00174 
00175         // Send an R0 chunk to some network address,
00176         // presumably a client we've discovered somehow is trying to reach us,
00177         // in order to punch a hole in any NATs we may be behind
00178         // and prod the client into (re-)sending us its I1 immediately.
00179         void sendR0(const Endpoint &dst);
00180 
00181 
00182 protected:
00183         // KeyResponder calls this to check whether to accept a connection,
00184         // before actually bothering to verify the initiator's identity.
00185         // The default implementation always just returns true.
00186         virtual bool checkInitiator(const SocketEndpoint &epi,
00187                         const QByteArray &eidi, const QByteArray &ulpi);
00188 
00189         // KeyResponder calls this to create a flow requested by a client.
00190         // The returned flow must be bound to the specified source endpoint,
00191         // but not yet active (started).
00192         // The 'ulpi' contains the information block passed by the client,
00193         // and the 'ulpr' block will be passed back to the client.
00194         // This method can return NULL to reject the incoming connection.
00195         virtual Flow *newFlow(const SocketEndpoint &epi,
00196                         const QByteArray &eidi, const QByteArray &ulpi,
00197                         QByteArray &ulpr) = 0;
00198 
00199 
00200 private:
00201         void gotChkI1(KeyChunkChkI1Data &i1, const SocketEndpoint &src);
00202 
00203         void gotDhI1(KeyChunkDhI1Data &i1, const SocketEndpoint &src);
00204         void handleDhI1(quint8 dhgroup, const QByteArray &nhi,
00205                                 QByteArray &pki, const SocketEndpoint &src);
00206         void gotDhI2(KeyChunkDhI2Data &i2, const SocketEndpoint &src);
00207 
00208         static QByteArray calcDhCookie(DHKey *hk,
00209                         const QByteArray &nr, const QByteArray &nhi,
00210                         const Endpoint &src);
00211 };
00212 
00213 
00214 // (endpoint, chkkey) pair for initchks hash table
00215 typedef QPair<Endpoint, quint32> KeyEpChk;
00216 
00217 class KeyHostState
00218 {
00219         friend class KeyInitiator;
00220 
00221 private:
00222         // Hash table of all currently active KeyInitiator, indexed on chkkey
00223         QHash<KeyEpChk, KeyInitiator*> initchks;
00224 
00225         // Hash table of all currently active KeyInitiator, indexed on nhi
00226         QHash<QByteArray, KeyInitiator*> initnhis;
00227 
00228         // Same, indexed on target endpoint
00229         QHash<Endpoint, KeyInitiator*> initeps;
00230 
00231 public:
00232 };
00233 
00234 
00235 } // namespace SST
00236 
00237 #endif  // SST_KEY_H

Generated on Wed Mar 28 11:48:05 2007 for SST by doxygen 1.3.4