Added 'goodfet.ccspi sniffnonce' for sniffing the nonce sequences.
[goodfet] / client / goodfet.ccspi
1 #!/usr/bin/env python
2
3 #GoodFET Chipcon SPI Client
4 # (C) 2011 Travis Goodspeed
5 # Additions 2011-2012 Ryan Speers ryan@rmspeers.com
6
7 #N.B.,
8 #Might be CC2420 Specific
9
10 import sys;
11 import binascii;
12 import array, time;
13
14 from GoodFETCCSPI import GoodFETCCSPI;
15
16
17 #Some quick functions for yanking values out of a packet.
18 def srcadr(packet):
19     """Returns the source address of a packet as an integer."""
20     return ord(packet[4])+(ord(packet[5])<<8);
21 def isencrypted(packet):
22     """Returns true if the packet is encrypted.""";
23     return ord(packet[1])&0x08;
24 def pktnonceseq(packet):
25     """Returns the nonce sequence of a packet."""
26     nonce=0;
27     for byte in [0xa,9,8,7]:
28         nonce=(nonce<<8)|ord(packet[byte]);
29     return nonce;
30
31 if(len(sys.argv)==1):
32     print "Usage: %s verb [objects]\n" % sys.argv[0];
33     print "%s info" % sys.argv[0];
34     print "%s regs" % sys.argv[0];
35     print "%s ram" % sys.argv[0];
36     print "%s ramtest" % sys.argv[0];
37     print "%s test" % sys.argv[0];
38     print "%s peek 0x$start [0x$stop]" % sys.argv[0];
39     print "%s poke 0x$adr 0x$val" % sys.argv[0];
40     print "%s txtest" % sys.argv[0];
41     
42     print "\n%s rssi" % sys.argv[0];
43     print "%s spectrum" % sys.argv[0];
44     print "%s spectrumcsv" % sys.argv[0];
45     
46     print "\n%s surf" % sys.argv[0];
47     print "%s sniff [chan]" % sys.argv[0];
48     print "%s sniffstrings [chan]" % sys.argv[0];
49     print "%s bsniff [chan]" % sys.argv[0];
50     print "%s sniffcrypt 0x$key [chan]" % sys.argv[0];
51     print "%s sniffdissect" % sys.argv[0];
52     print "%s sniffnonce" % sys.argv[0];
53     
54     print "\n%s txtoscount [-i|-r]   TinyOS BlinkToLED" % sys.argv[0];
55     print "%s reflexjam [channel=11] [delay=0]" % sys.argv[0];
56
57     sys.exit();
58
59 #Initialize FET and set baud rate
60 client=GoodFETCCSPI();
61 client.serInit()
62
63 client.setup();
64
65 #Dummy read.
66 #Might read as all ones if chip has a startup delay.
67
68 if(sys.argv[1]=="carrier"):
69     if len(sys.argv)>2:
70         client.RF_setfreq(eval(sys.argv[2]));
71     while 1:
72         client.RF_carrier();
73     while(1):
74         time.sleep(1);
75
76 if(sys.argv[1]=="modulated_spectrum"):
77     if len(sys.argv)>2:
78         client.RF_setfreq(eval(sys.argv[2]));
79     while 1:
80         client.RF_modulated_spectrum();
81     while(1):
82         time.sleep(1);
83
84 if(sys.argv[1]=="reflexjam" or sys.argv[1]=="reflexjamack"):
85     #Setup the radio to listen promiscously on a frequency
86     client.RF_promiscuity(1);
87     client.RF_autocrc(0);
88     if len(sys.argv)>2:
89         freq=eval(sys.argv[2]);
90         if freq>100:
91             client.RF_setfreq(freq);
92         else:
93             client.RF_setchan(freq);
94     duration=0;
95     if len(sys.argv)>3:
96         duration=eval(sys.argv[3]);
97     client.CC_RFST_RX();
98     print "Reflexively jamming on %i MHz" % (client.RF_getfreq()/10**6);
99     #Now we let the firmware take over, watching for packets and jamming them.
100     #Standard reflexive jam is done with duration=0.
101     #To selectively jam packets that are above a certain length, set duration
102     # to the number of milliseconds needed to jam frames of that length.
103     # Api-Do project has script available to tune/test this duration.
104     #  code.google.com/p/zigbeesecurity (rmspeers)
105     if sys.argv[1]=="reflexjam":
106         client.RF_reflexjam(duration);
107     elif sys.argv[1]=="reflexjamack":
108         client.RF_reflexjam_autoack();
109
110 if(sys.argv[1]=="info"):
111     print "Found   %s" % client.identstr();
112     print "Freq:   %05f MHz" % (client.RF_getfreq()/(10**6));
113     print "Status: %s" % client.status();
114 if(sys.argv[1]=="regs"):
115     for adr in range(0x10,0x40): #*1024):
116         val=client.peek(adr);
117         print "%04x:=0x%04x" % (adr,val);
118 if(sys.argv[1]=="ram"):
119     for adr in range(0x0,0x16D,16):
120         row=client.peekram(adr,32);
121         s="";
122         for foo in row:
123             s=s+(" %02x" % ord(foo))
124         print "%04x: %s" % (adr,s);
125 if(sys.argv[1]=="ramtest"):
126     client.pokeram(0x00,[0xde,0xad,0xbe,0xef,
127                          0xde,0xad,0xbe,0xef,
128                          0xde,0xad,0xbe,0xef,
129                          0xde,0xad,0xbe,0xef,
130                          0xde,0xad,0xbe,0xef,
131                          0xde,0xad,0xbe,0xef,
132                          0xde,0xad,0xbe,0xef,
133                          0xde,0xad,0xbe,0xef,
134                          0xde,0xad,0xbe,0xef,
135                          0xde,0xad,0xbe,0xef,
136                          0xde,0xad,0xbe,0xef]);
137     
138     for adr in range(0x0,0x16D,16):
139         row=client.peekram(adr,32);
140         s="";
141         for foo in row:
142             s=s+(" %02x" % ord(foo))
143         print "%04x: %s" % (adr,s);
144 if(sys.argv[1]=="test"):
145     data=client.trans([0x20, 0xde, 0xad]);
146     print "%02x %02x" % (ord(data[1]), ord(data[2]));
147     data=client.trans([0x40|0x20, 0xde, 0xad]);
148     print "%02x %02x" % (ord(data[1]), ord(data[2]));
149 if(sys.argv[1]=="rssi"):
150     if len(sys.argv)>2:
151         freq=eval(sys.argv[2]);
152         if freq>100:
153             client.RF_setfreq(freq);
154         else:
155             client.RF_setchan(freq);
156     print "Listening on %f MHz." % (client.RF_getfreq()/10.0**6);
157         
158     client.strobe(0x02); #Calibrate
159     time.sleep(1);
160     
161     while 1:
162         client.CC_RFST_RX();
163         #client.strobe(0x03); #SRXON
164         rssi=client.RF_getrssi();
165         #client.CC_RFST_IDLE(); #idle
166         time.sleep(0.01);
167         string="";
168         for foo in range(0,rssi>>2):
169             string=("%s."%string);
170         print "%02x %04i %s" % (rssi,rssi, string); 
171 if(sys.argv[1]=="spectrum"):
172     for chan in range(2400000000,2480000000,5000000):
173         client.RF_setfreq(chan);
174         #print "Listening on %f MHz." % (client.RF_getfreq()/10.0**6);
175         
176         client.strobe(0x02); #Calibrate
177         #time.sleep(0.01);
178         
179         maxrssi=0;
180         for foo in range(1,10):
181             client.CC_RFST_RX();
182             rssi=client.RF_getrssi();
183             maxrssi=max(rssi,maxrssi);
184         string="";
185         for foo in range(50,rssi):
186             string=("%s."%string);
187         print "%04i %i %s" % (client.RF_getfreq()/10.0**6,rssi, string); 
188 if(sys.argv[1]=="spectrumcsv"):
189     start=time.time();
190     while 1:
191         for freq in range(2400000000,2480000000,1000000):
192             client.RF_setfreq(freq);
193             
194             client.strobe(0x02); #Calibrate
195             client.CC_RFST_RX();
196             rssi=client.RF_getrssi();
197         
198             print "%f %i %3i" % (
199                 time.time()-start,
200                 client.RF_getfreq()/10.0**6,
201                 rssi); 
202         sys.stdout.flush();
203
204 if sys.argv[1]=="surf":
205     print "Scanning channels [11,26].";
206     
207     #Promiscuous mode.
208     client.RF_promiscuity(1);
209     client.RF_autocrc(1);
210     
211     chan=11;
212     if len(sys.argv)>2:
213         chan=eval(sys.argv[2]);
214         
215     client.CC_RFST_RX();
216     
217     #Now we're ready to get packets.
218     while 1:
219         if chan>26: chan=11;
220         
221         client.setup(); #Really oughtn't be necessary, but can't hurt.
222         client.RF_setchan(chan);
223         
224         packet=None;
225         lasttime=time.time();
226         while packet==None and time.time()-lasttime<0.5:
227             packet=client.RF_rxpacket();
228         if packet!=None:
229             client.printpacket(packet=packet,
230                            prefix=("%02d: "%chan));
231         sys.stdout.flush();
232         chan=chan+1;
233
234 if(sys.argv[1]=="sniff" or sys.argv[1]=="sniffdissect" or sys.argv[1]=="sniffstrings" or
235    sys.argv[1]=="sniffnonce"):
236     #Promiscuous mode.
237     client.RF_promiscuity(1);
238     client.RF_autocrc(1);
239     
240     if len(sys.argv)>2:
241         freq=eval(sys.argv[2]);
242         if freq>3000:
243             client.RF_setfreq(freq);
244         elif freq>100:
245             client.RF_setfreq(freq*1000000);
246         else:
247             client.RF_setchan(freq);
248     client.CC_RFST_RX();
249     print "Listening as %010x on %i MHz" % (client.RF_getsmac(),
250                                             client.RF_getfreq()/10**6);
251     #Now we're ready to get packets.
252     while 1:
253         #client.setup(); #Really oughtn't be necessary, but can't hurt.
254         client.CC_RFST_RX();
255         
256         packet=None;
257         while packet==None:
258             packet=client.RF_rxpacket();
259         if sys.argv[1]=="sniffdissect":
260             client.printdissect(packet);
261         elif sys.argv[1]=="sniffstrings":
262             print packet;
263         elif sys.argv[1]=="sniffnonce":
264             if isencrypted(packet):
265                 print "%04x: %08x" % (srcadr(packet),pktnonceseq(packet));
266         else:
267             client.printpacket(packet);
268         sys.stdout.flush();
269
270 if(sys.argv[1]=="bsniff"):
271     #Just broadcast.
272     client.RF_promiscuity(0);
273     client.RF_setsmac(0xFFFFFFFF);
274     client.RF_autocrc(1);
275     
276     if len(sys.argv)>2:
277         freq=eval(sys.argv[2]);
278         if freq>100:
279             client.RF_setfreq(freq);
280         else:
281             client.RF_setchan(freq);
282     client.CC_RFST_RX();
283     print "Listening as %010x on %i MHz" % (client.RF_getsmac(),
284                                             client.RF_getfreq()/10**6);
285     #Now we're ready to get packets.
286     while 1:
287         packet=None;
288         while packet==None:
289             packet=client.RF_rxpacket();
290         client.printpacket(packet);
291         sys.stdout.flush();
292
293 if(sys.argv[1]=="sniffcrypt"):
294     print "Zigbee crypto is pretty damned complicated, and this doesn't work yet.";
295     #Just broadcast.
296     client.RF_promiscuity(1);
297     client.RF_setsmac(0xFFFFFFFF);
298     client.RF_autocrc(1);
299     #client.poke(0x19, 0x03C7); #SECCTRL0, enabling CCM crypto w/ KEY0
300     client.poke(0x19, 0x03C6); #SECCTRL0, enabling CTRL crypto w/ KEY0
301     
302     #What follows is the nonce.
303     client.poke(0x20, 0x000a); #SECCTRL1, skipping 10 bytes of header
304     
305     if len(sys.argv)>2:
306         key=int(sys.argv[2],16);
307         nonce=int(sys.argv[3],16);
308         
309         print "Setting KEY0 to %x" % key;
310         print "Setting NONCE to %x" % nonce;
311         client.RF_setkey(key);
312         client.RF_setnonce(nonce);
313     if len(sys.argv)>3:
314         freq=eval(sys.argv[3]);
315         if freq>100:
316             client.RF_setfreq(freq);
317         else:
318             client.RF_setchan(freq);
319     client.CC_RFST_RX();
320     print "Listening as %010x on %i MHz" % (client.RF_getsmac(),
321                                             client.RF_getfreq()/10**6);
322     #Now we're ready to get packets.
323     while 1:
324         packet=None;
325         while packet==None:
326             packet=client.RF_rxpacketdec();
327         client.printpacket(packet);
328         sys.stdout.flush();
329
330 if(sys.argv[1]=="txtest"):
331     if len(sys.argv)>2:
332         freq=eval(sys.argv[2]);
333         if freq>100:
334             client.RF_setfreq(freq);
335         else:
336             client.RF_setchan(freq);
337     print "Transmitting DEADBEEF as %010x on %i MHz" % (
338         client.RF_getsmac(),
339         client.RF_getfreq()/10**6);
340     
341     while 1:
342         client.RF_txpacket([0x0f, 0x01, 0x08, 0x82,
343                             0xff, 0xff, 0xff, 0xff,
344                             0xde, 0xad, 0xbe, 0xef,
345                             0xba, 0xbe, 0xc0]);
346
347 if(sys.argv[1]=="txtoscount"):
348     '''
349     Clone of what TinyOS's RadioCountToLeds demo code does.  Specify a
350     channel a TinyOS mote programmed with RadioCountToLeds is on, and
351     this will act as the second device. (ryan@rmspeers.com)
352     '''
353     if (len(sys.argv)<=3):
354         print "Provide -r to work via replays or -i to work via incrementing itself.";
355         sys.exit(1);
356     if (sys.argv[3]=="-r"):
357         client.RF_promiscuity(1);
358     client.RF_autocrc(1);
359     if len(sys.argv)>2:
360         freq=eval(sys.argv[2]);
361         if freq>100:
362             client.RF_setfreq(freq);
363         else:
364             client.RF_setchan(freq);
365     if (sys.argv[3]=="-r"):
366         client.CC_RFST_RX();
367         print "Listening as %010x on %i MHz" % (client.RF_getsmac(), client.RF_getfreq()/10**6);
368     print "Transmitting like the TinyOS CountToRadio program on %i MHz" % (client.RF_getfreq()/10**6);
369     if (sys.argv[3]=="-i"):
370         i = 0;
371         countpkt = [0x0f, 0x41, 0x88, 0xFF, 0x22, 0x00, 0xff, 0xff, 0x01, 0x00, 0x3f, 0x06, 0x00, 0xFF];
372     while 1:
373         if (sys.argv[3]=="-r"): #give -r to do via replays from the other device
374             packet=None;
375             while packet==None:
376                 packet=client.RF_rxpacket();
377             pkt = packet[:14];
378             client.RF_txpacket(pkt);
379         elif (sys.argv[3]=="-i"): #give -i to have it increment and send
380             #Use this code for it to actually do increments itself:
381             pkt = countpkt[:];
382             pkt[3] = i;
383             pkt[13] = i+1;
384             client.RF_txpacket(pkt);
385             if i >= 31: i = 0;
386             else:       i += 1;
387             time.sleep(0.5);
388
389 if(sys.argv[1]=="txpiptest" or sys.argv[1]=="txpipscapy"):
390     if len(sys.argv)>2:
391         freq=eval(sys.argv[2]);
392         if freq>100:
393             client.RF_setfreq(freq);
394         else:
395             client.RF_setchan(freq);
396     print "Transmitting on as %010x on %i MHz" % (
397         client.RF_getsmac(),
398         client.RF_getfreq()/10**6);
399     
400     client.RF_setsync(0xFFFF);
401     
402     while 1:
403         if(sys.argv[1]=="txpiptest"):
404             client.RF_txpacket([
405                     0x7f, 
406                     #Real header, must begin with SFD.
407                     0x00, 0x00, 0x00,
408                     0x00, 0xA7,
409                     
410                     #Length
411                     0x1f, 0x01, 0x08, 0x82,
412                     0xDF, 0xff, 0xff, 0xff,
413                     0xde, 0xad, 0xbe, 0xef,
414                     0xba, 0xbe, 0xc0,
415                     
416                     #Preamble
417                     0x00, 0x00, 0x00,
418                     #SFD
419                     0x00, 0xA7,  #CC2420 SFD
420                     #Packet In Packet
421                     0x0f, 0x01, 0x08, 0x82,
422                     0xff, 0xff, 0xff, 0xff,
423                     0xde, 0xad, 0xbe, 0xef,
424                     0xba, 0xbe, 0xc0,
425                     
426                     0xff, 0xff, 0xff, 0xff,
427                     0xff, 0xff, 0xff, 0xff,
428                     0xff, 0xff, 0xff, 0xff,
429                     0xff, 0xff, 0xff, 0xff,
430                     0xff, 0xff, 0xff, 0xff,
431                     0xff, 0xff, 0xff, 0xff,
432                     0xff, 0xff, 0xff, 0xff,
433                     ]);
434         elif(sys.argv[1]=="txpipscapy"):
435             # NB: Requires Scapy with dot15d4.py layer. (rmspeers)
436             try:
437                 from scapy.all import Dot15d4, Dot15d4FCS, Dot15d4Data, Raw
438                 import struct
439             except ImportError:
440                 print "To use packet building, Scapy must be installed and have the dot15d4 layer present."
441                 print "try: hg clone http://hg.secdev.org/scapy-com";
442                 print "     sudo ./setup.py install";
443             #Overall method is to build from the inner packet outwards in the pkt string
444             # Make inner packet
445             scapyinner = Dot15d4FCS(seqnum=130)/Dot15d4Data()/Raw('\xde\xad\xbe\xef');
446             pkt = str(scapyinner);                  #build inner pkt to bytes, adding FCS automatically
447             pkt = struct.pack('b', len(pkt)) + pkt  #prepend with its length
448             pkt = "\x00\x00\x00\x00\xA7" + pkt      #add preamble and SFD to inner packet
449             # Make outer (wrapping) packet
450             scapyouter = Dot15d4(seqnum=130)/Dot15d4Data(dest_panid=0xffdf)/Raw('\xde\xad\xbe\xef\xba\xbe\xc0') #TODO why need these last 3 bytes?
451             pkt = str(scapyouter) + pkt
452             pkt = struct.pack('b', len(pkt)) + pkt  #prepend with its length
453             pkt = '\x00\x00\x00\x00\xA7' + pkt + ('\xff'*28) #start with preamble/SFD and add 0xff fill at end
454             pkt = struct.pack('b', len(pkt)) + pkt  #prepend with its length (originally used \x7f)
455             client.printpacket(pkt)
456             client.RF_autocrc(1);
457             client.RF_txpacket(pkt)
458
459 if(sys.argv[1]=="peek"):
460     start=0x0000;
461     if(len(sys.argv)>2):
462         start=int(sys.argv[2],16);
463     stop=start;
464     if(len(sys.argv)>3):
465         stop=int(sys.argv[3],16);
466     print "Peeking from %04x to %04x." % (start,stop);
467     while start<=stop:
468         print "%04x: 0x%04x" % (start,client.peek(start));
469         start=start+1;
470 if(sys.argv[1]=="poke"):
471     start=0x0000;
472     val=0x00;
473     if(len(sys.argv)>2):
474         start=int(sys.argv[2],16);
475     if(len(sys.argv)>3):
476         val=int(sys.argv[3],16);
477     print "Poking r%02x to become 0x%04x." % (start,val);
478     
479     client.poke(start,val);
480