-
Notifications
You must be signed in to change notification settings - Fork 43
/
crypto.h
182 lines (160 loc) · 9.86 KB
/
crypto.h
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
/*
Copyright (C) 2010-2015 Rudolf Polzer (divVerent)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef CRYPTO_H
#define CRYPTO_H
#include <stddef.h>
#include "qtypes.h"
struct lhnetaddress_s;
extern struct cvar_s crypto_developer;
extern struct cvar_s crypto_aeslevel;
#define ENCRYPTION_REQUIRED (crypto_aeslevel.integer >= 3)
extern int crypto_keyfp_recommended_length; // applies to LOCAL IDs, and to ALL keys
#define CRYPTO_HEADERSIZE 31
// AES case causes 16 to 31 bytes overhead
// SHA256 case causes 16 bytes overhead as we truncate to 128bit
#define FP64_SIZE 44
#define DHKEY_SIZE 16
typedef struct
{
unsigned char dhkey[DHKEY_SIZE]; // shared key, not NUL terminated
char client_idfp[FP64_SIZE+1];
char client_keyfp[FP64_SIZE+1];
qbool client_issigned;
char server_idfp[FP64_SIZE+1];
char server_keyfp[FP64_SIZE+1];
qbool server_issigned;
qbool authenticated;
qbool use_aes;
void *data;
}
crypto_t;
void Crypto_Init(void);
void Crypto_Init_Commands(void);
void Crypto_LoadKeys(void); // NOTE: when this is called, the SV_LockThreadMutex MUST be active
void Crypto_Shutdown(void);
qbool Crypto_Available(void);
void sha256(unsigned char *out, const unsigned char *in, int n); // may ONLY be called if Crypto_Available()
const void *Crypto_EncryptPacket(crypto_t *crypto, const void *data_src, size_t len_src, void *data_dst, size_t *len_dst, size_t len);
const void *Crypto_DecryptPacket(crypto_t *crypto, const void *data_src, size_t len_src, void *data_dst, size_t *len_dst, size_t len);
#define CRYPTO_NOMATCH 0 // process as usual (packet was not used)
#define CRYPTO_MATCH 1 // process as usual (packet was used)
#define CRYPTO_DISCARD 2 // discard this packet
#define CRYPTO_REPLACE 3 // make the buffer the current packet
int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, size_t *len_out, lhnetaddress_t *peeraddress, const char *peeraddressstring);
int Crypto_ServerParsePacket(const char *data_in, size_t len_in, char *data_out, size_t *len_out, lhnetaddress_t *peeraddress);
// if len_out is nonzero, the packet is to be sent to the client
qbool Crypto_ServerAppendToChallenge(const char *data_in, size_t len_in, char *data_out, size_t *len_out, size_t maxlen);
crypto_t *Crypto_ServerGetInstance(struct lhnetaddress_s *peeraddress);
qbool Crypto_FinishInstance(crypto_t *out, crypto_t *in); // also clears allocated memory, and frees the instance received by ServerGetInstance
const char *Crypto_GetInfoResponseDataString(void);
// retrieves a host key for an address (can be exposed to menuqc, or used by the engine to look up stored keys e.g. for server bookmarking)
// pointers may be NULL
qbool Crypto_RetrieveHostKey(struct lhnetaddress_s *peeraddress, int *keyid, char *keyfp, size_t keyfplen, char *idfp, size_t idfplen, int *aeslevel, qbool *issigned);
int Crypto_RetrieveLocalKey(int keyid, char *keyfp, size_t keyfplen, char *idfp, size_t idfplen, qbool *issigned); // return value: -1 if more to come, +1 if valid, 0 if end of list
size_t Crypto_SignData(const void *data, size_t datasize, int keyid, void *signed_data, size_t signed_size);
size_t Crypto_SignDataDetached(const void *data, size_t datasize, int keyid, void *signed_data, size_t signed_size);
// netconn protocol:
// non-crypto:
// getchallenge >
// < challenge
// connect >
// < accept (or: reject)
// crypto:
// getchallenge >
// < challenge SP <challenge> NUL vlen <size> d0pk <fingerprints I can auth to> NUL NUL <other fingerprints I accept>
//
// IF serverfp:
// d0pk\cnt\0\challenge\<challenge>\aeslevel\<level> NUL <serverfp> NUL <clientfp>
// >
// check if client would get accepted; if not, do "reject" now
// require non-control packets to be encrypted require non-control packets to be encrypted
// do not send anything yet do not send anything yet
// RESET to serverfp RESET to serverfp
// d0_blind_id_authenticate_with_private_id_start() = 1
// < d0pk\cnt\1\aes\<aesenabled> NUL *startdata*
// d0_blind_id_authenticate_with_private_id_challenge() = 1
// d0pk\cnt\2 NUL *challengedata* >
// d0_blind_id_authenticate_with_private_id_response() = 0
// < d0pk\cnt\3 NUL *responsedata*
// d0_blind_id_authenticate_with_private_id_verify() = 1
// store server's fingerprint NOW
// d0_blind_id_sessionkey_public_id() = 1 d0_blind_id_sessionkey_public_id() = 1
//
// IF clientfp AND NOT serverfp:
// RESET to clientfp RESET to clientfp
// d0_blind_id_authenticate_with_private_id_start() = 1
// d0pk\cnt\0\challenge\<challenge>\aeslevel\<level> NUL NUL <clientfp> NUL *startdata*
// >
// check if client would get accepted; if not, do "reject" now
// require non-control packets to be encrypted require non-control packets to be encrypted
// d0_blind_id_authenticate_with_private_id_challenge() = 1
// < d0pk\cnt\5\aes\<aesenabled> NUL *challengedata*
//
// IF clientfp AND serverfp:
// RESET to clientfp RESET to clientfp
// d0_blind_id_authenticate_with_private_id_start() = 1
// d0pk\cnt\4 NUL *startdata* >
// d0_blind_id_authenticate_with_private_id_challenge() = 1
// < d0pk\cnt\5 NUL *challengedata*
//
// IF clientfp:
// d0_blind_id_authenticate_with_private_id_response() = 0
// d0pk\cnt\6 NUL *responsedata* >
// d0_blind_id_authenticate_with_private_id_verify() = 1
// store client's fingerprint NOW
// d0_blind_id_sessionkey_public_id() = 1 d0_blind_id_sessionkey_public_id() = 1
// note: the ... is the "connect" message, except without the challenge. Reinterpret as regular connect message on server side
//
// enforce encrypted transmission (key is XOR of the two DH keys)
//
// IF clientfp:
// < challenge (mere sync message)
//
// connect\... >
// < accept (ALWAYS accept if connection is encrypted, ignore challenge as it had been checked before)
//
// commence with ingame protocol
// in short:
// server:
// getchallenge NUL d0_blind_id: reply with challenge with added fingerprints
// cnt=0: IF server will auth, cnt=1, ELSE cnt=5
// cnt=2: cnt=3
// cnt=4: cnt=5
// cnt=6: send "challenge"
// client:
// challenge with added fingerprints: cnt=0; if client will auth but not server, append client auth start
// cnt=1: cnt=2
// cnt=3: IF client will auth, cnt=4, ELSE rewrite as "challenge"
// cnt=5: cnt=6, server will continue by sending "challenge" (let's avoid sending two packets as response to one)
// other change:
// accept empty "challenge", and challenge-less connect in case crypto protocol has executed and finished
// statusResponse and infoResponse get an added d0_blind_id key that lists
// the keys the server can auth with and to in key@ca SPACE key@ca notation
// any d0pk\ message has an appended "id" parameter; messages with an unexpected "id" are ignored to prevent errors from multiple concurrent auth runs
// comparison to OTR:
// - encryption: yes
// - authentication: yes
// - deniability: no (attacker requires the temporary session key to prove you
// have sent a specific message, the private key itself does not suffice), no
// measures are taken to provide forgeability to even provide deniability
// against an attacker who knows the temporary session key, as using CTR mode
// for the encryption - which, together with deriving the MAC key from the
// encryption key, and MACing the ciphertexts instead of the plaintexts,
// would provide forgeability and thus deniability - requires longer
// encrypted packets and deniability was not a goal of this, as we may e.g.
// reserve the right to capture packet dumps + extra state info to prove a
// client/server has sent specific packets to prove cheating)
// - perfect forward secrecy: yes (session key is derived via DH key exchange)
#endif