blob: a98d66c291eeec4794ebf1c432b861471b89cba0 [file] [log] [blame]
Emeric Vigier2f625822012-08-06 11:09:52 -04001// test ccRTP functionality
2// Copyright (C) 2004 Federico Montesino Pouzols <fedemp@altern.org>
3//
4// This program is free software; you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation; either version 2 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18#include <cstdlib>
19#include <ccrtp/rtp.h>
20#include <ccrtp/rtppkt.h>
21#include <ccrtp/crypto/AesSrtp.h>
22#include <ccrtp/CryptoContext.h>
23
24#ifdef CCXX_NAMESPACES
25using namespace ost;
26using namespace std;
27#endif
28
29static void hexdump(const char* title, const unsigned char *s, int l)
30{
31 int n=0;
32
33 if (s == NULL) return;
34
35 fprintf(stderr, "%s",title);
36 for( ; n < l ; ++n) {
37 if((n%16) == 0)
38 fprintf(stderr, "\n%04x",n);
39 fprintf(stderr, " %02x",s[n]);
40 }
41 fprintf(stderr, "\n");
42}
43
44inline int hex_char_to_nibble(uint8_t c)
45{
46 switch(c) {
47 case ('0'): return 0x0;
48 case ('1'): return 0x1;
49 case ('2'): return 0x2;
50 case ('3'): return 0x3;
51 case ('4'): return 0x4;
52 case ('5'): return 0x5;
53 case ('6'): return 0x6;
54 case ('7'): return 0x7;
55 case ('8'): return 0x8;
56 case ('9'): return 0x9;
57 case ('a'): return 0xa;
58 case ('A'): return 0xa;
59 case ('b'): return 0xb;
60 case ('B'): return 0xb;
61 case ('c'): return 0xc;
62 case ('C'): return 0xc;
63 case ('d'): return 0xd;
64 case ('D'): return 0xd;
65 case ('e'): return 0xe;
66 case ('E'): return 0xe;
67 case ('f'): return 0xf;
68 case ('F'): return 0xf;
69 default: return -1; /* this flags an error */
70 }
71 /* NOTREACHED */
72 return -1; /* this keeps compilers from complaining */
73}
74
75/*
76 * hex_string_to_octet_string converts a hexadecimal string
77 * of length 2 * len to a raw octet string of length len
78 */
79
80int hex_string_to_octet_string(char *raw, char *hex, int len)
81{
82 uint8 x;
83 int tmp;
84 int hex_len;
85
86 hex_len = 0;
87 while (hex_len < len) {
88 tmp = hex_char_to_nibble(hex[0]);
89 if (tmp == -1)
90 return hex_len;
91 x = (tmp << 4);
92 hex_len++;
93 tmp = hex_char_to_nibble(hex[1]);
94 if (tmp == -1)
95 return hex_len;
96 x |= (tmp & 0xff);
97 hex_len++;
98 *raw++ = x;
99 hex += 2;
100 }
101 return hex_len;
102}
103
104class PacketsPattern
105{
106public:
107 inline const InetHostAddress& getDestinationAddress() const
108 { return destinationAddress; }
109
110 inline const tpport_t getDestinationPort() const
111 { return destinationPort; }
112
113 uint32 getPacketsNumber() const
114 { return packetsNumber; }
115
116 uint32 getSsrc() const
117 { return 0xdeadbeef; }
118
119 const unsigned char* getPacketData(uint32 i)
120 { return data; }
121
122 const size_t getPacketSize(uint32 i)
123 { return packetsSize; }
124
125private:
126 static const InetHostAddress destinationAddress;
127 static const uint16 destinationPort = 5002;
128 static const uint32 packetsNumber = 10;
129 static const uint32 packetsSize = 12;
130 static unsigned char data[];
131};
132
133const InetHostAddress PacketsPattern::destinationAddress =
134 InetHostAddress("localhost");
135
136unsigned char PacketsPattern::data[] = {
137 "0123456789\n"
138};
139
140PacketsPattern pattern;
141
142static char* fixKey = (char *)"c2479f224b21c2008deea6ef0e5dbd4a761aef98e7ebf8eed405986c4687";
143
144// static uint8* masterKey = (uint8*)"masterKeymasterKeymasterKeymaster";
145// static uint8* masterSalt = (uint8*)"NaClNaClNaClNa";
146
147uint8 masterKey[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
148 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
149
150uint8 masterSalt[] = { 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
151 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d };
152
153static uint8 binKeys[60];
154
155class SendPacketTransmissionTest: public Thread, public TimerPort
156{
157public:
158 void run() {
159 doTest();
160 }
161
162 int doTest()
163 {
164 // should be valid?
165 //RTPSession tx();
166 RTPSession tx(pattern.getSsrc(), InetHostAddress("localhost"));
167 tx.setSchedulingTimeout(10000);
168 tx.setExpireTimeout(1000000);
169
170 CryptoContext* txCryptoCtx =
171 new CryptoContext(pattern.getSsrc(),
172 0, // roc,
173 0L, // keydr << 48,
174 SrtpEncryptionAESCM, // encryption algo
175 SrtpAuthenticationSha1Hmac, // authtication algo
176 masterKey, // Master Key
177 128 / 8, // Master Key length
178 masterSalt, // Master Salt
179 112 / 8, // Master Salt length
180 128 / 8, // encryption keyl
181 160 / 8, // authentication key len (SHA1))
182 112 / 8, // session salt len
183 80 / 8); // authentication tag len
184 txCryptoCtx->deriveSrtpKeys(0);
185
186 tx.setOutQueueCryptoContext(txCryptoCtx);
187
188 tx.startRunning();
189
190 tx.setPayloadFormat(StaticPayloadFormat(sptPCMU));
191 if (!tx.addDestination(pattern.getDestinationAddress(), pattern.getDestinationPort()) ) {
192 return 1;
193 }
194
195 // 50 packets per second (packet duration of 20ms)
196 uint32 period = 20;
197 uint16 inc = tx.getCurrentRTPClockRate()/50;
198 TimerPort::setTimer(period);
199 for ( uint32 i = 0; i < pattern.getPacketsNumber(); i++ ) {
200 tx.putData(i*inc, pattern.getPacketData(i), pattern.getPacketSize(i));
201 cout << "Sent some data: " << i << endl;
202 Thread::sleep(TimerPort::getTimer());
203 TimerPort::incTimer(period);
204 }
205 return 0;
206 }
207};
208
209
210class RecvPacketTransmissionTest: public Thread
211{
212public:
213 void run() {
214 doTest();
215 }
216
217 int doTest()
218 {
219 RTPSession rx(pattern.getSsrc(), pattern.getDestinationAddress(),
220 pattern.getDestinationPort());
221
222 rx.setSchedulingTimeout(10000);
223 rx.setExpireTimeout(1000000);
224
225 CryptoContext* rxCryptoCtx =
226 new CryptoContext(0, // SSRC == 0 -> Context template
227 0, // roc,
228 0L, // keydr << 48,
229 SrtpEncryptionAESCM, // encryption algo
230 SrtpAuthenticationSha1Hmac, // authtication algo
231 masterKey, // Master Key
232 128 / 8, // Master Key length
233 masterSalt, // Master Salt
234 112 / 8, // Master Salt length
235 128 / 8, // encryption keyl
236 160 / 8, // authentication keylen (SHA1))
237 112 / 8, // session salt len
238 80 / 8); // authentication tag len
239 rx.setInQueueCryptoContext(rxCryptoCtx);
240
241 rx.startRunning();
242 rx.setPayloadFormat(StaticPayloadFormat(sptPCMU));
243 // arbitrary number of loops
244 for ( int i = 0; i < 500 ; i++ ) {
245 const AppDataUnit* adu;
246 while ( (adu = rx.getData(rx.getFirstTimestamp())) ) {
247 cerr << "got some data: " << adu->getData() << endl;
248 delete adu;
249 }
250 Thread::sleep(70);
251 }
252 return 0;
253 }
254};
255
256/*
257 * The F8 test vectors according to RFC3711
258 */
259unsigned char salt[] = {0x32, 0xf2, 0x87, 0x0d};
260
261unsigned char iv[] = { 0x00, 0x6e, 0x5c, 0xba, 0x50, 0x68, 0x1d, 0xe5,
262 0x5c, 0x62, 0x15, 0x99, 0xd4, 0x62, 0x56, 0x4a};
263
264unsigned char key[]= { 0x23, 0x48, 0x29, 0x00, 0x84, 0x67, 0xbe, 0x18,
265 0x6c, 0x3d, 0xe1, 0x4a, 0xae, 0x72, 0xd6, 0x2c};
266
267unsigned char payload[] = {
268 0x70, 0x73, 0x65, 0x75, 0x64, 0x6f, 0x72, 0x61,
269 0x6e, 0x64, 0x6f, 0x6d, 0x6e, 0x65, 0x73, 0x73,
270 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20,
271 0x6e, 0x65, 0x78, 0x74, 0x20, 0x62, 0x65, 0x73,
272 0x74, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x67}; // 39 bytes
273
274unsigned char cipherText[] = {
275 0x01, 0x9c, 0xe7, 0xa2, 0x6e, 0x78, 0x54, 0x01,
276 0x4a, 0x63, 0x66, 0xaa, 0x95, 0xd4, 0xee, 0xfd,
277 0x1a, 0xd4, 0x17, 0x2a, 0x14, 0xf9, 0xfa, 0xf4,
278 0x55, 0xb7, 0xf1, 0xd4, 0xb6, 0x2b, 0xd0, 0x8f,
279 0x56, 0x2c, 0x0e, 0xef, 0x7c, 0x48, 0x02}; // 39 bytes
280
281unsigned char rtpPacketHeader[] = {
282 0x80, 0x6e, 0x5c, 0xba, 0x50, 0x68, 0x1d, 0xe5,
283 0x5c, 0x62, 0x15, 0x99};
284
285unsigned char rtpPacket[] = {
286 0x80, 0x6e, 0x5c, 0xba, 0x50, 0x68, 0x1d, 0xe5,
287 0x5c, 0x62, 0x15, 0x99, // header
288 0x70, 0x73, 0x65, 0x75, 0x64, 0x6f, 0x72, 0x61, // payload
289 0x6e, 0x64, 0x6f, 0x6d, 0x6e, 0x65, 0x73, 0x73,
290 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20,
291 0x6e, 0x65, 0x78, 0x74, 0x20, 0x62, 0x65, 0x73,
292 0x74, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x67};
293uint32 ROC = 0xd462564a;
294
295static int testF8()
296{
297 IncomingRTPPkt* rtp = new IncomingRTPPkt(rtpPacket, sizeof(rtpPacket));
298 AesSrtp* aesCipher = new AesSrtp();
299 AesSrtp* f8AesCipher = new AesSrtp();
300
301 aesCipher->setNewKey(key, sizeof(key));
302
303 /* Create the F8 IV (refer to chapter 4.1.2.2 in RFC 3711):
304 *
305 * IV = 0x00 || M || PT || SEQ || TS || SSRC || ROC
306 * 8Bit 1bit 7bit 16bit 32bit 32bit 32bit
307 * ------------\ /--------------------------------------------------
308 * XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
309 */
310
311 unsigned char derivedIv[16];
312 uint32 *ui32p = (uint32 *)derivedIv;
313
314 derivedIv[0] = 0;
315 memcpy(&derivedIv[1], rtp->getRawPacket()+1, 11);
316
317 // set ROC in network order into IV
318 ui32p[3] = htonl(ROC);
319
320 int32 pad = rtp->isPadded() ? rtp->getPaddingSize() : 0;
321
322 if (memcmp(iv, derivedIv, 16) != 0) {
323 cerr << "Wrong IV constructed" << endl;
324 hexdump("derivedIv", derivedIv, 16);
325 hexdump("test vector Iv", iv, 16);
326 return -1;
327 }
328
329 // now encrypt the RTP payload data
330 aesCipher->f8_encrypt(rtp->getPayload(), rtp->getPayloadSize()+pad,
331 derivedIv, key, sizeof(key), salt, sizeof(salt), f8AesCipher);
332
333 // compare with test vector cipher data
334 if (memcmp(rtp->getPayload(), cipherText, rtp->getPayloadSize()+pad) != 0) {
335 cerr << "cipher data mismatch" << endl;
336 hexdump("computed cipher data", rtp->getPayload(), rtp->getPayloadSize()+pad);
337 hexdump("Test vcetor cipher data", cipherText, sizeof(cipherText));
338 return -1;
339 }
340
341 // Now decrypt the data to get the payload data again
342 aesCipher->f8_encrypt(rtp->getPayload(), rtp->getPayloadSize()+pad,
343 derivedIv, key, sizeof(key), salt, sizeof(salt), f8AesCipher);
344
345 // compare decrypted data with test vector payload data
346 if (memcmp(rtp->getPayload(), payload, rtp->getPayloadSize()+pad) != 0) {
347 cerr << "payload data mismatch" << endl;
348 hexdump("computed payload data", rtp->getPayload(), rtp->getPayloadSize()+pad);
349 hexdump("Test vector payload data", payload, sizeof(payload));
350 return -1;
351 }
352 return 0;
353}
354
355int main(int argc, char *argv[])
356{
357 int result = 0;
358 bool send = false;
359 bool recv = false;
360 bool f8Test = false;
361
362 char c;
363 char* inputKey = NULL;
364
365 /* check args */
366 while (1) {
367 c = getopt(argc, argv, "k:rs8");
368 if (c == -1) {
369 break;
370 }
371 switch (c) {
372 case 'k':
373 inputKey = optarg;
374 break;
375 case 'r':
376 recv = true;
377 break;
378 case 's':
379 send = true;
380 break;
381 case '8':
382 f8Test = true;
383 break;
384 default:
385 cerr << "Wrong Arguments" << endl;
386 exit(1);
387 }
388 }
389
390 if (inputKey == NULL) {
391 inputKey = fixKey;
392 }
393 hex_string_to_octet_string((char*)binKeys, inputKey, 60);
394
395 if (send || recv) {
396 if (send) {
397 cout << "Running as sender" << endl;
398 }
399 else {
400 cout << "Running as receiver" << endl;
401 }
402 }
403 else if (f8Test) {
404 int ret = testF8();
405 exit(ret);
406 }
407
408 RecvPacketTransmissionTest *rx;
409 SendPacketTransmissionTest *tx;
410
411 // accept as parameter if must run as -s, -r, -8
412
413 // run several tests in parallel threads
414 if (send) {
415 tx = new SendPacketTransmissionTest();
416 tx->start();
417 tx->join();
418 } else if (recv) {
419 rx = new RecvPacketTransmissionTest();
420 rx->start();
421 rx->join();
422 }
423 exit(result);
424}
425
426/** EMACS **
427 * Local variables:
428 * mode: c++
429 * c-basic-offset: 4
430 * End:
431 */
432