1
0
Fork 0

Initial checkin.

This commit is contained in:
flabbergast 2014-11-14 10:11:41 +00:00
commit d853bc95d9
31 changed files with 5815 additions and 0 deletions

13
.gitignore vendored Normal file
View file

@ -0,0 +1,13 @@
*.hex
*.bin
*.eep
*.o
*.elf
*.map
*.lss
*.lst
*.d
*.sym
.idea
LUFA/

19
LICENSE Normal file
View file

@ -0,0 +1,19 @@
Copyright (c) 2014 flabbergast at sdfeu dott org
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

81
README.md Normal file
View file

@ -0,0 +1,81 @@
# sha204_playground
Firmware for ATMEL chips to communicate with ATSHA204 chip interactively
over Serial. Sources are supplied in two forms:
- As AVR-GCC project utilizing the [LUFA] library (for USB capable
ATMEL chips, tested on atmega32U4 and atxmega128a3u).
- As an [Arduino] sketch.
Along with this, a python script `talk_to_sha204.py` is provided to
access some of the functionality of the ATSHA204 chip, talking to
it via the firmware above (in a binary mode), a-la [hashlet].
## Usage / Installation
Please see the READMEs in the individual subdirectories for some info on
how to use them.
## Pictures
The original impetus to write this stuff for me was that I had a couple
of ATSHA204 breakout boards from [Sparkfun] and I wanted to play with
them outside Arduino, using just avr-gcc and [LUFA], on some USB sticks
with ATMEL chips. Here are pics with two of them:
![A pic of atmega32U4 USB stick with ATSHA204](https://dl.dropboxusercontent.com/u/638633/scriptogram/sha204_m32u4_stick.jpg "atmega32u4 USB stick with ATSHA204")
And matrixstorm's [AVR stick] with the same Sparkfun's ATSHA breakout:
![A pic of AVR stick with ATSHA204](https://dl.dropboxusercontent.com/u/638633/scriptogram/sha204_avrstick.jpg "AVR stick with ATSHA204")
## Idle thoughts / ramblings
So, what can be ATSHA204 used for? Well, it performs hashing (SHA256)
and it can securely store "keys" (32 bytes long blocks of data).
The important point (for me) is that to verify that a "signature" (hash,
MAC) was generated on a particular ATSHA requires knowing all the
"secrets" that were used to generate the signature. This works fine for
Client/Server-like situations: "Client" ATSHA generates a signature for,
say, a file, (e.g. on a local computer), which is then sent to "Server"
for verification. However the "Server" has to know the "secret keys" on
ATSHA that were used in the signature computation; so either the
"Server" has a copy of the keys stored in ATSHA, or there's another
ATSHA on the "Server" with the keys.
This kind of thing can be used for instance to roll one's own
authentication service a-la [YubiKey].
What I wanted to use it for is to securely store encryption keys
locally, for a "two-factor key storage". This is also possible to do:
user enters a password, this is passed to ATSHA which generates a hash
of the user's password, together with one of its "secret keys". This
hash is then used as an encryption key. So to obtain the
encryption key, one has to know the password and have the ATSHA along.
## Notes
Please see the READMEs in the individual subdirectories for more info
about the various parts of the project, as well as credits.
## TODO / Roadmap
- Make also binary-mode-only versions of the firmware (for smaller than
32kB chips).
## License
My code is (c) flabbergast. MIT license (see LICENSE file). Portions
of the code come from LUFA demos, this is licensed by LUFA's license.
The original code from SHA204 library is licensed by Apache V2.0 license.
[AVRstick]: http://matrixstorm.com/avr/avrstick/
[LUFA]: http://www.fourwalledcubicle.com/LUFA.php
[hashlet]: https://github.com/cryptotronix/hashlet
[YubiKey]: https://www.yubico.com/products/yubikey-hardware/yubikey-2/
[Arduino]: http://www.arduino.cc/
[Sparkfun]: https://www.sparkfun.com/products/11551
[AVR stick]: http://matrixstorm.com/avr/avrstick/

47
arduino/README.md Normal file
View file

@ -0,0 +1,47 @@
# sha204_playground firmware: Arduino version
## Installation / usage
Copy the `avr/SHA204` library to your Arduino IDE's `libraries` folder,
and copy the sketch `sha204_playground_arduino` to your sketchbook.
Make the modifications to the library file
`SHA204/SHA204SWI_hardware_config.h` to match your hardware setup (i.e.
to which pin is your ATSHA204 connected to).
Compile and upload the sketch to your Arduino using IDE. Open the IDE's
Serial Monitor to talk to the Arduino/ATSHA204.
Note that I've tested on Arduino IDE version 1.0.5.
## Problems
Arduino sets the size of the Serial buffer to 64. This seems to cause
problems when the data sent to firmware is longer than that (which it is
for instance with the `talk_to_sha204 check_mac` command). If things
work, but you get a weird error with this command, increase the size of
the Serial buffer. Unfortunately, this requires editing a file in the
Arduino directory:
`<ARDUINO_APP_DIR>/<MAYBE_SOME_MORE_DIRS/hardware/arduino/cores/arduino/HardwareSerial.cpp`,
change `#define SERIAL_BUFFER_SIZE 64` to `#define SERIAL_BUFFER_SIZE
100`.
## Communicating with the firmware
Probably the first thing to try is pressing `k` (to test waking the
ATSHA up) and pressing `s` (to print the serial number of your device).
Alternatively (depending on the functionality required), you can use the
`talk_to_sha204.py` script to talk to the ATSHA via this firmware.
### Binary mode
The firmware supports a "binary mode", for use with scripts. For a
demonstration on how this is done, have a look at the python script
`talk_to_sha204.py`.
## License
My code is (c) flabbergast. MIT license (see LICENSE file). Portions
of the code come from LUFA demos, this is licensed by LUFA's license.
The original code from SHA204 library is licensed by Apache V2.0 license.

View file

@ -0,0 +1,810 @@
/*
* sha204_playground.ino
* (c) 2014 flabbergast
*
* Firmware for USB capable ATMEL chips (tested on atmega32U4 and
* atxmega128a3u) to communicate with ATSHA204 (single-wire
* interface) interactively, over Serial.
*
* The code is quite ugly ... one reason being that it's just an Arduino adaptatation
* of the original avr-gcc/LUFA code, so some of the things are unnecessarily complicated.
* I like to keep it this way, so that it's easier to merge any patches/changes to the
* original code.
*/
#include "SHA204SWI.h"
#include "SHA204Definitions.h" // for constants and such
#include "SHA204ReturnCodes.h" // want messages for return codes
/*************************************************************************
* ----------------------- Global variables -----------------------------*
*************************************************************************/
// when this byte is received, switch to binary mode
#define BINARY_MODE_CHAR 0xFD
#define MAX_BUFFER_SIZE 100
volatile uint8_t idle = 0;
volatile uint8_t hexprint_separator = ' ';
SHA204SWI sha204;
/*************************************************************************
* ----------------------- Helper functions -----------------------------*
*************************************************************************/
void hexprint(uint8_t *p, uint16_t length);
void hexprint_noln(uint8_t *p, uint16_t length);
void hexprint_byte(uint8_t b);
void hexprint_byte_sep(uint8_t b);
uint8_t get_bytes_serial(uint8_t *output, uint16_t len);
void print_help(void);
void print_executing(void);
void print_received_from_sha(uint8_t *rx_buffer);
void print_execute_params(uint8_t opcode, uint8_t param1);
void print_return_code(uint8_t code);
void process_config(uint8_t *config);
void sleep_or_idle(SHA204SWI *sha204);
uint8_t receive_serial_binary_transaction(uint8_t *buffer, uint8_t len);
uint8_t binary_mode_transaction(uint8_t *data, uint8_t rxsize, uint8_t *rx_buffer, SHA204SWI *sha204);
#define BINARY_TRANSACTION_OK 0
#define BINARY_TRANSACTION_RECEIVE_ERROR 1
#define BINARY_TRANSACTION_PARAM_ERROR 2
#define BINARY_TRANSACTION_EXECUTE_ERROR 3
/* A hack so that I don't have to edit too much code from the original AVR code */
// Declarations originally from LufaLayer.h
uint16_t usb_serial_available(void); // number of getchars guaranteed to succeed immediately
int16_t usb_serial_getchar(void); // negative values mean error in receiving (not connected or no input)
void usb_serial_putchar(uint8_t ch);
void usb_serial_write(const char* const buffer);
void usb_serial_write_P(PGM_P data);;
uint16_t usb_serial_readline(char *buffer, const uint16_t buffer_size, const bool obscure_input); // BLOCKING (takes care of _tasks)
#define W(s) Serial.print(F(s))
#define Wl(s) Serial.println(F(s))
/* Setup */
void setup(void)
{
sha204.power_up();
/* Initialisation */
Serial.begin(115200);
print_help();
}
void loop() {
/* Variables */
uint8_t rx_buffer[SHA204_RSP_SIZE_MAX];
uint8_t tx_buffer[MAX_BUFFER_SIZE];
uint8_t configuration_zone[88];
uint8_t r;
uint8_t param1;
uint16_t param2;
uint8_t data1[32];
uint8_t data2[32];
uint8_t data3[14];
// main serial processing
if(usb_serial_available() > 0) {
byte c = (byte)usb_serial_getchar();
if(c==BINARY_MODE_CHAR) { // handle binary mode for one transaction
r = receive_serial_binary_transaction(tx_buffer, MAX_BUFFER_SIZE); // blocking
if(r == BINARY_TRANSACTION_OK)
r = binary_mode_transaction(tx_buffer, SHA204_RSP_SIZE_MAX, rx_buffer, &sha204); // blocking
// transmit the response
usb_serial_putchar(r);
if(r == BINARY_TRANSACTION_OK)
for(r=0; r<rx_buffer[0]; r++)
usb_serial_putchar(rx_buffer[r]);
} else {
if(idle)
Wl("--- I ---");
else
Wl("--- S ---");
switch(c) { // yes, code like this sucks
case 's': // serial number
Wl("Request serial number.");
sha204.wakeup(rx_buffer);
r = sha204.serialNumber(rx_buffer);
print_executing();
print_return_code(r);
Wl("Should get: 01 23 xx xx xx xx xx xx EE");
W("Received SN: ");
hexprint(rx_buffer, 9);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
break;
case 'o': // config zone
Wl("Request and display config zone contents.");
print_executing();
memset(configuration_zone,0,32);
if(!(r=sha204.wakeup(rx_buffer))) { // read first 2 32-byte blocks manually
if(!(r=sha204.read(tx_buffer,rx_buffer,SHA204_ZONE_CONFIG|READ_ZONE_MODE_32_BYTES,0))) {
memcpy(configuration_zone,rx_buffer+1,32);
if(!(r=sha204.read(tx_buffer,rx_buffer,SHA204_ZONE_CONFIG|READ_ZONE_MODE_32_BYTES,32))) {
memcpy(configuration_zone+32, rx_buffer+1, 32);
uint8_t addr = 64; // have to read the rest of the zone in 4-byte blocks
while(addr < 88) {
if((r=sha204.read(tx_buffer,rx_buffer,SHA204_ZONE_CONFIG,addr)))
break;
memcpy(configuration_zone+addr, rx_buffer+1, 4);
addr+=4;
}
}
}
}
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
if(!r)
process_config(configuration_zone);
break;
case 'c': // check_mac
Wl("Calculate and compare MAC (CheckMAC command).");
Wl("Enter mode (1 byte; default 0):");
param1 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param1 = tx_buffer[0];
Wl("Enter Slot ID (1 byte; default 0):");
param2 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param2 = tx_buffer[0];
Wl("Enter client challenge (32 bytes; default 0):");
memset((void *)data1, 0, 32);
get_bytes_serial(data1, 32);
Wl("Enter client response (32 bytes; default 0):");
memset((void *)data2, 0, 32);
get_bytes_serial(data2, 32);
Wl("Enter other data (13 bytes; default 0):");
memset((void *)data3, 0, 13);
get_bytes_serial(data3, 13);
print_execute_params(SHA204_CHECKMAC,param1);
hexprint_byte_sep((uint8_t)param2);
W("data1 ");
hexprint(data1, 32);
W("data2 ");
hexprint(data2, 32);
W("data3 ");
hexprint(data3, 13);
print_executing();
sha204.wakeup(rx_buffer);
r = sha204.check_mac(tx_buffer, rx_buffer, param1, param2, data1, data2, data3);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
print_received_from_sha(rx_buffer);
break;
case 'd': // derive_key
Wl("Combine current key with nonce and store in a key slot (DeriveKey command).");
Wl("Enter random (1 byte: 00 or 04; default 0):");
param1 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param1 = tx_buffer[0];
Wl("Enter Target Slot ID (1 byte; default 0):");
param2 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param2 = tx_buffer[0];
Wl("Enter MAC for validation (0 or 32 bytes; default 0):");
memset((void *)data1, 0, 32);
get_bytes_serial(data1, 32);
print_execute_params(SHA204_DERIVE_KEY,param1);
hexprint_byte_sep((uint8_t)param2);
W("data1 ");
hexprint(data1, 32);
print_executing();
sha204.wakeup(rx_buffer);
r = sha204.derive_key(tx_buffer, rx_buffer, param1, param2, data1);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
print_received_from_sha(rx_buffer);
break;
case 'v': // dev_rev
Wl("Request device revision.");
sha204.wakeup(rx_buffer);
r = sha204.dev_rev(tx_buffer, rx_buffer);
print_executing();
print_return_code(r);
print_received_from_sha(rx_buffer);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
break;
case 'g': // gen_dig
Wl("Compute SHA-256 from TempKey+stored value, store result in TempKey (GenDig command).");
Wl("Enter zone (1 byte: 00/Config or 01/OTP or 02/Data; default 0):");
param1 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param1 = tx_buffer[0];
Wl("Enter Key ID / OTP slot ID (1 byte; default 0):");
param2 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param2 = tx_buffer[0];
Wl("Enter other data (4 bytes when CheckKey, otherwise ignored; default 0):");
memset((void *)data1, 0, 4);
get_bytes_serial(data1, 4);
print_execute_params(SHA204_GENDIG,param1);
hexprint_byte_sep((uint8_t)param2);
W("data1 ");
hexprint(data1, 4);
print_executing();
sha204.wakeup(rx_buffer);
r = sha204.gen_dig(tx_buffer, rx_buffer, param1, param2, data1);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
print_received_from_sha(rx_buffer);
break;
case 'h': // HMAC
Wl("Compute HMAC/SHA-256 digest from key + other info on device (HMAC command).");
Wl("Enter mode (1 byte; default 0):");
param1 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param1 = tx_buffer[0];
Wl("Enter Slot ID (2 bytes; default 0):");
param2 = 0;
if(2 == get_bytes_serial(tx_buffer, 2))
param2 = tx_buffer[0]*256 + tx_buffer[1];
print_execute_params(SHA204_HMAC,param1);
hexprint_byte_sep((uint8_t)(param2>>8));
hexprint_byte((uint8_t)param2);
W("\n\r");
print_executing();
sha204.wakeup(rx_buffer);
r = sha204.hmac(tx_buffer, rx_buffer, param1, param2);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
print_received_from_sha(rx_buffer);
break;
case 'm': // mac
Wl("Compute SHA-256 from key + challenge + other info on device (MAC command).");
Wl("Enter mode (1 byte; default 0):");
param1 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param1 = tx_buffer[0];
Wl("Enter Slot ID (2 bytes; default 0):");
param2 = 0;
if(2 == get_bytes_serial(tx_buffer, 2))
param2 = tx_buffer[0]*256 + tx_buffer[1];
Wl("Enter challenge (32 bytes; default 0):");
memset((void *)data1, 0, 32);
get_bytes_serial(data1, 32);
print_execute_params(SHA204_MAC,param1);
hexprint_byte_sep((uint8_t)(param2>>8));
hexprint_byte_sep((uint8_t)param2);
W("data1 ");
hexprint(data1, 32);
print_executing();
sha204.wakeup(rx_buffer);
r = sha204.mac(tx_buffer, rx_buffer, param1, param2, data1);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
print_received_from_sha(rx_buffer);
break;
case 'n': // nonce
Wl("Generate a nonce for subsequent use by other commands (Nonce command).");
Wl("Enter mode (00 to 03; default 0):");
param1 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param1 = tx_buffer[0];
Wl("Enter input value (20 or 32 bytes (dep on mode); default all 0):");
memset((void *)data1, 0, 32);
get_bytes_serial(data1, 32);
print_execute_params(SHA204_NONCE, param1);
Wl("none");
print_executing();
sha204.wakeup(rx_buffer);
r = sha204.nonce(tx_buffer,rx_buffer,param1,data1);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
print_received_from_sha(rx_buffer);
break;
case 'r': // random
Wl("Generate a random sequence.");
Wl("Enter mode (00 or 01; default 0):");
param1 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param1 = tx_buffer[0];
print_execute_params(SHA204_RANDOM,param1);
Wl("none");
print_executing();
sha204.wakeup(rx_buffer);
r = sha204.random(tx_buffer,rx_buffer,param1);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
print_received_from_sha(rx_buffer);
break;
case 'e': // read
Wl("Read from device.");
Wl("Enter zone (1 byte: 00/Config or 01/OTP or 02/Data, +0x80 to read 32 instead of 4 bytes; default 0):");
param1 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param1 = tx_buffer[0];
Wl("Enter Address (2 bytes; default 0):");
param2 = 0;
if(2 == get_bytes_serial(tx_buffer, 2))
param2 = tx_buffer[0]*256 + tx_buffer[1];
print_execute_params(SHA204_READ,param1);
hexprint_byte_sep((uint8_t)(param2>>8));
hexprint_byte((uint8_t)param2);
W("\n\r");
print_executing();
sha204.wakeup(rx_buffer);
r = sha204.read(tx_buffer, rx_buffer, param1, param2);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
print_received_from_sha(rx_buffer);
break;
case 'w': // write
Wl("Write to device.");
Wl("Enter zone (1 byte: 00/Config or 01/OTP or 02/Data,");
Wl(" +0x80 to write 32 instead of 4 bytes, +0x40 to require encryption; default 0):");
param1 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param1 = tx_buffer[0];
Wl("Enter Address (2 bytes; default 0):");
param2 = 0;
if(2 == get_bytes_serial(tx_buffer, 2))
param2 = tx_buffer[0]*256 + tx_buffer[1];
Wl("Enter data (4 or 32 bytes; default 0):");
memset((void *)data1, 0, 32);
get_bytes_serial(data1, 32);
Wl("Enter MAC to validate address and data (0 or 32 bytes; default 0):");
memset((void *)data2, 0, 32);
get_bytes_serial(data2, 32);
print_execute_params(SHA204_WRITE,param1);
hexprint_byte_sep((uint8_t)(param2>>8));
hexprint_byte_sep((uint8_t)param2);
W("data1 ");
hexprint_noln(data1, 32);
W("data2 ");
hexprint(data2, 32);
print_executing();
sha204.wakeup(rx_buffer);
r = sha204.write(tx_buffer, rx_buffer, param1, param2, data1, data2);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
print_received_from_sha(rx_buffer);
break;
case 'u': // update_extra
Wl("Update 'UserExtra' bytes (84 and 85) in the Conf zone after locking.");
Wl("Enter mode (1 byte: 00->update 84, 01->update 85; default 0):");
param1 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param1 = tx_buffer[0];
Wl("Enter new value (1 byte; default 0):");
param2 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param2 = tx_buffer[0];
print_execute_params(SHA204_UPDATE_EXTRA,param1);
hexprint_byte((uint8_t)param2);
W("\n\r");
print_executing();
sha204.wakeup(rx_buffer);
r = sha204.update_extra(tx_buffer, rx_buffer, param1, param2);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
print_received_from_sha(rx_buffer);
break;
case 'k': // wake
Wl("Test waking up.");
print_executing();
r = sha204.wakeup(rx_buffer);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
Wl("Should receive: 04 11 33 43");
print_received_from_sha(rx_buffer);
break;
case 'L': // lock
Wl("Lock a zone. This is a one time thing! Once you lock a zone, it CAN'T BE UNLOCKED. EVER!");
Wl("Enter zone (1 byte: 00/Config or 01/OTP_or_Data, +0x80 for 'force' (CRC ignored); default 0):");
param1 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param1 = tx_buffer[0];
Wl("Enter Summary / CRC-16 of the zone (2 bytes, should be 0 if 'force'; default 0):");
param2 = 0;
if(2 == get_bytes_serial(tx_buffer, 2))
param2 = tx_buffer[0]*256 + tx_buffer[1];
print_execute_params(SHA204_LOCK,param1);
hexprint_byte_sep((uint8_t)(param2>>8));
hexprint_byte((uint8_t)param2);
W("\n\r");
print_executing();
sha204.wakeup(rx_buffer);
r = sha204.lock(tx_buffer, rx_buffer, param1, param2);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
print_received_from_sha(rx_buffer);
break;
case '\r': // enter
case '?': // help
print_help();
break;
case 'I': // switch idle and sleep
W("Switching whether the ATSHA should be put to sleep or to idle mode after commands.\n\rCurrent setting: ");
if(idle) {
idle = 0;
Wl("Sleep.");
}
else {
idle = 1;
Wl("Idle.");
}
break;
default:
break;
}
}
}
}
/* Helper functions implementation */
void hexprint_byte(uint8_t b) {
uint8_t high, low;
low = b & 0xF;
high = b >> 4;
usb_serial_putchar(high+'0'+7*(high/10));
usb_serial_putchar(low+'0'+7*(low/10));
}
void hexprint_byte_sep(uint8_t b) {
hexprint_byte(b);
usb_serial_putchar(hexprint_separator);
}
void hexprint_noln(uint8_t *p, uint16_t length) {
for(uint16_t i=0; i<length; i++) {
hexprint_byte(p[i]);
if(hexprint_separator!=0)
usb_serial_putchar(hexprint_separator);
}
}
void hexprint(uint8_t *p, uint16_t length) {
hexprint_noln(p, length);
usb_serial_write_P(PSTR("\n\r"));
}
void hexprint_4bits(uint8_t b) {
usb_serial_putchar((b&0xF)+'0'+7*((b&0xF)/10));
}
void hexprint_1bit(uint8_t b) {
usb_serial_putchar((b&1)+'0');
}
void print_help(void) {
Wl("*** SHA204 playground [(c) 2014 flabbergast] ***\n\r");
Wl("Raw commands: wa[k]e [c]heckMAC [d]erive_key dev_re[v]ision [g]en_dig [h]MAC");
Wl(" [m]ac [n]once [r]andom r[e]ad [w]rite [u]date_extra");
Wl("Processed commands: [s]erial c[o]nfig_zone");
Wl("Playground config: [I]dle-or-sleep");
Wl("'?' -> this help");
Wl("Dangerous/one-time only! [L]ock\n\r");
Wl("Additional comments:");
Wl(" - Format of ATSHA204 command responses:");
Wl(" <1byte:packet_size> <msg_byte> <msg_byte> ... <1byte:crc_1> <1byte:crc_2>");
Wl(" - ATSHA204 is sent to sleep or to idle mode after every command, select via [I].");
Wl(" - The 'sent packet' info does not always match what's actually exactly sent. It's provided");
Wl(" mainly to check the entered parameters.");
Wl(" - Input, when requested, is expected in (padded) HEX format, e.g. 'AB01' for two bytes: 171 1.\n\r");
}
void print_executing(void) {
W("Executing: ");
}
void print_received_from_sha(uint8_t *rx_buffer) {
W("Received from ATSHA204: ");
hexprint(rx_buffer, rx_buffer[0]);
}
void print_execute_params(uint8_t opcode, uint8_t param1) {
W("Will run with: opcode ");
hexprint_byte_sep(opcode);
W("param1 ");
hexprint_byte_sep(param1);
W("param2 ");
}
uint8_t get_bytes_serial(uint8_t *output, uint16_t len) {
char buffer[2*MAX_BUFFER_SIZE];
uint16_t input_length;
uint16_t i;
uint8_t low, high;
input_length = usb_serial_readline(buffer, 2*len+1, false);
strupr(buffer);
for(i=0; i<input_length; i+=2) {
high = buffer[i] - '0';
if(high > 9)
high -= 7;
low = buffer[i+1] - '0';
if(low > 9)
low -= 7;
output[i/2] = (uint8_t)(low + (high << 4));
}
return (input_length/2);
}
/* Read and Interpret ATSHA204 configuration */
void process_config(uint8_t *config) {
// serial number
W("Serial number: ");
hexprint_noln(config+ADDRESS_SN03, 4);
hexprint(config+ADDRESS_SN47, 5);
// revision number
W("Revision number: ");
hexprint(config+ADDRESS_RevNum,4);
// I2C setup
if(config[ADDRESS_I2CEN]&1) {
W("I2C enabled; Address: ");
hexprint_byte(config[ADDRESS_I2CADD]>>1);
W("\n\r");
} else {
W("SingleWire (I2C disabled); TTL input level: ");
if(config[ADDRESS_I2CADD]&0b1000)
Wl("Vcc");
else
Wl("fixed");
}
// OTP mode
W("OTP mode: ");
switch(config[ADDRESS_OTPMODE]) {
case 0xAA:
Wl("read-only");
break;
case 0x55:
Wl("consumption");
break;
case 0x00:
Wl("legacy");
break;
default:
Wl("reserved value (problem!)");
break;
}
// selector mode
W("Selector: ");
if(!config[ADDRESS_SELECTOR])
Wl("can be updated with UpdateExtra.");
else
Wl("can be updated only if it is 0.");
// User extra
W("User Extra byte: ");
hexprint_byte(config[84]);
W("\n\r");
// Selector
W("Selector byte: ");
hexprint_byte(config[85]);
W("\n\r");
// Lock data
W("Data and OTP zones are ");
if(config[86]==0x55)
Wl("unlocked.");
else
Wl("locked!");
// Lock config
W("Config zone is ");
if(config[87]==0x55)
Wl("unlocked.");
else
Wl("locked!");
// Slots
uint8_t i;
Wl("Configurations of slots: ");
for(i=0; i<16; i++) {
uint8_t addr = 20+(2*i);
W("Slot:");
hexprint_4bits(i);
W(" ReadKey:");
hexprint_4bits(config[addr]); // getting the 2 config bytes LSB first
W(" CheckOnly:");
hexprint_1bit(config[addr]>>4);
W(" SingleUse:");
hexprint_1bit(config[addr]>>5);
W(" EncryptRead:");
hexprint_1bit(config[addr]>>6);
W(" IsSecret:");
hexprint_1bit(config[addr]>>7);
W("\n\r WriteKey:");
hexprint_4bits(config[addr+1]);
W(" WriteConfig:");
hexprint_4bits(config[addr+1]>>4);
W("\n\r");
if(i<8) { // slots 0-7 have extra data
W(" UseFlag:");
hexprint_byte(config[52+(2*i)]);
W(" UpdateCount:");
hexprint_byte(config[53+(2*i)]);
W("\n\r");
}
if(i==15) { // slot15 has limit on usage
W(" LastKeyUse: ");
hexprint(config+68, 16);
}
}
}
uint8_t receive_serial_binary_transaction(uint8_t *buffer, uint8_t len) {
delay(100); // give the transmitting side the chance to push the rest through
if( usb_serial_available() == 0 ) {
return BINARY_TRANSACTION_RECEIVE_ERROR;
}
uint8_t n_bytes = usb_serial_getchar();
if( n_bytes >= len ) {
return BINARY_TRANSACTION_RECEIVE_ERROR;
}
buffer[0] = n_bytes;
for(uint8_t i=1; i<=n_bytes; i++) {
if( usb_serial_available() == 0 ) {
return BINARY_TRANSACTION_RECEIVE_ERROR;
}
buffer[i] = (uint8_t)usb_serial_getchar();
delay(3);
}
return BINARY_TRANSACTION_OK;
}
uint8_t binary_mode_transaction(uint8_t *data, uint8_t rxsize, uint8_t *rx_buffer, SHA204SWI *sha204) {
uint8_t i = 0;
uint8_t len;
uint8_t idle;
uint8_t opcode;
uint8_t param1;
uint16_t param2;
uint8_t datalen1=0;
uint8_t data1[32];
uint8_t datalen2=0;
uint8_t data2[32];
uint8_t datalen3=0;
uint8_t data3[14];
// process the input packet
len = data[0];
idle = data[1];
if(len<5)
return BINARY_TRANSACTION_PARAM_ERROR;
opcode = data[2];
param1 = data[3];
param2 = data[4] + 256*data[5];
if(len>5) {
if((datalen1=data[6]) > 32)
return BINARY_TRANSACTION_PARAM_ERROR;
for(i=0; i<datalen1; i++)
data1[i] = data[7+i];
}
if(len>6+datalen1) {
if((datalen2=data[7+datalen1]) > 32)
return BINARY_TRANSACTION_PARAM_ERROR;
for(i=0; i<datalen2; i++)
data2[i] = data[8+datalen1+i];
}
if(len>7+datalen1+datalen2) {
if((datalen3=data[8+datalen1+datalen2]) > 13)
return BINARY_TRANSACTION_PARAM_ERROR;
for(i=0; i<datalen3; i++)
data3[i] = data[9+datalen1+datalen2+i];
}
// run the transaction
sha204->wakeup(data);
i = sha204->execute(opcode, param1, param2,
datalen1, data1, datalen2, data2, datalen3, data3,
len, data, rxsize, rx_buffer);
if(idle)
sha204->idle();
else
sha204->sleep();
if(i != SHA204_SUCCESS)
return BINARY_TRANSACTION_EXECUTE_ERROR;
return BINARY_TRANSACTION_OK;
}
/* Return code stuff */
const char retcode_success[] PROGMEM = "Success.";
const char retcode_parse_error[] PROGMEM = "Parse error.";
const char retcode_cmd_fail[] PROGMEM = "Command execution error.";
const char retcode_status_crc[] PROGMEM = "CRC error.";
const char retcode_status_unknown[] PROGMEM = "Unknown error.";
const char retcode_func_fail[] PROGMEM = "Couldn't execute due to wrong condition/state.";
const char retcode_gen_fail[] PROGMEM = "Unspecified error.";
const char retcode_bad_param[] PROGMEM = "Bad parameter.";
const char retcode_invalid_id[] PROGMEM = "Invalid device ID.";
const char retcode_invalid_size[] PROGMEM = "Out of range error.";
const char retcode_bad_crc[] PROGMEM = "Bad CRC received.";
const char retcode_rx_fail[] PROGMEM = "Timeout while waiting for a response (got >0 bytes).";
const char retcode_rx_no_response[] PROGMEM = "Timeout (not an error while busy).";
const char retcode_resync_with_wakeup[] PROGMEM = "Resync OK after wakeup.";
const char retcode_comm_fail[] PROGMEM = "Communication failed";
const char retcode_timeout[] PROGMEM = "Timeout while waiting for a response (got no bytes).";
const char retcode_unknown[] PROGMEM = "Unknown error message.";
void print_return_code(uint8_t code) {
const char *p PROGMEM;
switch(code) {
case(SHA204_SUCCESS):
p = retcode_success;
break;
case(SHA204_PARSE_ERROR):
p = retcode_parse_error;
break;
case(SHA204_CMD_FAIL):
p = retcode_cmd_fail;
break;
case(SHA204_STATUS_CRC):
p = retcode_status_crc;
break;
case(SHA204_STATUS_UNKNOWN):
p = retcode_status_unknown;
break;
case(SHA204_FUNC_FAIL):
p = retcode_func_fail;
break;
case(SHA204_GEN_FAIL):
p = retcode_gen_fail;
break;
case(SHA204_BAD_PARAM):
p = retcode_bad_param;
break;
case(SHA204_INVALID_ID):
p = retcode_invalid_id;
break;
case(SHA204_INVALID_SIZE):
p = retcode_invalid_size;
break;
case(SHA204_BAD_CRC):
p = retcode_bad_crc;
break;
case(SHA204_RX_FAIL):
p = retcode_rx_fail;
break;
case(SHA204_RX_NO_RESPONSE):
p = retcode_rx_no_response;
break;
case(SHA204_RESYNC_WITH_WAKEUP):
p = retcode_resync_with_wakeup;
break;
case(SHA204_COMM_FAIL):
p = retcode_comm_fail;
break;
case(SHA204_TIMEOUT):
p = retcode_timeout;
break;
default:
p = retcode_unknown;
break;
}
usb_serial_write_P(p);
Serial.write("\n\r");
}
// implementation of "LufaLayer"
uint16_t usb_serial_available(void) {
return Serial.available();
}
int16_t usb_serial_getchar(void) {
return Serial.read();
}
void usb_serial_putchar(uint8_t ch) {
Serial.write(ch);
}
void usb_serial_write_P(PGM_P data) {
for (uint8_t c; (c = pgm_read_byte(data)); data++) Serial.write(c);
}
void usb_serial_write(const char* const buffer) {
Serial.write(buffer);
}
uint16_t usb_serial_readline(char *buffer, const uint16_t buffer_size, const bool obscure_input)
{
while(Serial.available() == 0) ;
Serial.setTimeout(100);
uint16_t r = Serial.readBytes(buffer, buffer_size);
Serial.setTimeout(1000);
return(r);
}

57
avr/Board/Board.h Normal file
View file

@ -0,0 +1,57 @@
/*
* Board.h
* (c) 2014 flabbergast
* Custom Board definitions for use with LUFA, for AVR stick.
*
* Based on a template from LUFA library (license below).
*/
/*
LUFA Library
Copyright (C) Dean Camera, 2014.
dean [at] fourwalledcubicle [dot] com
www.lufa-lib.org
*/
/*
Copyright 2014 Dean Camera (dean [at] fourwalledcubicle [dot] com)
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name of the author not be used in
advertising or publicity pertaining to distribution of the
software without specific, written prior permission.
The author disclaims all warranties with regard to this
software, including all implied warranties of merchantability
and fitness. In no event shall the author be liable for any
special, indirect or consequential damages or any damages
whatsoever resulting from loss of use, data or profits, whether
in an action of contract, negligence or other tortious action,
arising out of or in connection with the use or performance of
this software.
*/
#ifndef __BOARD_USER_H__
#define __BOARD_USER_H__
/* Includes: */
// TODO: Add any required includes here
/* Enable C linkage for C++ Compilers: */
#if defined(__cplusplus)
extern "C" {
#endif
/* Preprocessor Checks: */
#if !defined(__INCLUDE_FROM_BOARD_H)
#error Do not include this file directly. Include LUFA/Drivers/Board/Board.h instead.
#endif
/* Public Interface - May be used in end-application: */
/* Macros: */
#define BOARD_HAS_BUTTONS
// #define BOARD_HAS_DATAFLASH
// #define BOARD_HAS_JOYSTICK
// #define BOARD_HAS_LEDS
/* Disable C linkage for C++ Compilers: */
#if defined(__cplusplus)
}
#endif
#endif

74
avr/Board/Buttons.h Normal file
View file

@ -0,0 +1,74 @@
/*
* Buttons.h
* (c) 2014 flabbergast
* Custom Buttons definitions for use with LUFA, for AVR stick.
*
* Based on a template from LUFA library (license below).
*/
/*
LUFA Library
Copyright (C) Dean Camera, 2014.
dean [at] fourwalledcubicle [dot] com
www.lufa-lib.org
*/
/*
Copyright 2014 Dean Camera (dean [at] fourwalledcubicle [dot] com)
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name of the author not be used in
advertising or publicity pertaining to distribution of the
software without specific, written prior permission.
The author disclaims all warranties with regard to this
software, including all implied warranties of merchantability
and fitness. In no event shall the author be liable for any
special, indirect or consequential damages or any damages
whatsoever resulting from loss of use, data or profits, whether
in an action of contract, negligence or other tortious action,
arising out of or in connection with the use or performance of
this software.
*/
#ifndef __BUTTONS_USER_H__
#define __BUTTONS_USER_H__
/* Includes: */
// TODO: Add any required includes here
#include <avr/io.h>
/* Enable C linkage for C++ Compilers: */
#if defined(__cplusplus)
extern "C" {
#endif
/* Preprocessor Checks: */
#if !defined(__INCLUDE_FROM_BUTTONS_H)
#error Do not include this file directly. Include LUFA/Drivers/Board/Buttons.h instead.
#endif
/* Public Interface - May be used in end-application: */
/* Macros: */
#define BUTTONS_BUTTON1 _BV(7)
/* Inline Functions: */
#if !defined(__DOXYGEN__)
static inline void Buttons_Init(void)
{
PORTF.DIRCLR = BUTTONS_BUTTON1;
PORTF.PIN7CTRL = PORT_OPC_PULLUP_gc; // pull-up on pin 7
}
static inline void Buttons_Disable(void)
{
PORTF.PIN7CTRL = 0; // don't know what's the default state of pull-up/down?
PORTF.DIRCLR = BUTTONS_BUTTON1;
}
static inline uint8_t Buttons_GetStatus(void) ATTR_WARN_UNUSED_RESULT;
static inline uint8_t Buttons_GetStatus(void)
{
// TODO: Return current button status here, debounced if required
return ((PORTF.IN & BUTTONS_BUTTON1) ^ BUTTONS_BUTTON1);
}
#endif
/* Disable C linkage for C++ Compilers: */
#if defined(__cplusplus)
}
#endif
#endif

99
avr/Board/LEDs.h Normal file
View file

@ -0,0 +1,99 @@
/*
* LEDs.h
* (c) 2014 flabbergast
* Custom LEDs definitions for use with LUFA, for AVR stick.
*
* Based on a template from LUFA library (license below).
*/
/*
LUFA Library
Copyright (C) Dean Camera, 2014.
dean [at] fourwalledcubicle [dot] com
www.lufa-lib.org
*/
/*
Copyright 2014 Dean Camera (dean [at] fourwalledcubicle [dot] com)
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name of the author not be used in
advertising or publicity pertaining to distribution of the
software without specific, written prior permission.
The author disclaims all warranties with regard to this
software, including all implied warranties of merchantability
and fitness. In no event shall the author be liable for any
special, indirect or consequential damages or any damages
whatsoever resulting from loss of use, data or profits, whether
in an action of contract, negligence or other tortious action,
arising out of or in connection with the use or performance of
this software.
*/
#ifndef __LEDS_USER_H__
#define __LEDS_USER_H__
/* Includes: */
#include <avr/io.h>
/* Enable C linkage for C++ Compilers: */
#if defined(__cplusplus)
extern "C" {
#endif
/* Preprocessor Checks: */
#if !defined(__INCLUDE_FROM_LEDS_H)
#error Do not include this file directly. Include LUFA/Drivers/Board/LEDS.h instead.
#endif
/* Public Interface - May be used in end-application: */
/* Macros: */
#define LEDS_LED1 (1 << 0)
#define LEDS_LED2 (1 << 1)
#define LEDS_LED3 (1 << 2)
#define LEDS_LED4 (1 << 3)
#define LEDS_ALL_LEDS (LEDS_LED1 | LEDS_LED2 | LEDS_LED3 | LEDS_LED4)
#define LEDS_NO_LEDS 0
/* Inline Functions: */
#if !defined(__DOXYGEN__)
static inline void LEDs_Init(void)
{
PORTE.DIRSET = LEDS_ALL_LEDS;
PORTE.OUTCLR = LEDS_ALL_LEDS;
}
static inline void LEDs_Disable(void)
{
PORTE.DIRCLR = LEDS_ALL_LEDS;
PORTE.OUTCLR = LEDS_ALL_LEDS;
}
static inline void LEDs_TurnOnLEDs(const uint8_t LEDMask)
{
PORTE.OUTSET = LEDMask & LEDS_ALL_LEDS;
}
static inline void LEDs_TurnOffLEDs(const uint8_t LEDMask)
{
PORTE.OUTCLR = LEDMask & LEDS_ALL_LEDS;
}
static inline void LEDs_SetAllLEDs(const uint8_t LEDMask)
{
PORTE.OUTCLR = LEDS_ALL_LEDS;
PORTE.OUTSET = LEDMask & LEDS_ALL_LEDS;
}
static inline void LEDs_ChangeLEDs(const uint8_t LEDMask, const uint8_t ActiveMask)
{
PORTE.OUTCLR = (LEDMask & LEDS_ALL_LEDS);
PORTE.OUTSET = (ActiveMask & LEDS_ALL_LEDS);
}
static inline void LEDs_ToggleLEDs(const uint8_t LEDMask)
{
PORTE.OUTTGL = (LEDMask & LEDS_ALL_LEDS);
}
static inline uint8_t LEDs_GetLEDs(void) ATTR_WARN_UNUSED_RESULT;
static inline uint8_t LEDs_GetLEDs(void)
{
return ((PORTE.OUT & LEDS_ALL_LEDS));
}
#endif
/* Disable C linkage for C++ Compilers: */
#if defined(__cplusplus)
}
#endif
#endif

126
avr/Config/LUFAConfig.h Normal file
View file

@ -0,0 +1,126 @@
/*
LUFA Library
Copyright (C) Dean Camera, 2014.
dean [at] fourwalledcubicle [dot] com
www.lufa-lib.org
*/
/*
Copyright 2014 Dean Camera (dean [at] fourwalledcubicle [dot] com)
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name of the author not be used in
advertising or publicity pertaining to distribution of the
software without specific, written prior permission.
The author disclaims all warranties with regard to this
software, including all implied warranties of merchantability
and fitness. In no event shall the author be liable for any
special, indirect or consequential damages or any damages
whatsoever resulting from loss of use, data or profits, whether
in an action of contract, negligence or other tortious action,
arising out of or in connection with the use or performance of
this software.
*/
/** \file
* \brief LUFA Library Configuration Header File
*
* This header file is used to configure LUFA's compile time options,
* as an alternative to the compile time constants supplied through
* a makefile.
*
* For information on what each token does, refer to the LUFA
* manual section "Summary of Compile Tokens".
*/
#ifndef _LUFA_CONFIG_H_
#define _LUFA_CONFIG_H_
#if (ARCH == ARCH_AVR8)
/* Non-USB Related Configuration Tokens: */
// #define DISABLE_TERMINAL_CODES
/* USB Class Driver Related Tokens: */
// #define HID_HOST_BOOT_PROTOCOL_ONLY
// #define HID_STATETABLE_STACK_DEPTH {Insert Value Here}
// #define HID_USAGE_STACK_DEPTH {Insert Value Here}
// #define HID_MAX_COLLECTIONS {Insert Value Here}
// #define HID_MAX_REPORTITEMS {Insert Value Here}
// #define HID_MAX_REPORT_IDS {Insert Value Here}
// #define NO_CLASS_DRIVER_AUTOFLUSH
/* General USB Driver Related Tokens: */
// #define ORDERED_EP_CONFIG
#define USE_STATIC_OPTIONS (USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL)
#define USB_DEVICE_ONLY
// #define USB_HOST_ONLY
// #define USB_STREAM_TIMEOUT_MS {Insert Value Here}
// #define NO_LIMITED_CONTROLLER_CONNECT
// #define NO_SOF_EVENTS
/* USB Device Mode Driver Related Tokens: */
// #define USE_RAM_DESCRIPTORS
#define USE_FLASH_DESCRIPTORS
// #define USE_EEPROM_DESCRIPTORS
// #define NO_INTERNAL_SERIAL
#define FIXED_CONTROL_ENDPOINT_SIZE 8
// #define DEVICE_STATE_AS_GPIOR {Insert Value Here}
#define FIXED_NUM_CONFIGURATIONS 1
// #define CONTROL_ONLY_DEVICE
#define INTERRUPT_CONTROL_ENDPOINT
// #define NO_DEVICE_REMOTE_WAKEUP
// #define NO_DEVICE_SELF_POWER
/* USB Host Mode Driver Related Tokens: */
// #define HOST_STATE_AS_GPIOR {Insert Value Here}
// #define USB_HOST_TIMEOUT_MS {Insert Value Here}
// #define HOST_DEVICE_SETTLE_DELAY_MS {Insert Value Here}
// #define NO_AUTO_VBUS_MANAGEMENT
// #define INVERTED_VBUS_ENABLE_LINE
#elif (ARCH == ARCH_XMEGA)
/* Non-USB Related Configuration Tokens: */
// #define DISABLE_TERMINAL_CODES
/* USB Class Driver Related Tokens: */
// #define HID_HOST_BOOT_PROTOCOL_ONLY
// #define HID_STATETABLE_STACK_DEPTH {Insert Value Here}
// #define HID_USAGE_STACK_DEPTH {Insert Value Here}
// #define HID_MAX_COLLECTIONS {Insert Value Here}
// #define HID_MAX_REPORTITEMS {Insert Value Here}
// #define HID_MAX_REPORT_IDS {Insert Value Here}
// #define NO_CLASS_DRIVER_AUTOFLUSH
/* General USB Driver Related Tokens: */
#define USE_STATIC_OPTIONS (USB_DEVICE_OPT_FULLSPEED | USB_OPT_RC32MCLKSRC | USB_OPT_BUSEVENT_PRIHIGH)
// #define USB_STREAM_TIMEOUT_MS {Insert Value Here}
// #define NO_LIMITED_CONTROLLER_CONNECT
// #define NO_SOF_EVENTS
/* USB Device Mode Driver Related Tokens: */
// #define USE_RAM_DESCRIPTORS
#define USE_FLASH_DESCRIPTORS
// #define USE_EEPROM_DESCRIPTORS
// #define NO_INTERNAL_SERIAL
#define FIXED_CONTROL_ENDPOINT_SIZE 8
// #define DEVICE_STATE_AS_GPIOR {Insert Value Here}
#define FIXED_NUM_CONFIGURATIONS 1
// #define CONTROL_ONLY_DEVICE
#define MAX_ENDPOINT_INDEX 4
// #define NO_DEVICE_REMOTE_WAKEUP
// #define NO_DEVICE_SELF_POWER
#else
#error Unsupported architecture for this LUFA configuration file.
#endif
#endif

325
avr/Descriptors.c Normal file
View file

@ -0,0 +1,325 @@
/*
* Descriptors.c
* (c) 2014 flabbergast
* USB Descriptors.
* Most code comes from a LUFA library Demo (license below).
*/
/*
LUFA Library
Copyright (C) Dean Camera, 2014.
dean [at] fourwalledcubicle [dot] com
www.lufa-lib.org
*/
/*
Copyright 2014 Dean Camera (dean [at] fourwalledcubicle [dot] com)
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name of the author not be used in
advertising or publicity pertaining to distribution of the
software without specific, written prior permission.
The author disclaims all warranties with regard to this
software, including all implied warranties of merchantability
and fitness. In no event shall the author be liable for any
special, indirect or consequential damages or any damages
whatsoever resulting from loss of use, data or profits, whether
in an action of contract, negligence or other tortious action,
arising out of or in connection with the use or performance of
this software.
*/
/** \file
*
* USB Device Descriptors, for library use when in USB device mode. Descriptors are special
* computer-readable structures which the host requests upon device enumeration, to determine
* the device's capabilities and functions.
*/
#include "Descriptors.h"
/** HID class report descriptor. This is a special descriptor constructed with values from the
* USBIF HID class specification to describe the reports and capabilities of the HID device. This
* descriptor is parsed by the host and its contents used to determine what data (and in what encoding)
* the device will send, and what it may be sent back from the host. Refer to the HID specification for
* more details on HID report descriptors.
*/
const USB_Descriptor_HIDReport_Datatype_t PROGMEM KeyboardReport[] =
{
/* Use the HID class driver's standard Keyboard report.
* Max simultaneous keys: 6
*/
HID_DESCRIPTOR_KEYBOARD(6)
};
/** Device descriptor structure. This descriptor, located in FLASH memory, describes the overall
* device characteristics, including the supported USB version, control endpoint size and the
* number of device configurations. The descriptor is read out by the USB host when the enumeration
* process begins.
*/
const USB_Descriptor_Device_t PROGMEM DeviceDescriptor =
{
.Header = {.Size = sizeof(USB_Descriptor_Device_t), .Type = DTYPE_Device},
.USBSpecification = VERSION_BCD(1,1,0),
.Class = USB_CSCP_IADDeviceClass,
.SubClass = USB_CSCP_IADDeviceSubclass,
.Protocol = USB_CSCP_IADDeviceProtocol,
.Endpoint0Size = FIXED_CONTROL_ENDPOINT_SIZE,
.VendorID = 0x03EB,
.ProductID = 0x206F,
.ReleaseNumber = VERSION_BCD(0,0,1),
.ManufacturerStrIndex = STRING_ID_Manufacturer,
.ProductStrIndex = STRING_ID_Product,
.SerialNumStrIndex = USE_INTERNAL_SERIAL,
.NumberOfConfigurations = FIXED_NUM_CONFIGURATIONS
};
/** Configuration descriptor structure. This descriptor, located in FLASH memory, describes the usage
* of the device in one of its supported configurations, including information about any device interfaces
* and endpoints. The descriptor is read out by the USB host during the enumeration process when selecting
* a configuration so that the host may correctly communicate with the USB device.
*/
const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor =
{
.Config =
{
.Header = {.Size = sizeof(USB_Descriptor_Configuration_Header_t), .Type = DTYPE_Configuration},
.TotalConfigurationSize = sizeof(USB_Descriptor_Configuration_t),
.TotalInterfaces = 3,
.ConfigurationNumber = 1,
.ConfigurationStrIndex = NO_DESCRIPTOR,
.ConfigAttributes = (USB_CONFIG_ATTR_RESERVED | USB_CONFIG_ATTR_SELFPOWERED),
.MaxPowerConsumption = USB_CONFIG_POWER_MA(100)
},
.CDC_IAD =
{
.Header = {.Size = sizeof(USB_Descriptor_Interface_Association_t), .Type = DTYPE_InterfaceAssociation},
.FirstInterfaceIndex = INTERFACE_ID_CDC_CCI,
.TotalInterfaces = 2,
.Class = CDC_CSCP_CDCClass,
.SubClass = CDC_CSCP_ACMSubclass,
.Protocol = CDC_CSCP_ATCommandProtocol,
.IADStrIndex = NO_DESCRIPTOR
},
.CDC_CCI_Interface =
{
.Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface},
.InterfaceNumber = INTERFACE_ID_CDC_CCI,
.AlternateSetting = 0,
.TotalEndpoints = 1,
.Class = CDC_CSCP_CDCClass,
.SubClass = CDC_CSCP_ACMSubclass,
.Protocol = CDC_CSCP_ATCommandProtocol,
.InterfaceStrIndex = NO_DESCRIPTOR
},
.CDC_Functional_Header =
{
.Header = {.Size = sizeof(USB_CDC_Descriptor_FunctionalHeader_t), .Type = DTYPE_CSInterface},
.Subtype = CDC_DSUBTYPE_CSInterface_Header,
.CDCSpecification = VERSION_BCD(1,1,0),
},
.CDC_Functional_ACM =
{
.Header = {.Size = sizeof(USB_CDC_Descriptor_FunctionalACM_t), .Type = DTYPE_CSInterface},
.Subtype = CDC_DSUBTYPE_CSInterface_ACM,
.Capabilities = 0x06,
},
.CDC_Functional_Union =
{
.Header = {.Size = sizeof(USB_CDC_Descriptor_FunctionalUnion_t), .Type = DTYPE_CSInterface},
.Subtype = CDC_DSUBTYPE_CSInterface_Union,
.MasterInterfaceNumber = INTERFACE_ID_CDC_CCI,
.SlaveInterfaceNumber = INTERFACE_ID_CDC_DCI,
},
.CDC_NotificationEndpoint =
{
.Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint},
.EndpointAddress = CDC_NOTIFICATION_EPADDR,
.Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
.EndpointSize = CDC_NOTIFICATION_EPSIZE,
.PollingIntervalMS = 0xFF
},
.CDC_DCI_Interface =
{
.Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface},
.InterfaceNumber = INTERFACE_ID_CDC_DCI,
.AlternateSetting = 0,
.TotalEndpoints = 2,
.Class = CDC_CSCP_CDCDataClass,
.SubClass = CDC_CSCP_NoDataSubclass,
.Protocol = CDC_CSCP_NoDataProtocol,
.InterfaceStrIndex = NO_DESCRIPTOR
},
.CDC_DataOutEndpoint =
{
.Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint},
.EndpointAddress = CDC_RX_EPADDR,
.Attributes = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
.EndpointSize = CDC_TXRX_EPSIZE,
.PollingIntervalMS = 0x05
},
.CDC_DataInEndpoint =
{
.Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint},
.EndpointAddress = CDC_TX_EPADDR,
.Attributes = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
.EndpointSize = CDC_TXRX_EPSIZE,
.PollingIntervalMS = 0x05
},
.HID_Interface =
{
.Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface},
.InterfaceNumber = INTERFACE_ID_Keyboard,
.AlternateSetting = 0,
.TotalEndpoints = 1,
.Class = HID_CSCP_HIDClass,
.SubClass = HID_CSCP_BootSubclass,
.Protocol = HID_CSCP_KeyboardBootProtocol,
.InterfaceStrIndex = NO_DESCRIPTOR
},
.HID_KeyboardHID =
{
.Header = {.Size = sizeof(USB_HID_Descriptor_HID_t), .Type = HID_DTYPE_HID},
.HIDSpec = VERSION_BCD(1,1,1),
.CountryCode = 0x00,
.TotalReportDescriptors = 1,
.HIDReportType = HID_DTYPE_Report,
.HIDReportLength = sizeof(KeyboardReport)
},
.HID_ReportINEndpoint =
{
.Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint},
.EndpointAddress = KEYBOARD_EPADDR,
.Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
.EndpointSize = KEYBOARD_EPSIZE,
.PollingIntervalMS = 0x05
}
};
/** Language descriptor structure. This descriptor, located in FLASH memory, is returned when the host requests
* the string descriptor with index 0 (the first index). It is actually an array of 16-bit integers, which indicate
* via the language ID table available at USB.org what languages the device supports for its string descriptors.
*/
const USB_Descriptor_String_t PROGMEM LanguageString = USB_STRING_DESCRIPTOR_ARRAY(LANGUAGE_ID_ENG);
/** Manufacturer descriptor string. This is a Unicode string containing the manufacturer's details in human readable
* form, and is read out upon request by the host when the appropriate string ID is requested, listed in the Device
* Descriptor.
*/
const USB_Descriptor_String_t PROGMEM ManufacturerString = USB_STRING_DESCRIPTOR(L"flabbergast");
/** Product descriptor string. This is a Unicode string containing the product's details in human readable form,
* and is read out upon request by the host when the appropriate string ID is requested, listed in the Device
* Descriptor.
*/
const USB_Descriptor_String_t PROGMEM ProductString = USB_STRING_DESCRIPTOR(L"serial/keyboard AVR stick dev demo");
/** This function is called by the library when in device mode, and must be overridden (see library "USB Descriptors"
* documentation) by the application code so that the address and size of a requested descriptor can be given
* to the USB library. When the device receives a Get Descriptor request on the control endpoint, this function
* is called so that the descriptor details can be passed back and the appropriate descriptor sent back to the
* USB host.
*/
uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue,
const uint8_t wIndex,
const void** const DescriptorAddress)
{
const uint8_t DescriptorType = (wValue >> 8);
const uint8_t DescriptorNumber = (wValue & 0xFF);
const void* Address = NULL;
uint16_t Size = NO_DESCRIPTOR;
switch (DescriptorType)
{
case DTYPE_Device:
Address = &DeviceDescriptor;
Size = sizeof(USB_Descriptor_Device_t);
break;
case DTYPE_Configuration:
Address = &ConfigurationDescriptor;
Size = sizeof(USB_Descriptor_Configuration_t);
break;
case DTYPE_String:
switch (DescriptorNumber)
{
case STRING_ID_Language:
Address = &LanguageString;
Size = pgm_read_byte(&LanguageString.Header.Size);
break;
case STRING_ID_Manufacturer:
Address = &ManufacturerString;
Size = pgm_read_byte(&ManufacturerString.Header.Size);
break;
case STRING_ID_Product:
Address = &ProductString;
Size = pgm_read_byte(&ProductString.Header.Size);
break;
}
break;
case HID_DTYPE_HID:
Address = &ConfigurationDescriptor.HID_KeyboardHID;
Size = sizeof(USB_HID_Descriptor_HID_t);
break;
case HID_DTYPE_Report:
Address = &KeyboardReport;
Size = sizeof(KeyboardReport);
break;
}
*DescriptorAddress = Address;
return Size;
}

130
avr/Descriptors.h Normal file
View file

@ -0,0 +1,130 @@
/*
* Descriptors.h
* (c) 2014 flabbergast
* USB Descriptors.
* Most code comes from a LUFA library Demo (license below).
*/
/*
LUFA Library
Copyright (C) Dean Camera, 2014.
dean [at] fourwalledcubicle [dot] com
www.lufa-lib.org
*/
/*
Copyright 2014 Dean Camera (dean [at] fourwalledcubicle [dot] com)
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name of the author not be used in
advertising or publicity pertaining to distribution of the
software without specific, written prior permission.
The author disclaims all warranties with regard to this
software, including all implied warranties of merchantability
and fitness. In no event shall the author be liable for any
special, indirect or consequential damages or any damages
whatsoever resulting from loss of use, data or profits, whether
in an action of contract, negligence or other tortious action,
arising out of or in connection with the use or performance of
this software.
*/
/** \file
*
* Header file for Descriptors.c.
*/
#ifndef _DESCRIPTORS_H_
#define _DESCRIPTORS_H_
/* Includes: */
#include <avr/pgmspace.h>
#include <LUFA/Drivers/USB/USB.h>
/* Macros: */
/** Endpoint address of the CDC device-to-host notification IN endpoint. */
#define CDC_NOTIFICATION_EPADDR (ENDPOINT_DIR_IN | 2)
/** Endpoint address of the CDC device-to-host data IN endpoint. */
#define CDC_TX_EPADDR (ENDPOINT_DIR_IN | 3)
/** Endpoint address of the CDC host-to-device data OUT endpoint. */
#define CDC_RX_EPADDR (ENDPOINT_DIR_OUT | 4)
/** Size in bytes of the CDC device-to-host notification IN endpoint. */
#define CDC_NOTIFICATION_EPSIZE 8
/** Size in bytes of the CDC data IN and OUT endpoints. */
#define CDC_TXRX_EPSIZE 16
/** Endpoint address of the Keyboard HID reporting IN endpoint. */
#define KEYBOARD_EPADDR (ENDPOINT_DIR_IN | 1)
/** Size in bytes of the Keyboard HID reporting IN endpoint. */
#define KEYBOARD_EPSIZE 8
/* Type Defines: */
/** Type define for the device configuration descriptor structure. This must be defined in the
* application code, as the configuration descriptor contains several sub-descriptors which
* vary between devices, and which describe the device's usage to the host.
*/
typedef struct
{
USB_Descriptor_Configuration_Header_t Config;
// CDC Control Interface
USB_Descriptor_Interface_Association_t CDC_IAD;
USB_Descriptor_Interface_t CDC_CCI_Interface;
USB_CDC_Descriptor_FunctionalHeader_t CDC_Functional_Header;
USB_CDC_Descriptor_FunctionalACM_t CDC_Functional_ACM;
USB_CDC_Descriptor_FunctionalUnion_t CDC_Functional_Union;
USB_Descriptor_Endpoint_t CDC_NotificationEndpoint;
// CDC Data Interface
USB_Descriptor_Interface_t CDC_DCI_Interface;
USB_Descriptor_Endpoint_t CDC_DataOutEndpoint;
USB_Descriptor_Endpoint_t CDC_DataInEndpoint;
// Keyboard HID Interface
USB_Descriptor_Interface_t HID_Interface;
USB_HID_Descriptor_HID_t HID_KeyboardHID;
USB_Descriptor_Endpoint_t HID_ReportINEndpoint;
} USB_Descriptor_Configuration_t;
/** Enum for the device interface descriptor IDs within the device. Each interface descriptor
* should have a unique ID index associated with it, which can be used to refer to the
* interface from other descriptors.
*/
enum InterfaceDescriptors_t
{
INTERFACE_ID_CDC_CCI = 0, /**< CDC CCI interface descriptor ID */
INTERFACE_ID_CDC_DCI = 1, /**< CDC DCI interface descriptor ID */
INTERFACE_ID_Keyboard = 2, /**< Keyboard interface descriptor ID */
};
/** Enum for the device string descriptor IDs within the device. Each string descriptor should
* have a unique ID index associated with it, which can be used to refer to the string from
* other descriptors.
*/
enum StringDescriptors_t
{
STRING_ID_Language = 0, /**< Supported Languages string descriptor ID (must be zero) */
STRING_ID_Manufacturer = 1, /**< Manufacturer string ID */
STRING_ID_Product = 2, /**< Product string ID */
};
/* Function Prototypes: */
uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue,
const uint8_t wIndex,
const void** const DescriptorAddress)
ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3);
#endif

414
avr/LufaLayer.c Normal file
View file

@ -0,0 +1,414 @@
/*
* LufaLayer.c
* (c) 2014 flabbergast
* USB and Board layer: provides "easy-to-use" functions for
* the main program code.
*
* Portions of code come from a LUFA library Demo (license below).
*/
/*
LUFA Library
Copyright (C) Dean Camera, 2014.
dean [at] fourwalledcubicle [dot] com
www.lufa-lib.org
*/
/*
Copyright 2014 Dean Camera (dean [at] fourwalledcubicle [dot] com)
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name of the author not be used in
advertising or publicity pertaining to distribution of the
software without specific, written prior permission.
The author disclaims all warranties with regard to this
software, including all implied warranties of merchantability
and fitness. In no event shall the author be liable for any
special, indirect or consequential damages or any damages
whatsoever resulting from loss of use, data or profits, whether
in an action of contract, negligence or other tortious action,
arising out of or in connection with the use or performance of
this software.
*/
#include "LufaLayer.h"
/* Includes: */
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/power.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <string.h>
#include "Descriptors.h"
#include "Timer.h"
#include <LUFA/Drivers/Board/LEDs.h>
#include <LUFA/Drivers/Board/Buttons.h>
#include <LUFA/Drivers/USB/USB.h>
#include <LUFA/Platform/Platform.h>
/** LUFA CDC Class driver interface configuration and state information. This structure is
* passed to all CDC Class driver functions, so that multiple instances of the same class
* within a device can be differentiated from one another.
*/
USB_ClassInfo_CDC_Device_t VirtualSerial_CDC_Interface =
{
.Config =
{
.ControlInterfaceNumber = INTERFACE_ID_CDC_CCI,
.DataINEndpoint =
{
.Address = CDC_TX_EPADDR,
.Size = CDC_TXRX_EPSIZE,
.Banks = 1,
},
.DataOUTEndpoint =
{
.Address = CDC_RX_EPADDR,
.Size = CDC_TXRX_EPSIZE,
.Banks = 1,
},
.NotificationEndpoint =
{
.Address = CDC_NOTIFICATION_EPADDR,
.Size = CDC_NOTIFICATION_EPSIZE,
.Banks = 1,
},
},
};
/** Buffer to hold the previously generated Keyboard HID report, for comparison purposes inside the HID class driver. */
static uint8_t PrevKeyboardHIDReportBuffer[sizeof(USB_KeyboardReport_Data_t)];
/** LUFA HID Class driver interface configuration and state information. This structure is
* passed to all HID Class driver functions, so that multiple instances of the same class
* within a device can be differentiated from one another.
*/
USB_ClassInfo_HID_Device_t Keyboard_HID_Interface =
{
.Config =
{
.InterfaceNumber = INTERFACE_ID_Keyboard,
.ReportINEndpoint =
{
.Address = KEYBOARD_EPADDR,
.Size = KEYBOARD_EPSIZE,
.Banks = 1,
},
.PrevReportINBuffer = PrevKeyboardHIDReportBuffer,
.PrevReportINBufferSize = sizeof(PrevKeyboardHIDReportBuffer),
},
};
/* by flabbergast:
* implementation of exported functions
* ** basic functions **
*/
void init(void)
{
SetupHardware();
LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY);
GlobalInterruptEnable();
}
void usb_tasks(void)
{
CDC_Device_USBTask(&VirtualSerial_CDC_Interface);
HID_Device_USBTask(&Keyboard_HID_Interface);
USB_USBTask();
}
/*
* ** usb_serial **
*/
uint16_t usb_serial_available(void)
{
return CDC_Device_BytesReceived(&VirtualSerial_CDC_Interface);
}
int16_t usb_serial_getchar(void)
{
return CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface);
}
void usb_serial_wait_for_key(void)
{
while(usb_serial_available() == 0) {
usb_tasks();
service_button();
_delay_ms(10);
}
}
void usb_serial_flush_input(void)
{
int16_t i;
int16_t bufsize = CDC_Device_BytesReceived(&VirtualSerial_CDC_Interface);
for(i=0; i<bufsize; i++)
CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface);
}
void usb_serial_putchar(uint8_t ch)
{
CDC_Device_SendByte(&VirtualSerial_CDC_Interface, ch);
}
void usb_serial_write(const char* const buffer)
{
CDC_Device_SendString(&VirtualSerial_CDC_Interface, buffer);
}
void usb_serial_write_P(const char* data)
{
while(pgm_read_byte(data) != 0)
usb_serial_putchar(pgm_read_byte(data++));
}
void usb_serial_writeln(const char* const buffer)
{
CDC_Device_SendString(&VirtualSerial_CDC_Interface, buffer);
usb_serial_write_P(PSTR("\r\n"));
}
void usb_serial_writeln_P(const char* data)
{
usb_serial_write_P(data);
usb_serial_write_P(PSTR("\r\n"));
}
void usb_serial_flush_output(void)
{
CDC_Device_Flush(&VirtualSerial_CDC_Interface);
}
void usb_serial_readline_refresh(const char *buffer, const uint16_t n, const bool obscure_input)
{
uint16_t i;
// delete the previous stuff on the line (plus one extra space)
usb_serial_putchar('\r');
for(i=0; i<=n; i++)
usb_serial_putchar(' ');
// write out the buffer contents
usb_serial_putchar('\r');
for(i=0; i<n; i++)
if(obscure_input)
usb_serial_putchar('*');
else
usb_serial_putchar(buffer[i]);
}
uint16_t usb_serial_readline(char *buffer, const uint16_t buffer_size, const bool obscure_input)
{
uint16_t end = 0;
char c;
while(true) {
if(usb_serial_available() > 0) {
c = (char)usb_serial_getchar();
switch((char) c) {
case '\r': // enter
buffer[end] = 0;
usb_serial_putchar('\n');
usb_serial_putchar('\r');
return end;
break;
case '\b': // ^H
case 127: // backspace
end--;
usb_serial_write("\b \b"); // remove the last char from the display
//usb_serial_readline_refresh(buffer, end, obscure_input);
break;
default:
if(end < buffer_size-1) {
buffer[end++] = c;
if(obscure_input)
usb_serial_putchar('*');
else
usb_serial_putchar(c);
}
break;
}
}
usb_tasks();
service_button();
_delay_ms(10);
}
return 0; // never reached
}
bool usb_serial_dtr(void) {
return(VirtualSerial_CDC_Interface.State.ControlLineStates.HostToDevice & CDC_CONTROL_LINE_OUT_DTR);
}
/** Configures the board hardware and chip peripherals for the demo's functionality. */
void SetupHardware(void)
{
#if (ARCH == ARCH_AVR8)
/* Disable watchdog if enabled by bootloader/fuses */
MCUSR &= ~(1 << WDRF);
wdt_disable();
/* Disable clock division */
clock_prescale_set(clock_div_1);
#elif (ARCH == ARCH_XMEGA)
/* Start the PLL to multiply the 2MHz RC oscillator to 32MHz and switch the CPU core to run from it */
XMEGACLK_StartPLL(CLOCK_SRC_INT_RC2MHZ, 2000000, F_CPU);
XMEGACLK_SetCPUClockSource(CLOCK_SRC_PLL);
/* Start the 32MHz internal RC oscillator and start the DFLL to increase it to 48MHz using the USB SOF as a reference */
XMEGACLK_StartInternalOscillator(CLOCK_SRC_INT_RC32MHZ);
XMEGACLK_StartDFLL(CLOCK_SRC_INT_RC32MHZ, DFLL_REF_INT_USBSOF, F_USB);
PMIC.CTRL = PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm | PMIC_HILVLEN_bm;
#endif
/* Hardware Initialization */
LEDs_Init();
Buttons_Init();
USB_Init();
Timer_Init();
}
/*
* ** button **
*/
bool button_is_pressed = false;
uint32_t button_pressed_timestamp = 0;
/** Checks for the button state */
void service_button(void)
{
uint8_t ButtonStatus_LCL = Buttons_GetStatus();
if (ButtonStatus_LCL & BUTTONS_BUTTON1) {
if (! button_is_pressed) {
button_pressed_timestamp = millis10();
button_is_pressed = true;
}
} else {
button_is_pressed = false;
}
}
/** returns for how long was the button pressed, in 10/1024 ms */
uint32_t button_pressed_for(void) {
if(button_is_pressed)
return (millis10() - button_pressed_timestamp);
else
return 0;
}
/** Event handler for the library USB Connection event. */
void EVENT_USB_Device_Connect(void)
{
LEDs_SetAllLEDs(LEDMASK_USB_ENUMERATING);
}
/** Event handler for the library USB Disconnection event. */
void EVENT_USB_Device_Disconnect(void)
{
LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY);
}
/** Event handler for the library USB Configuration Changed event. */
void EVENT_USB_Device_ConfigurationChanged(void)
{
bool ConfigSuccess = true;
ConfigSuccess &= HID_Device_ConfigureEndpoints(&Keyboard_HID_Interface);
ConfigSuccess &= CDC_Device_ConfigureEndpoints(&VirtualSerial_CDC_Interface);
USB_Device_EnableSOFEvents();
LEDs_SetAllLEDs(ConfigSuccess ? LEDMASK_USB_READY : LEDMASK_USB_ERROR);
}
/** Event handler for the library USB Control Request reception event. */
void EVENT_USB_Device_ControlRequest(void)
{
CDC_Device_ProcessControlRequest(&VirtualSerial_CDC_Interface);
HID_Device_ProcessControlRequest(&Keyboard_HID_Interface);
}
/** Event handler for the USB device Start Of Frame event. */
void EVENT_USB_Device_StartOfFrame(void)
{
HID_Device_MillisecondElapsed(&Keyboard_HID_Interface);
}
/* by flabbergast:
* global variables used for the HID callback below
*/
uint8_t usb_keyboard_current_key_GLOBAL = 0;
uint8_t usb_keyboard_current_modifier_GLOBAL = 0;
bool usb_keyboard_send_current_data_GLOBAL = false;
/** HID class driver callback function for the creation of HID reports to the host.
*
* \param[in] HIDInterfaceInfo Pointer to the HID class interface configuration structure being referenced
* \param[in,out] ReportID Report ID requested by the host if non-zero, otherwise callback should set to the generated report ID
* \param[in] ReportType Type of the report to create, either HID_REPORT_ITEM_In or HID_REPORT_ITEM_Feature
* \param[out] ReportData Pointer to a buffer where the created report should be stored
* \param[out] ReportSize Number of bytes written in the report (or zero if no report is to be sent)
*
* \return Boolean \c true to force the sending of the report, \c false to let the library determine if it needs to be sent
*/
bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
uint8_t* const ReportID,
const uint8_t ReportType,
void* ReportData,
uint16_t* const ReportSize)
{
USB_KeyboardReport_Data_t* KeyboardReport = (USB_KeyboardReport_Data_t*)ReportData;
if (usb_keyboard_send_current_data_GLOBAL)
{
KeyboardReport->KeyCode[0] = usb_keyboard_current_key_GLOBAL;
KeyboardReport->Modifier = usb_keyboard_current_modifier_GLOBAL;
usb_keyboard_send_current_data_GLOBAL = false;
}
*ReportSize = sizeof(USB_KeyboardReport_Data_t);
return false;
}
/* by flabbergast:
* usb_keyboard_press(key, mod)
* set global variables to let the above callback generate a keypress
* returns 'true' if data is set; 'false' when the previous keypress wasn't sent yet
*/
bool usb_keyboard_press(uint8_t key, uint8_t modifier) {
if(usb_keyboard_send_current_data_GLOBAL)
return false;
usb_keyboard_current_key_GLOBAL = key;
usb_keyboard_current_modifier_GLOBAL = modifier;
usb_keyboard_send_current_data_GLOBAL = true;
return true;
}
/** HID class driver callback function for the processing of HID reports from the host.
*
* \param[in] HIDInterfaceInfo Pointer to the HID class interface configuration structure being referenced
* \param[in] ReportID Report ID of the received report from the host
* \param[in] ReportType The type of report that the host has sent, either HID_REPORT_ITEM_Out or HID_REPORT_ITEM_Feature
* \param[in] ReportData Pointer to a buffer where the received report has been stored
* \param[in] ReportSize Size in bytes of the received HID report
*/
void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
const uint8_t ReportID,
const uint8_t ReportType,
const void* ReportData,
const uint16_t ReportSize)
{
// Unused (but mandatory for the HID class driver) in this demo, since there are no Host->Device reports
}

120
avr/LufaLayer.h Normal file
View file

@ -0,0 +1,120 @@
/*
* LufaLayer.h
* (c) 2014 flabbergast
* USB and Board layer: header file.
* See below the #includes for a list of functions provided by this
* layer.
*
* Portions of code come from a LUFA library Demo (license below).
*/
#ifndef _LUFALAYER_H_
#define _LUFALAYER_H_
/*
LUFA Library
Copyright (C) Dean Camera, 2014.
dean [at] fourwalledcubicle [dot] com
www.lufa-lib.org
*/
/*
Copyright 2014 Dean Camera (dean [at] fourwalledcubicle [dot] com)
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name of the author not be used in
advertising or publicity pertaining to distribution of the
software without specific, written prior permission.
The author disclaims all warranties with regard to this
software, including all implied warranties of merchantability
and fitness. In no event shall the author be liable for any
special, indirect or consequential damages or any damages
whatsoever resulting from loss of use, data or profits, whether
in an action of contract, negligence or other tortious action,
arising out of or in connection with the use or performance of
this software.
*/
/** \file
*
* Header file for LufaLayer.c.
*/
/* Includes: */
#include <LUFA/Drivers/Board/LEDs.h>
#include <LUFA/Drivers/Board/Buttons.h>
#include <LUFA/Drivers/USB/USB.h>
#include <LUFA/Platform/Platform.h>
#ifdef __cplusplus
extern "C"{
#endif
/* by flabbergast: exported functions to be used in the main program code
*/
// basic functions
void init(void);
void usb_tasks(void);
// usb_serial
uint16_t usb_serial_available(void); // number of getchars guaranteed to succeed immediately
int16_t usb_serial_getchar(void); // negative values mean error in receiving (not connected or no input)
void usb_serial_flush_input(void);
void usb_serial_putchar(uint8_t ch);
void usb_serial_wait_for_key(void); // BLOCKING (takes care of _tasks)
void usb_serial_write(const char* const buffer);
void usb_serial_write_P(const char* data);
void usb_serial_writeln(const char* const buffer);
void usb_serial_writeln_P(const char* data);
void usb_serial_flush_output(void);
uint16_t usb_serial_readline(char *buffer, const uint16_t buffer_size, const bool obscure_input); // BLOCKING (takes care of _tasks)
bool usb_serial_dtr(void);
// usb_keyboard
bool usb_keyboard_press(uint8_t key, uint8_t mod);
// buttons, LEDs and such
uint32_t button_pressed_for(void); // for how long was the button pressed? (in 10/1024 sec; 0 if not pressed)
void service_button(void); // should be called periodically to update the button state
/* Macros: */
/** LED mask for the library LED driver, to indicate that the USB interface is not ready. */
#define LEDMASK_USB_NOTREADY LEDS_LED1
/** LED mask for the library LED driver, to indicate that the USB interface is enumerating. */
#define LEDMASK_USB_ENUMERATING (LEDS_LED2 | LEDS_LED3)
/** LED mask for the library LED driver, to indicate that the USB interface is ready. */
#define LEDMASK_USB_READY (LEDS_LED2 | LEDS_LED4)
/** LED mask for the library LED driver, to indicate that an error has occurred in the USB interface. */
#define LEDMASK_USB_ERROR (LEDS_LED1 | LEDS_LED3)
/* Function Prototypes: */
void SetupHardware(void);
void EVENT_USB_Device_Connect(void);
void EVENT_USB_Device_Disconnect(void);
void EVENT_USB_Device_ConfigurationChanged(void);
void EVENT_USB_Device_ControlRequest(void);
void EVENT_USB_Device_StartOfFrame(void);
bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
uint8_t* const ReportID,
const uint8_t ReportType,
void* ReportData,
uint16_t* const ReportSize);
void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
const uint8_t ReportID,
const uint8_t ReportType,
const void* ReportData,
const uint16_t ReportSize);
#ifdef __cplusplus
}
#endif
#endif

75
avr/README.md Normal file
View file

@ -0,0 +1,75 @@
# sha204_playground firmware: straight avr-gcc version
Here are the source for the sha204_playground firmware, to be compiled
with avr-gcc against the [LUFA] library.
## Compilation / usage
Get the [LUFA] library and copy the `LUFA` folder to the subdirectory
`avr/LUFA`.
Make modifications to match your hardware setup (`makefile` and
`SHA204/SHA204SWI_hardware_config.h` are probably the most important
ones).
Compile (`make`) and upload to your board with ATSHA204 (how to do this
depends on your bootloader).
After reset, your board should enumerate as a CDC Serial Device (also as
a keyboard, but that functionality isn't used yet). Note that an `.inf`
file might be required on Windows systems.
Connect to this Serial device using a serial terminal (e.g. puTTY,
picocom, minicom, screen, ...) and you should be presented with a "menu"
of commands that interact with ATSHA204 device.
## Communicating with the firmware
Probably the first thing to try is pressing `k` (to test waking the
ATSHA up) and pressing `s` (to print the serial number of your device).
Alternatively (depending on the functionality required), you can use the
`talk_to_sha204.py` script to talk to the ATSHA via this firmware.
### Binary mode
The firmware supports a "binary mode", for use with scripts. For a
demonstration on how this is done, have a look at the python script
`talk_to_sha204.py`.
## Notes
- The subdirectory `SHA204` contains a re-usable library, working on
both AVR8 and XMEGA architectures. Note that only 16MHz and 32MHz CPU
speeds are tested.
- At the moment, only Single-Wire Interface for ATSHA204 is implemented,
by bit-banging in C (so a speedy CPU is probably required).
- The firmware also enumerates as a Keyboard. This functionality is not
used at the moment; see `LufaLayer.h` for the functions that can
generate "keypresses".
## VID/PID
Every USB device has a Vendor/Product identifying signature. This is set
in software here (`Descriptors.c`). The current code uses a pair which
belongs to the [LUFA] library. DO NOT USE IT for any other than
development purposes only! See [LUFA's VID/PID
page](http://www.fourwalledcubicle.com/files/LUFA/Doc/120730/html/_page__v_i_d_p_i_d.html).
## Credits
- The whole USB interface utilizes Dean Camera's excellent [LUFA]
library, and builds on several LUFA demos supplied with the library.
- I've started with bettse's version of a SHA204 library for Arduino
[on github](https://github.com/bettse/arduino_projects/tree/master/libraries/SHA204),
and modified it to work on XMEGA firmware, and not necessarily as an
Arduino library.
## License
My code is (c) flabbergast. MIT license (see LICENSE file). Portions
of the code come from LUFA demos, this is licensed by LUFA's license.
The original code from SHA204 library is licensed by Apache V2.0 license.
[LUFA]: http://www.fourwalledcubicle.com/LUFA.php

11
avr/SHA204/README.md Normal file
View file

@ -0,0 +1,11 @@
SHA204 Library
==============
This is the main SHA204 library for the ATSHA204 chip from Atmel.
In order to use it you will need to copy this directory to a
subdirectory of your project, include `SHA204SWI.h` and instantiate a
`SHA204SWI` object.
Note that you'll need to modify the `SHA204SWI_hardware_config.h` to
match your hardware setup (the wiring is hardcoded during compilation!).

795
avr/SHA204/SHA204.cpp Normal file
View file

@ -0,0 +1,795 @@
/*
Copyright 2013 Nusku Networks
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "SHA204.h"
#include "SHA204ReturnCodes.h"
#include "SHA204Definitions.h"
#include <util/delay.h>
#include <string.h>
// helper function to make delay work with variable delay
void Delay_ms(uint32_t n) {
while (n--)
_delay_ms(1);
}
/* Puts a the ATSHA204's unique, 4-byte serial number in the response array
returns an SHA204 Return code */
uint8_t SHA204::serialNumber(uint8_t * response) {
uint8_t readCommand[READ_COUNT];
uint8_t readResponse[READ_4_RSP_SIZE];
/* read from bytes 0->3 of config zone */
uint8_t returnCode = read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN03);
if (!returnCode) // should return 0 if successful
{
for (int i=0; i<4; i++) // store bytes 0-3 into respones array
response[i] = readResponse[SHA204_BUFFER_POS_DATA+i];
/* read from bytes 8->11 of config zone */
returnCode = read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN47);
for (int i=4; i<8; i++) // store bytes 4-7 of SN into response array
response[i] = readResponse[SHA204_BUFFER_POS_DATA+(i-4)];
if (!returnCode)
{ /* Finally if last two reads were successful, read byte 8 of the SN */
returnCode = read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN8);
response[8] = readResponse[SHA204_BUFFER_POS_DATA]; // Byte 8 of SN should always be 0xEE
}
}
return returnCode;
}
/* Communication functions */
uint8_t SHA204::wakeup(uint8_t *response) {
uint8_t ret_code = chip_wakeup();
if (ret_code != SHA204_SUCCESS)
return ret_code;
ret_code = receive_response(SHA204_RSP_SIZE_MIN, response);
if (ret_code != SHA204_SUCCESS)
return ret_code;
// Verify status response.
if (response[SHA204_BUFFER_POS_COUNT] != SHA204_RSP_SIZE_MIN)
ret_code = SHA204_INVALID_SIZE;
else if (response[SHA204_BUFFER_POS_STATUS] != SHA204_STATUS_BYTE_WAKEUP)
ret_code = SHA204_COMM_FAIL;
else
{
if ((response[SHA204_RSP_SIZE_MIN - SHA204_CRC_SIZE] != 0x33)
|| (response[SHA204_RSP_SIZE_MIN + 1 - SHA204_CRC_SIZE] != 0x43))
ret_code = SHA204_BAD_CRC;
}
if (ret_code != SHA204_SUCCESS)
_delay_ms(SHA204_COMMAND_EXEC_MAX);
return ret_code;
}
uint8_t SHA204::send_and_receive(uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer, uint8_t execution_delay, uint8_t execution_timeout) {
uint8_t ret_code = SHA204_FUNC_FAIL;
uint8_t ret_code_resync;
uint8_t n_retries_send;
uint8_t n_retries_receive;
uint8_t i;
uint8_t status_byte;
uint8_t count = tx_buffer[SHA204_BUFFER_POS_COUNT];
uint8_t count_minus_crc = count - SHA204_CRC_SIZE;
uint16_t execution_timeout_us = (uint16_t) (execution_timeout * 1000) + SHA204_RESPONSE_TIMEOUT();
volatile uint16_t timeout_countdown;
// Append CRC.
calculate_crc(count_minus_crc, tx_buffer, tx_buffer + count_minus_crc);
// Retry loop for sending a command and receiving a response.
n_retries_send = SHA204_RETRY_COUNT + 1;
while ((n_retries_send-- > 0) && (ret_code != SHA204_SUCCESS))
{
// Send command.
ret_code = send_command(count, tx_buffer);
if (ret_code != SHA204_SUCCESS)
{
if (resync(rx_size, rx_buffer) == SHA204_RX_NO_RESPONSE)
return ret_code; // The device seems to be dead in the water.
else
continue;
}
// Wait minimum command execution time and then start polling for a response.
Delay_ms(execution_delay);
// Retry loop for receiving a response.
n_retries_receive = SHA204_RETRY_COUNT + 1;
while (n_retries_receive-- > 0)
{
// Reset response buffer.
for (i = 0; i < rx_size; i++)
rx_buffer[i] = 0;
// Poll for response.
timeout_countdown = execution_timeout_us;
do
{
ret_code = receive_response(rx_size, rx_buffer);
timeout_countdown -= SHA204_RESPONSE_TIMEOUT();
}
while ((timeout_countdown > SHA204_RESPONSE_TIMEOUT()) && (ret_code == SHA204_RX_NO_RESPONSE));
if (ret_code == SHA204_RX_NO_RESPONSE)
{
// We did not receive a response. Re-synchronize and send command again.
if (resync(rx_size, rx_buffer) == SHA204_RX_NO_RESPONSE)
// The device seems to be dead in the water.
return ret_code;
else
break;
}
// Check whether we received a valid response.
if (ret_code == SHA204_INVALID_SIZE)
{
// We see 0xFF for the count when communication got out of sync.
ret_code_resync = resync(rx_size, rx_buffer);
if (ret_code_resync == SHA204_SUCCESS)
// We did not have to wake up the device. Try receiving response again.
continue;
if (ret_code_resync == SHA204_RESYNC_WITH_WAKEUP)
// We could re-synchronize, but only after waking up the device.
// Re-send command.
break;
else
// We failed to re-synchronize.
return ret_code;
}
// We received a response of valid size.
// Check the consistency of the response.
ret_code = check_crc(rx_buffer);
if (ret_code == SHA204_SUCCESS)
{
// Received valid response.
if (rx_buffer[SHA204_BUFFER_POS_COUNT] > SHA204_RSP_SIZE_MIN)
// Received non-status response. We are done.
return ret_code;
// Received status response.
status_byte = rx_buffer[SHA204_BUFFER_POS_STATUS];
// Translate the three possible device status error codes
// into library return codes.
if (status_byte == SHA204_STATUS_BYTE_PARSE)
return SHA204_PARSE_ERROR;
if (status_byte == SHA204_STATUS_BYTE_EXEC)
return SHA204_CMD_FAIL;
if (status_byte == SHA204_STATUS_BYTE_COMM)
{
// In case of the device status byte indicating a communication
// error this function exits the retry loop for receiving a response
// and enters the overall retry loop
// (send command / receive response).
ret_code = SHA204_STATUS_CRC;
break;
}
// Received status response from CheckMAC, DeriveKey, GenDig,
// Lock, Nonce, Pause, UpdateExtra, or Write command.
return ret_code;
}
else
{
// Received response with incorrect CRC.
ret_code_resync = resync(rx_size, rx_buffer);
if (ret_code_resync == SHA204_SUCCESS)
// We did not have to wake up the device. Try receiving response again.
continue;
if (ret_code_resync == SHA204_RESYNC_WITH_WAKEUP)
// We could re-synchronize, but only after waking up the device.
// Re-send command.
break;
else
// We failed to re-synchronize.
return ret_code;
} // block end of check response consistency
} // block end of receive retry loop
} // block end of send and receive retry loop
return ret_code;
}
/* Marshaling functions */
uint8_t SHA204::random(uint8_t * tx_buffer, uint8_t * rx_buffer, uint8_t mode) {
if (!tx_buffer || !rx_buffer || (mode > RANDOM_NO_SEED_UPDATE))
return SHA204_BAD_PARAM;
tx_buffer[SHA204_COUNT_IDX] = RANDOM_COUNT;
tx_buffer[SHA204_OPCODE_IDX] = SHA204_RANDOM;
tx_buffer[RANDOM_MODE_IDX] = mode & RANDOM_SEED_UPDATE;
tx_buffer[RANDOM_PARAM2_IDX] =
tx_buffer[RANDOM_PARAM2_IDX + 1] = 0;
return send_and_receive(&tx_buffer[0], RANDOM_RSP_SIZE, &rx_buffer[0], RANDOM_DELAY, RANDOM_EXEC_MAX - RANDOM_DELAY);
}
uint8_t SHA204::dev_rev(uint8_t *tx_buffer, uint8_t *rx_buffer) {
if (!tx_buffer || !rx_buffer)
return SHA204_BAD_PARAM;
tx_buffer[SHA204_COUNT_IDX] = DEVREV_COUNT;
tx_buffer[SHA204_OPCODE_IDX] = SHA204_DEVREV;
// Parameters are 0.
tx_buffer[DEVREV_PARAM1_IDX] =
tx_buffer[DEVREV_PARAM2_IDX] =
tx_buffer[DEVREV_PARAM2_IDX + 1] = 0;
return send_and_receive(&tx_buffer[0], DEVREV_RSP_SIZE, &rx_buffer[0],
DEVREV_DELAY, DEVREV_EXEC_MAX - DEVREV_DELAY);
}
uint8_t SHA204::read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address) {
uint8_t rx_size;
if (!tx_buffer || !rx_buffer || ((zone & ~READ_ZONE_MASK) != 0)
|| ((zone & READ_ZONE_MODE_32_BYTES) && (zone == SHA204_ZONE_OTP)))
return SHA204_BAD_PARAM;
address >>= 2;
if ((zone & SHA204_ZONE_MASK) == SHA204_ZONE_CONFIG)
{
if (address > SHA204_ADDRESS_MASK_CONFIG)
return SHA204_BAD_PARAM;
}
else if ((zone & SHA204_ZONE_MASK) == SHA204_ZONE_OTP)
{
if (address > SHA204_ADDRESS_MASK_OTP)
return SHA204_BAD_PARAM;
}
else if ((zone & SHA204_ZONE_MASK) == SHA204_ZONE_DATA)
{
if (address > SHA204_ADDRESS_MASK)
return SHA204_BAD_PARAM;
}
tx_buffer[SHA204_COUNT_IDX] = READ_COUNT;
tx_buffer[SHA204_OPCODE_IDX] = SHA204_READ;
tx_buffer[READ_ZONE_IDX] = zone;
tx_buffer[READ_ADDR_IDX] = (uint8_t) (address & SHA204_ADDRESS_MASK);
tx_buffer[READ_ADDR_IDX + 1] = 0;
rx_size = (zone & SHA204_ZONE_COUNT_FLAG) ? READ_32_RSP_SIZE : READ_4_RSP_SIZE;
return send_and_receive(&tx_buffer[0], rx_size, &rx_buffer[0], READ_DELAY, READ_EXEC_MAX - READ_DELAY);
}
uint8_t SHA204::execute(uint8_t op_code, uint8_t param1, uint16_t param2,
uint8_t datalen1, uint8_t *data1, uint8_t datalen2, uint8_t *data2, uint8_t datalen3, uint8_t *data3,
uint8_t tx_size, uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer) {
uint8_t poll_delay, poll_timeout, response_size;
uint8_t *p_buffer;
uint8_t len;
uint8_t ret_code = check_parameters(op_code, param1, param2,
datalen1, data1, datalen2, data2, datalen3, data3,
tx_size, tx_buffer, rx_size, rx_buffer);
if (ret_code != SHA204_SUCCESS)
return ret_code;
// Supply delays and response size.
switch (op_code)
{
case SHA204_CHECKMAC:
poll_delay = CHECKMAC_DELAY;
poll_timeout = CHECKMAC_EXEC_MAX - CHECKMAC_DELAY;
response_size = CHECKMAC_RSP_SIZE;
break;
case SHA204_DERIVE_KEY:
poll_delay = DERIVE_KEY_DELAY;
poll_timeout = DERIVE_KEY_EXEC_MAX - DERIVE_KEY_DELAY;
response_size = DERIVE_KEY_RSP_SIZE;
break;
case SHA204_DEVREV:
poll_delay = DEVREV_DELAY;
poll_timeout = DEVREV_EXEC_MAX - DEVREV_DELAY;
response_size = DEVREV_RSP_SIZE;
break;
case SHA204_GENDIG:
poll_delay = GENDIG_DELAY;
poll_timeout = GENDIG_EXEC_MAX - GENDIG_DELAY;
response_size = GENDIG_RSP_SIZE;
break;
case SHA204_HMAC:
poll_delay = HMAC_DELAY;
poll_timeout = HMAC_EXEC_MAX - HMAC_DELAY;
response_size = HMAC_RSP_SIZE;
break;
case SHA204_LOCK:
poll_delay = LOCK_DELAY;
poll_timeout = LOCK_EXEC_MAX - LOCK_DELAY;
response_size = LOCK_RSP_SIZE;
break;
case SHA204_MAC:
poll_delay = MAC_DELAY;
poll_timeout = MAC_EXEC_MAX - MAC_DELAY;
response_size = MAC_RSP_SIZE;
break;
case SHA204_NONCE:
poll_delay = NONCE_DELAY;
poll_timeout = NONCE_EXEC_MAX - NONCE_DELAY;
response_size = param1 == NONCE_MODE_PASSTHROUGH
? NONCE_RSP_SIZE_SHORT : NONCE_RSP_SIZE_LONG;
break;
case SHA204_PAUSE:
poll_delay = PAUSE_DELAY;
poll_timeout = PAUSE_EXEC_MAX - PAUSE_DELAY;
response_size = PAUSE_RSP_SIZE;
break;
case SHA204_RANDOM:
poll_delay = RANDOM_DELAY;
poll_timeout = RANDOM_EXEC_MAX - RANDOM_DELAY;
response_size = RANDOM_RSP_SIZE;
break;
case SHA204_READ:
poll_delay = READ_DELAY;
poll_timeout = READ_EXEC_MAX - READ_DELAY;
response_size = (param1 & SHA204_ZONE_COUNT_FLAG)
? READ_32_RSP_SIZE : READ_4_RSP_SIZE;
break;
case SHA204_UPDATE_EXTRA:
poll_delay = UPDATE_DELAY;
poll_timeout = UPDATE_EXEC_MAX - UPDATE_DELAY;
response_size = UPDATE_RSP_SIZE;
break;
case SHA204_WRITE:
poll_delay = WRITE_DELAY;
poll_timeout = WRITE_EXEC_MAX - WRITE_DELAY;
response_size = WRITE_RSP_SIZE;
break;
default:
poll_delay = 0;
poll_timeout = SHA204_COMMAND_EXEC_MAX;
response_size = rx_size;
}
// Assemble command.
len = datalen1 + datalen2 + datalen3 + SHA204_CMD_SIZE_MIN;
p_buffer = tx_buffer;
*p_buffer++ = len;
*p_buffer++ = op_code;
*p_buffer++ = param1;
*p_buffer++ = param2 & 0xFF;
*p_buffer++ = param2 >> 8;
if (datalen1 > 0) {
memcpy(p_buffer, data1, datalen1);
p_buffer += datalen1;
}
if (datalen2 > 0) {
memcpy(p_buffer, data2, datalen2);
p_buffer += datalen2;
}
if (datalen3 > 0) {
memcpy(p_buffer, data3, datalen3);
p_buffer += datalen3;
}
calculate_crc(len - SHA204_CRC_SIZE, tx_buffer, p_buffer);
// Send command and receive response.
return send_and_receive(&tx_buffer[0], response_size,
&rx_buffer[0], poll_delay, poll_timeout);
}
uint8_t SHA204::check_parameters(uint8_t op_code, uint8_t param1, uint16_t param2,
uint8_t datalen1, uint8_t *data1, uint8_t datalen2, uint8_t *data2, uint8_t datalen3, uint8_t *data3,
uint8_t tx_size, uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer) {
#ifdef SHA204_CHECK_PARAMETERS
uint8_t len = datalen1 + datalen2 + datalen3 + SHA204_CMD_SIZE_MIN;
if (!tx_buffer || tx_size < len || rx_size < SHA204_RSP_SIZE_MIN || !rx_buffer)
return SHA204_BAD_PARAM;
if ((datalen1 > 0 && !data1) || (datalen2 > 0 && !data2) || (datalen3 > 0 && !data3))
return SHA204_BAD_PARAM;
// Check parameters depending on op-code.
switch (op_code)
{
case SHA204_CHECKMAC:
if (
// no null pointers allowed
!data1 || !data2
// No reserved bits should be set.
|| (param1 | CHECKMAC_MODE_MASK) != CHECKMAC_MODE_MASK
// key_id > 15 not allowed
|| param2 > SHA204_KEY_ID_MAX
)
return SHA204_BAD_PARAM;
break;
case SHA204_DERIVE_KEY:
if (param2 > SHA204_KEY_ID_MAX)
return SHA204_BAD_PARAM;
break;
case SHA204_DEVREV:
break;
case SHA204_GENDIG:
if ((param1 != GENDIG_ZONE_OTP) && (param1 != GENDIG_ZONE_DATA))
return SHA204_BAD_PARAM;
break;
case SHA204_HMAC:
if ((param1 & ~HMAC_MODE_MASK) != 0)
return SHA204_BAD_PARAM;
break;
case SHA204_LOCK:
if (((param1 & ~LOCK_ZONE_MASK) != 0)
|| ((param1 & LOCK_ZONE_NO_CRC) && (param2 != 0)))
return SHA204_BAD_PARAM;
break;
case SHA204_MAC:
if (((param1 & ~MAC_MODE_MASK) != 0)
|| (((param1 & MAC_MODE_BLOCK2_TEMPKEY) == 0) && !data1))
return SHA204_BAD_PARAM;
break;
case SHA204_NONCE:
if ( !data1
|| (param1 > NONCE_MODE_PASSTHROUGH)
|| (param1 == NONCE_MODE_INVALID)
)
return SHA204_BAD_PARAM;
break;
case SHA204_PAUSE:
break;
case SHA204_RANDOM:
if (param1 > RANDOM_NO_SEED_UPDATE)
return SHA204_BAD_PARAM;
break;
case SHA204_READ:
if (((param1 & ~READ_ZONE_MASK) != 0)
|| ((param1 & READ_ZONE_MODE_32_BYTES) && (param1 == SHA204_ZONE_OTP)))
return SHA204_BAD_PARAM;
break;
case SHA204_TEMPSENSE:
break;
case SHA204_UPDATE_EXTRA:
if (param1 > UPDATE_CONFIG_BYTE_86)
return SHA204_BAD_PARAM;
break;
case SHA204_WRITE:
if (!data1 || ((param1 & ~WRITE_ZONE_MASK) != 0))
return SHA204_BAD_PARAM;
break;
default:
// unknown op-code
return SHA204_BAD_PARAM;
}
return SHA204_SUCCESS;
#else
return SHA204_SUCCESS;
#endif
}
/* CRC Calculator and Checker */
void SHA204::calculate_crc(uint8_t length, uint8_t *data, uint8_t *crc) {
uint8_t counter;
uint16_t crc_register = 0;
uint16_t polynom = 0x8005;
uint8_t shift_register;
uint8_t data_bit, crc_bit;
for (counter = 0; counter < length; counter++)
{
for (shift_register = 0x01; shift_register > 0x00; shift_register <<= 1)
{
data_bit = (data[counter] & shift_register) ? 1 : 0;
crc_bit = crc_register >> 15;
// Shift CRC to the left by 1.
crc_register <<= 1;
if ((data_bit ^ crc_bit) != 0)
crc_register ^= polynom;
}
}
crc[0] = (uint8_t) (crc_register & 0x00FF);
crc[1] = (uint8_t) (crc_register >> 8);
}
uint8_t SHA204::check_crc(uint8_t *response) {
uint8_t crc[SHA204_CRC_SIZE];
uint8_t count = response[SHA204_BUFFER_POS_COUNT];
count -= SHA204_CRC_SIZE;
calculate_crc(count, response, crc);
return (crc[0] == response[count] && crc[1] == response[count + 1])
? SHA204_SUCCESS : SHA204_BAD_CRC;
}
uint8_t SHA204::check_mac(uint8_t *tx_buffer, uint8_t *rx_buffer,
uint8_t mode, uint8_t key_id, uint8_t *client_challenge, uint8_t *client_response, uint8_t *other_data) {
if (
// no null pointers allowed
!tx_buffer || !rx_buffer || !client_response || !other_data
// No reserved bits should be set.
|| (mode | CHECKMAC_MODE_MASK) != CHECKMAC_MODE_MASK
// key_id > 15 not allowed
|| key_id > SHA204_KEY_ID_MAX
)
return SHA204_BAD_PARAM;
tx_buffer[SHA204_COUNT_IDX] = CHECKMAC_COUNT;
tx_buffer[SHA204_OPCODE_IDX] = SHA204_CHECKMAC;
tx_buffer[CHECKMAC_MODE_IDX] = mode & CHECKMAC_MODE_MASK;
tx_buffer[CHECKMAC_KEYID_IDX]= key_id;
tx_buffer[CHECKMAC_KEYID_IDX + 1] = 0;
if (client_challenge == NULL)
memset(&tx_buffer[CHECKMAC_CLIENT_CHALLENGE_IDX], 0, CHECKMAC_CLIENT_CHALLENGE_SIZE);
else
memcpy(&tx_buffer[CHECKMAC_CLIENT_CHALLENGE_IDX], client_challenge, CHECKMAC_CLIENT_CHALLENGE_SIZE);
memcpy(&tx_buffer[CHECKMAC_CLIENT_RESPONSE_IDX], client_response, CHECKMAC_CLIENT_RESPONSE_SIZE);
memcpy(&tx_buffer[CHECKMAC_DATA_IDX], other_data, CHECKMAC_OTHER_DATA_SIZE);
return send_and_receive(&tx_buffer[0], CHECKMAC_RSP_SIZE, &rx_buffer[0],
CHECKMAC_DELAY, CHECKMAC_EXEC_MAX - CHECKMAC_DELAY);
}
uint8_t SHA204::derive_key(uint8_t *tx_buffer, uint8_t *rx_buffer,
uint8_t random, uint8_t target_key, uint8_t *mac) {
if (!tx_buffer || !rx_buffer || ((random & ~DERIVE_KEY_RANDOM_FLAG) != 0)
|| (target_key > SHA204_KEY_ID_MAX))
return SHA204_BAD_PARAM;
tx_buffer[SHA204_OPCODE_IDX] = SHA204_DERIVE_KEY;
tx_buffer[DERIVE_KEY_RANDOM_IDX] = random;
tx_buffer[DERIVE_KEY_TARGETKEY_IDX] = target_key;
tx_buffer[DERIVE_KEY_TARGETKEY_IDX + 1] = 0;
if (mac != NULL)
{
memcpy(&tx_buffer[DERIVE_KEY_MAC_IDX], mac, DERIVE_KEY_MAC_SIZE);
tx_buffer[SHA204_COUNT_IDX] = DERIVE_KEY_COUNT_LARGE;
}
else
tx_buffer[SHA204_COUNT_IDX] = DERIVE_KEY_COUNT_SMALL;
return send_and_receive(&tx_buffer[0], DERIVE_KEY_RSP_SIZE, &rx_buffer[0],
DERIVE_KEY_DELAY, DERIVE_KEY_EXEC_MAX - DERIVE_KEY_DELAY);
}
uint8_t SHA204::gen_dig(uint8_t *tx_buffer, uint8_t *rx_buffer,
uint8_t zone, uint8_t key_id, uint8_t *other_data) {
if (!tx_buffer || !rx_buffer || (zone > GENDIG_ZONE_DATA))
return SHA204_BAD_PARAM;
if (((zone == GENDIG_ZONE_OTP) && (key_id > SHA204_OTP_BLOCK_MAX))
|| ((zone == GENDIG_ZONE_DATA) && (key_id > SHA204_KEY_ID_MAX)))
return SHA204_BAD_PARAM;
tx_buffer[SHA204_OPCODE_IDX] = SHA204_GENDIG;
tx_buffer[GENDIG_ZONE_IDX] = zone;
tx_buffer[GENDIG_KEYID_IDX] = key_id;
tx_buffer[GENDIG_KEYID_IDX + 1] = 0;
if (other_data != NULL)
{
memcpy(&tx_buffer[GENDIG_DATA_IDX], other_data, GENDIG_OTHER_DATA_SIZE);
tx_buffer[SHA204_COUNT_IDX] = GENDIG_COUNT_DATA;
}
else
tx_buffer[SHA204_COUNT_IDX] = GENDIG_COUNT;
return send_and_receive(&tx_buffer[0], GENDIG_RSP_SIZE, &rx_buffer[0],
GENDIG_DELAY, GENDIG_EXEC_MAX - GENDIG_DELAY);
}
uint8_t SHA204::hmac(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t mode, uint16_t key_id) {
if (!tx_buffer || !rx_buffer || ((mode & ~HMAC_MODE_MASK) != 0))
return SHA204_BAD_PARAM;
tx_buffer[SHA204_COUNT_IDX] = HMAC_COUNT;
tx_buffer[SHA204_OPCODE_IDX] = SHA204_HMAC;
tx_buffer[HMAC_MODE_IDX] = mode;
// Although valid key identifiers are only
// from 0 to 15, all 16 bits are used in the HMAC message.
tx_buffer[HMAC_KEYID_IDX] = key_id & 0xFF;
tx_buffer[HMAC_KEYID_IDX + 1] = key_id >> 8;
return send_and_receive(&tx_buffer[0], HMAC_RSP_SIZE, &rx_buffer[0],
HMAC_DELAY, HMAC_EXEC_MAX - HMAC_DELAY);
}
uint8_t SHA204::lock(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t summary) {
if (!tx_buffer || !rx_buffer || ((zone & ~LOCK_ZONE_MASK) != 0)
|| ((zone & LOCK_ZONE_NO_CRC) && (summary != 0)))
return SHA204_BAD_PARAM;
tx_buffer[SHA204_COUNT_IDX] = LOCK_COUNT;
tx_buffer[SHA204_OPCODE_IDX] = SHA204_LOCK;
tx_buffer[LOCK_ZONE_IDX] = zone & LOCK_ZONE_MASK;
tx_buffer[LOCK_SUMMARY_IDX]= summary & 0xFF;
tx_buffer[LOCK_SUMMARY_IDX + 1]= summary >> 8;
return send_and_receive(&tx_buffer[0], LOCK_RSP_SIZE, &rx_buffer[0],
LOCK_DELAY, LOCK_EXEC_MAX - LOCK_DELAY);
}
uint8_t SHA204::mac(uint8_t *tx_buffer, uint8_t *rx_buffer,
uint8_t mode, uint16_t key_id, uint8_t *challenge) {
if (!tx_buffer || !rx_buffer || ((mode & ~MAC_MODE_MASK) != 0)
|| (((mode & MAC_MODE_BLOCK2_TEMPKEY) == 0) && !challenge))
return SHA204_BAD_PARAM;
tx_buffer[SHA204_COUNT_IDX] = MAC_COUNT_SHORT;
tx_buffer[SHA204_OPCODE_IDX] = SHA204_MAC;
tx_buffer[MAC_MODE_IDX] = mode;
tx_buffer[MAC_KEYID_IDX] = key_id & 0xFF;
tx_buffer[MAC_KEYID_IDX + 1] = key_id >> 8;
if ((mode & MAC_MODE_BLOCK2_TEMPKEY) == 0)
{
memcpy(&tx_buffer[MAC_CHALLENGE_IDX], challenge, MAC_CHALLENGE_SIZE);
tx_buffer[SHA204_COUNT_IDX] = MAC_COUNT_LONG;
}
return send_and_receive(&tx_buffer[0], MAC_RSP_SIZE, &rx_buffer[0],
MAC_DELAY, MAC_EXEC_MAX - MAC_DELAY);
}
uint8_t SHA204::nonce(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t mode, uint8_t *numin) {
uint8_t rx_size;
if (!tx_buffer || !rx_buffer || !numin
|| (mode > NONCE_MODE_PASSTHROUGH) || (mode == NONCE_MODE_INVALID))
return SHA204_BAD_PARAM;
tx_buffer[SHA204_OPCODE_IDX] = SHA204_NONCE;
tx_buffer[NONCE_MODE_IDX] = mode;
// 2. parameter is 0.
tx_buffer[NONCE_PARAM2_IDX] =
tx_buffer[NONCE_PARAM2_IDX + 1] = 0;
if (mode != NONCE_MODE_PASSTHROUGH)
{
memcpy(&tx_buffer[NONCE_INPUT_IDX], numin, NONCE_NUMIN_SIZE);
tx_buffer[SHA204_COUNT_IDX] = NONCE_COUNT_SHORT;
rx_size = NONCE_RSP_SIZE_LONG;
}
else
{
memcpy(&tx_buffer[NONCE_INPUT_IDX], numin, NONCE_NUMIN_SIZE_PASSTHROUGH);
tx_buffer[SHA204_COUNT_IDX] = NONCE_COUNT_LONG;
rx_size = NONCE_RSP_SIZE_SHORT;
}
return send_and_receive(&tx_buffer[0], rx_size, &rx_buffer[0],
NONCE_DELAY, NONCE_EXEC_MAX - NONCE_DELAY);
}
uint8_t SHA204::pause(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t selector) {
if (!tx_buffer || !rx_buffer)
return SHA204_BAD_PARAM;
tx_buffer[SHA204_COUNT_IDX] = PAUSE_COUNT;
tx_buffer[SHA204_OPCODE_IDX] = SHA204_PAUSE;
tx_buffer[PAUSE_SELECT_IDX] = selector;
// 2. parameter is 0.
tx_buffer[PAUSE_PARAM2_IDX] =
tx_buffer[PAUSE_PARAM2_IDX + 1] = 0;
return send_and_receive(&tx_buffer[0], PAUSE_RSP_SIZE, &rx_buffer[0],
PAUSE_DELAY, PAUSE_EXEC_MAX - PAUSE_DELAY);
}
uint8_t SHA204::update_extra(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t mode, uint8_t new_value) {
if (!tx_buffer || !rx_buffer || (mode > UPDATE_CONFIG_BYTE_86))
return SHA204_BAD_PARAM;
tx_buffer[SHA204_COUNT_IDX] = UPDATE_COUNT;
tx_buffer[SHA204_OPCODE_IDX] = SHA204_UPDATE_EXTRA;
tx_buffer[UPDATE_MODE_IDX] = mode;
tx_buffer[UPDATE_VALUE_IDX] = new_value;
tx_buffer[UPDATE_VALUE_IDX + 1] = 0;
return send_and_receive(&tx_buffer[0], UPDATE_RSP_SIZE, &rx_buffer[0],
UPDATE_DELAY, UPDATE_EXEC_MAX - UPDATE_DELAY);
}
uint8_t SHA204::write(uint8_t *tx_buffer, uint8_t *rx_buffer,
uint8_t zone, uint16_t address, uint8_t *new_value, uint8_t *mac) {
uint8_t *p_command;
uint8_t count;
if (!tx_buffer || !rx_buffer || !new_value || ((zone & ~WRITE_ZONE_MASK) != 0))
return SHA204_BAD_PARAM;
address >>= 2;
if ((zone & SHA204_ZONE_MASK) == SHA204_ZONE_CONFIG) {
if (address > SHA204_ADDRESS_MASK_CONFIG)
return SHA204_BAD_PARAM;
}
else if ((zone & SHA204_ZONE_MASK) == SHA204_ZONE_OTP) {
if (address > SHA204_ADDRESS_MASK_OTP)
return SHA204_BAD_PARAM;
}
else if ((zone & SHA204_ZONE_MASK) == SHA204_ZONE_DATA) {
if (address > SHA204_ADDRESS_MASK)
return SHA204_BAD_PARAM;
}
p_command = &tx_buffer[SHA204_OPCODE_IDX];
*p_command++ = SHA204_WRITE;
*p_command++ = zone;
*p_command++ = (uint8_t) (address & SHA204_ADDRESS_MASK);
*p_command++ = 0;
count = (zone & SHA204_ZONE_COUNT_FLAG) ? SHA204_ZONE_ACCESS_32 : SHA204_ZONE_ACCESS_4;
memcpy(p_command, new_value, count);
p_command += count;
if (mac != NULL)
{
memcpy(p_command, mac, WRITE_MAC_SIZE);
p_command += WRITE_MAC_SIZE;
}
// Supply count.
tx_buffer[SHA204_COUNT_IDX] = (uint8_t) (p_command - &tx_buffer[0] + SHA204_CRC_SIZE);
return send_and_receive(&tx_buffer[0], WRITE_RSP_SIZE, &rx_buffer[0],
WRITE_DELAY, WRITE_EXEC_MAX - WRITE_DELAY);
}

67
avr/SHA204/SHA204.h Normal file
View file

@ -0,0 +1,67 @@
/*
Copyright 2013 Nusku Networks
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef SHA204_Library_h
#define SHA204_Library_h
#include <stdint.h>
class SHA204 {
private:
virtual uint16_t SHA204_RESPONSE_TIMEOUT() = 0;
void calculate_crc(uint8_t length, uint8_t *data, uint8_t *crc);
uint8_t check_crc(uint8_t *response);
virtual uint8_t receive_bytes(uint8_t count, uint8_t *buffer) = 0;
virtual uint8_t send_bytes(uint8_t count, uint8_t *buffer) = 0;
virtual uint8_t send_byte(uint8_t value) = 0;
virtual uint8_t receive_response(uint8_t size, uint8_t *response) = 0;
virtual uint8_t send_command(uint8_t count, uint8_t * command) = 0;
virtual uint8_t chip_wakeup() = 0; // Called this because wakeup() was causing method lookup issues with wakeup(*response)
public:
virtual uint8_t sleep() = 0;
virtual uint8_t idle() = 0;
uint8_t wakeup(uint8_t *response);
uint8_t execute(uint8_t op_code, uint8_t param1, uint16_t param2,
uint8_t datalen1, uint8_t *data1, uint8_t datalen2, uint8_t *data2, uint8_t datalen3, uint8_t *data3,
uint8_t tx_size, uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer);
uint8_t check_parameters(uint8_t op_code, uint8_t param1, uint16_t param2,
uint8_t datalen1, uint8_t *data1, uint8_t datalen2, uint8_t *data2, uint8_t datalen3, uint8_t *data3,
uint8_t tx_size, uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer);
uint8_t send_and_receive(uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer, uint8_t execution_delay, uint8_t execution_timeout);
virtual uint8_t resync(uint8_t size, uint8_t *response) = 0;
uint8_t serialNumber(uint8_t *response);
uint8_t check_mac(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t mode, uint8_t key_id, uint8_t *client_challenge, uint8_t *client_response, uint8_t *other_data);
uint8_t derive_key(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t random, uint8_t target_key, uint8_t *mac);
uint8_t dev_rev(uint8_t *tx_buffer, uint8_t *rx_buffer);
uint8_t gen_dig(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint8_t key_id, uint8_t *other_data);
uint8_t hmac(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t mode, uint16_t key_id);
uint8_t lock(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t summary);
uint8_t mac(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t mode, uint16_t key_id, uint8_t *challenge);
uint8_t nonce(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t mode, uint8_t *numin);
uint8_t pause(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t selector);
uint8_t random(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t mode);
uint8_t read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address);
uint8_t update_extra(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t mode, uint8_t new_value);
uint8_t write(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address, uint8_t *value, uint8_t *mac);
};
#endif

View file

@ -0,0 +1,273 @@
/*
Copyright 2013 Nusku Networks
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef SHA204_Library_Definitions_h
#define SHA204_Library_Definitions_h
/* sha204_physical.h */
#define SHA204_RSP_SIZE_MIN ((uint8_t) 4) //!< minimum number of bytes in response
#define SHA204_RSP_SIZE_MAX ((uint8_t) 35) //!< maximum size of response packet
#define SHA204_BUFFER_POS_COUNT (0) //!< buffer index of count byte in command or response
#define SHA204_BUFFER_POS_DATA (1) //!< buffer index of data in response
//#define SHA204_WAKEUP_PULSE_WIDTH (uint8_t) (6.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5) //! width of Wakeup pulse in 10 us units
//#define SHA204_WAKEUP_DELAY (uint8_t) (3.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5) //! delay between Wakeup pulse and communication in ms
#define SHA204_WAKEUP_PULSE_WIDTH 62 // us (Twlo)
#define SHA204_WAKEUP_DELAY 2.5 // ms (Twhi)
/* sha204_comm_marshaling.h */
// command op-code definitions
#define SHA204_CHECKMAC ((uint8_t) 0x28) //!< CheckMac command op-code
#define SHA204_DERIVE_KEY ((uint8_t) 0x1C) //!< DeriveKey command op-code
#define SHA204_DEVREV ((uint8_t) 0x30) //!< DevRev command op-code
#define SHA204_GENDIG ((uint8_t) 0x15) //!< GenDig command op-code
#define SHA204_HMAC ((uint8_t) 0x11) //!< HMAC command op-code
#define SHA204_LOCK ((uint8_t) 0x17) //!< Lock command op-code
#define SHA204_MAC ((uint8_t) 0x08) //!< MAC command op-code
#define SHA204_NONCE ((uint8_t) 0x16) //!< Nonce command op-code
#define SHA204_PAUSE ((uint8_t) 0x01) //!< Pause command op-code
#define SHA204_RANDOM ((uint8_t) 0x1B) //!< Random command op-code
#define SHA204_READ ((uint8_t) 0x02) //!< Read command op-code
#define SHA204_UPDATE_EXTRA ((uint8_t) 0x20) //!< UpdateExtra command op-code
#define SHA204_WRITE ((uint8_t) 0x12) //!< Write command op-code
// packet size definitions
#define SHA204_RSP_SIZE_VAL ((uint8_t) 7) //!< size of response packet containing four bytes of data
// parameter range definitions
#define SHA204_KEY_ID_MAX ((uint8_t) 15) //!< maximum value for key id
#define SHA204_OTP_BLOCK_MAX ((uint8_t) 1) //!< maximum value for OTP block
// definitions for command packet indexes common to all commands
#define SHA204_COUNT_IDX ( 0) //!< command packet index for count
#define SHA204_OPCODE_IDX ( 1) //!< command packet index for op-code
#define SHA204_PARAM1_IDX ( 2) //!< command packet index for first parameter
#define SHA204_PARAM2_IDX ( 3) //!< command packet index for second parameter
#define SHA204_DATA_IDX ( 5) //!< command packet index for second parameter
// zone definitions
#define SHA204_ZONE_CONFIG ((uint8_t) 0x00) //!< Configuration zone
#define SHA204_ZONE_OTP ((uint8_t) 0x01) //!< OTP (One Time Programming) zone
#define SHA204_ZONE_DATA ((uint8_t) 0x02) //!< Data zone
#define SHA204_ZONE_MASK ((uint8_t) 0x03) //!< Zone mask
#define SHA204_ZONE_COUNT_FLAG ((uint8_t) 0x80) //!< Zone bit 7 set: Access 32 bytes, otherwise 4 bytes.
#define SHA204_ZONE_ACCESS_4 ((uint8_t) 4) //!< Read or write 4 bytes.
#define SHA204_ZONE_ACCESS_32 ((uint8_t) 32) //!< Read or write 32 bytes.
#define SHA204_ADDRESS_MASK_CONFIG ( 0x001F) //!< Address bits 5 to 7 are 0 for Configuration zone.
#define SHA204_ADDRESS_MASK_OTP ( 0x000F) //!< Address bits 4 to 7 are 0 for OTP zone.
#define SHA204_ADDRESS_MASK ( 0x007F) //!< Address bit 7 to 15 are always 0.
// CheckMAC command definitions
#define CHECKMAC_MODE_IDX SHA204_PARAM1_IDX //!< CheckMAC command index for mode
#define CHECKMAC_KEYID_IDX SHA204_PARAM2_IDX //!< CheckMAC command index for key identifier
#define CHECKMAC_CLIENT_CHALLENGE_IDX SHA204_DATA_IDX //!< CheckMAC command index for client challenge
#define CHECKMAC_CLIENT_RESPONSE_IDX (37) //!< CheckMAC command index for client response
#define CHECKMAC_DATA_IDX (69) //!< CheckMAC command index for other data
#define CHECKMAC_COUNT (84) //!< CheckMAC command packet size
#define CHECKMAC_MODE_MASK ((uint8_t) 0x27) //!< CheckMAC mode bits 3, 4, 6, and 7 are 0.
#define CHECKMAC_CLIENT_CHALLENGE_SIZE (32) //!< CheckMAC size of client challenge
#define CHECKMAC_CLIENT_RESPONSE_SIZE (32) //!< CheckMAC size of client response
#define CHECKMAC_OTHER_DATA_SIZE (13) //!< CheckMAC size of "other data"
// DeriveKey command definitions
#define DERIVE_KEY_RANDOM_IDX SHA204_PARAM1_IDX //!< DeriveKey command index for random bit
#define DERIVE_KEY_TARGETKEY_IDX SHA204_PARAM2_IDX //!< DeriveKey command index for target slot
#define DERIVE_KEY_MAC_IDX SHA204_DATA_IDX //!< DeriveKey command index for optional MAC
#define DERIVE_KEY_COUNT_SMALL SHA204_CMD_SIZE_MIN //!< DeriveKey command packet size without MAC
#define DERIVE_KEY_COUNT_LARGE (39) //!< DeriveKey command packet size with MAC
#define DERIVE_KEY_RANDOM_FLAG ((uint8_t) 4) //!< DeriveKey 1. parameter
#define DERIVE_KEY_MAC_SIZE (32) //!< DeriveKey MAC size
// DevRev command definitions
#define DEVREV_PARAM1_IDX SHA204_PARAM1_IDX //!< DevRev command index for 1. parameter (ignored)
#define DEVREV_PARAM2_IDX SHA204_PARAM2_IDX //!< DevRev command index for 2. parameter (ignored)
#define DEVREV_COUNT SHA204_CMD_SIZE_MIN //!< DevRev command packet size
// GenDig command definitions
#define GENDIG_ZONE_IDX SHA204_PARAM1_IDX //!< GenDig command index for zone
#define GENDIG_KEYID_IDX SHA204_PARAM2_IDX //!< GenDig command index for key id
#define GENDIG_DATA_IDX SHA204_DATA_IDX //!< GenDig command index for optional data
#define GENDIG_COUNT SHA204_CMD_SIZE_MIN //!< GenDig command packet size without "other data"
#define GENDIG_COUNT_DATA (11) //!< GenDig command packet size with "other data"
#define GENDIG_OTHER_DATA_SIZE (4) //!< GenDig size of "other data"
#define GENDIG_ZONE_CONFIG ((uint8_t) 0) //!< GenDig zone id config
#define GENDIG_ZONE_OTP ((uint8_t) 1) //!< GenDig zone id OTP
#define GENDIG_ZONE_DATA ((uint8_t) 2) //!< GenDig zone id data
// HMAC command definitions
#define HMAC_MODE_IDX SHA204_PARAM1_IDX //!< HMAC command index for mode
#define HMAC_KEYID_IDX SHA204_PARAM2_IDX //!< HMAC command index for key id
#define HMAC_COUNT SHA204_CMD_SIZE_MIN //!< HMAC command packet size
#define HMAC_MODE_MASK ((uint8_t) 0x74) //!< HMAC mode bits 0, 1, 3, and 7 are 0.
// Lock command definitions
#define LOCK_ZONE_IDX SHA204_PARAM1_IDX //!< Lock command index for zone
#define LOCK_SUMMARY_IDX SHA204_PARAM2_IDX //!< Lock command index for summary
#define LOCK_COUNT SHA204_CMD_SIZE_MIN //!< Lock command packet size
#define LOCK_ZONE_NO_CONFIG ((uint8_t) 0x01) //!< Lock zone is OTP or Data
#define LOCK_ZONE_NO_CRC ((uint8_t) 0x80) //!< Lock command: Ignore summary.
#define LOCK_ZONE_MASK (0x81) //!< Lock parameter 1 bits 2 to 6 are 0.
// Mac command definitions
#define MAC_MODE_IDX SHA204_PARAM1_IDX //!< MAC command index for mode
#define MAC_KEYID_IDX SHA204_PARAM2_IDX //!< MAC command index for key id
#define MAC_CHALLENGE_IDX SHA204_DATA_IDX //!< MAC command index for optional challenge
#define MAC_COUNT_SHORT SHA204_CMD_SIZE_MIN //!< MAC command packet size without challenge
#define MAC_COUNT_LONG (39) //!< MAC command packet size with challenge
#define MAC_MODE_BLOCK2_TEMPKEY ((uint8_t) 0x01) //!< MAC mode bit 0: second SHA block from TempKey
#define MAC_MODE_BLOCK1_TEMPKEY ((uint8_t) 0x02) //!< MAC mode bit 1: first SHA block from TempKey
#define MAC_MODE_SOURCE_FLAG_MATCH ((uint8_t) 0x04) //!< MAC mode bit 2: match TempKey.SourceFlag
#define MAC_MODE_PASSTHROUGH ((uint8_t) 0x07) //!< MAC mode bit 0-2: pass-through mode
#define MAC_MODE_INCLUDE_OTP_88 ((uint8_t) 0x10) //!< MAC mode bit 4: include first 88 OTP bits
#define MAC_MODE_INCLUDE_OTP_64 ((uint8_t) 0x20) //!< MAC mode bit 5: include first 64 OTP bits
#define MAC_MODE_INCLUDE_SN ((uint8_t) 0x40) //!< MAC mode bit 6: include serial number
#define MAC_CHALLENGE_SIZE (32) //!< MAC size of challenge
#define MAC_MODE_MASK ((uint8_t) 0x77) //!< MAC mode bits 3 and 7 are 0.
// Nonce command definitions
#define NONCE_MODE_IDX SHA204_PARAM1_IDX //!< Nonce command index for mode
#define NONCE_PARAM2_IDX SHA204_PARAM2_IDX //!< Nonce command index for 2. parameter
#define NONCE_INPUT_IDX SHA204_DATA_IDX //!< Nonce command index for input data
#define NONCE_COUNT_SHORT (27) //!< Nonce command packet size for 20 bytes of data
#define NONCE_COUNT_LONG (39) //!< Nonce command packet size for 32 bytes of data
#define NONCE_MODE_MASK ((uint8_t) 3) //!< Nonce mode bits 2 to 7 are 0.
#define NONCE_MODE_SEED_UPDATE ((uint8_t) 0x00) //!< Nonce mode: update seed
#define NONCE_MODE_NO_SEED_UPDATE ((uint8_t) 0x01) //!< Nonce mode: do not update seed
#define NONCE_MODE_INVALID ((uint8_t) 0x02) //!< Nonce mode 2 is invalid.
#define NONCE_MODE_PASSTHROUGH ((uint8_t) 0x03) //!< Nonce mode: pass-through
#define NONCE_NUMIN_SIZE (20) //!< Nonce data length
#define NONCE_NUMIN_SIZE_PASSTHROUGH (32) //!< Nonce data length in pass-through mode (mode = 3)
// Pause command definitions
#define PAUSE_SELECT_IDX SHA204_PARAM1_IDX //!< Pause command index for Selector
#define PAUSE_PARAM2_IDX SHA204_PARAM2_IDX //!< Pause command index for 2. parameter
#define PAUSE_COUNT SHA204_CMD_SIZE_MIN //!< Pause command packet size
// Random command definitions
#define RANDOM_MODE_IDX SHA204_PARAM1_IDX //!< Random command index for mode
#define RANDOM_PARAM2_IDX SHA204_PARAM2_IDX //!< Random command index for 2. parameter
#define RANDOM_COUNT SHA204_CMD_SIZE_MIN //!< Random command packet size
#define RANDOM_SEED_UPDATE ((uint8_t) 0x00) //!< Random mode for automatic seed update
#define RANDOM_NO_SEED_UPDATE ((uint8_t) 0x01) //!< Random mode for no seed update
// Read command definitions
#define READ_ZONE_IDX SHA204_PARAM1_IDX //!< Read command index for zone
#define READ_ADDR_IDX SHA204_PARAM2_IDX //!< Read command index for address
#define READ_COUNT SHA204_CMD_SIZE_MIN //!< Read command packet size
#define READ_ZONE_MASK ((uint8_t) 0x83) //!< Read zone bits 2 to 6 are 0.
#define READ_ZONE_MODE_32_BYTES ((uint8_t) 0x80) //!< Read mode: 32 bytes
// UpdateExtra command definitions
#define UPDATE_MODE_IDX SHA204_PARAM1_IDX //!< UpdateExtra command index for mode
#define UPDATE_VALUE_IDX SHA204_PARAM2_IDX //!< UpdateExtra command index for new value
#define UPDATE_COUNT SHA204_CMD_SIZE_MIN //!< UpdateExtra command packet size
#define UPDATE_CONFIG_BYTE_86 ((uint8_t) 0x01) //!< UpdateExtra mode: update Config byte 86
// Write command definitions
#define WRITE_ZONE_IDX SHA204_PARAM1_IDX //!< Write command index for zone
#define WRITE_ADDR_IDX SHA204_PARAM2_IDX //!< Write command index for address
#define WRITE_VALUE_IDX SHA204_DATA_IDX //!< Write command index for data
#define WRITE_MAC_VS_IDX ( 9) //!< Write command index for MAC following short data
#define WRITE_MAC_VL_IDX (37) //!< Write command index for MAC following long data
#define WRITE_COUNT_SHORT (11) //!< Write command packet size with short data and no MAC
#define WRITE_COUNT_LONG (39) //!< Write command packet size with long data and no MAC
#define WRITE_COUNT_SHORT_MAC (43) //!< Write command packet size with short data and MAC
#define WRITE_COUNT_LONG_MAC (71) //!< Write command packet size with long data and MAC
#define WRITE_MAC_SIZE (32) //!< Write MAC size
#define WRITE_ZONE_MASK ((uint8_t) 0xC3) //!< Write zone bits 2 to 5 are 0.
#define WRITE_ZONE_WITH_MAC ((uint8_t) 0x40) //!< Write zone bit 6: write encrypted with MAC
// Response size definitions
#define CHECKMAC_RSP_SIZE SHA204_RSP_SIZE_MIN //!< response size of DeriveKey command
#define DERIVE_KEY_RSP_SIZE SHA204_RSP_SIZE_MIN //!< response size of DeriveKey command
#define DEVREV_RSP_SIZE SHA204_RSP_SIZE_VAL //!< response size of DevRev command returns 4 bytes
#define GENDIG_RSP_SIZE SHA204_RSP_SIZE_MIN //!< response size of GenDig command
#define HMAC_RSP_SIZE SHA204_RSP_SIZE_MAX //!< response size of HMAC command
#define LOCK_RSP_SIZE SHA204_RSP_SIZE_MIN //!< response size of Lock command
#define MAC_RSP_SIZE SHA204_RSP_SIZE_MAX //!< response size of MAC command
#define NONCE_RSP_SIZE_SHORT SHA204_RSP_SIZE_MIN //!< response size of Nonce command with mode[0:1] = 3
#define NONCE_RSP_SIZE_LONG SHA204_RSP_SIZE_MAX //!< response size of Nonce command
#define PAUSE_RSP_SIZE SHA204_RSP_SIZE_MIN //!< response size of Pause command
#define RANDOM_RSP_SIZE SHA204_RSP_SIZE_MAX //!< response size of Random command
#define READ_4_RSP_SIZE SHA204_RSP_SIZE_VAL //!< response size of Read command when reading 4 bytes
#define READ_32_RSP_SIZE SHA204_RSP_SIZE_MAX //!< response size of Read command when reading 32 bytes
#define TEMP_SENSE_RSP_SIZE SHA204_RSP_SIZE_VAL //!< response size of TempSense command returns 4 bytes
#define UPDATE_RSP_SIZE SHA204_RSP_SIZE_MIN //!< response size of UpdateExtra command
#define WRITE_RSP_SIZE SHA204_RSP_SIZE_MIN //!< response size of Write command
// command timing definitions for minimum execution times (ms)
#define CHECKMAC_DELAY ((uint8_t) (12.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
#define DERIVE_KEY_DELAY ((uint8_t) (14.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
#define DEVREV_DELAY ((uint8_t) ( 0.4 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
#define GENDIG_DELAY ((uint8_t) (11.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
#define HMAC_DELAY ((uint8_t) (27.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
#define LOCK_DELAY ((uint8_t) ( 5.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
#define MAC_DELAY ((uint8_t) (12.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
#define NONCE_DELAY ((uint8_t) (22.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
#define PAUSE_DELAY ((uint8_t) ( 0.4 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
#define RANDOM_DELAY ((uint8_t) (11.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
#define READ_DELAY ((uint8_t) ( 0.4 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
#define TEMP_SENSE_DELAY ((uint8_t) ( 4.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
#define UPDATE_DELAY ((uint8_t) ( 4.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
#define WRITE_DELAY ((uint8_t) ( 4.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
// command timing definitions for maximum execution times (ms)
#define CHECKMAC_EXEC_MAX ((uint8_t) (38.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
#define DERIVE_KEY_EXEC_MAX ((uint8_t) (62.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
#define DEVREV_EXEC_MAX ((uint8_t) ( 2.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
#define GENDIG_EXEC_MAX ((uint8_t) (43.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
#define HMAC_EXEC_MAX ((uint8_t) (69.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
#define LOCK_EXEC_MAX ((uint8_t) (24.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
#define MAC_EXEC_MAX ((uint8_t) (35.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
#define NONCE_EXEC_MAX ((uint8_t) (60.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
#define PAUSE_EXEC_MAX ((uint8_t) ( 2.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
#define RANDOM_EXEC_MAX ((uint8_t) (50.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
#define READ_EXEC_MAX ((uint8_t) ( 4.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
#define TEMP_SENSE_EXEC_MAX ((uint8_t) (11.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
#define UPDATE_EXEC_MAX ((uint8_t) ( 6.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
#define WRITE_EXEC_MAX ((uint8_t) (42.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
/* from sha204_comm.h */
#define SHA204_COMMAND_EXEC_MAX ((uint8_t) (69.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5)) //! maximum command delay
#define SHA204_CMD_SIZE_MIN ((uint8_t) 7) //! minimum number of bytes in command (from count byte to second CRC byte)
#define SHA204_CMD_SIZE_MAX ((uint8_t) 84) //! maximum size of command packet (CheckMac)
#define SHA204_CRC_SIZE ((uint8_t) 2) //! number of CRC bytes
#define SHA204_BUFFER_POS_STATUS (1) //! buffer index of status byte in status response
#define SHA204_BUFFER_POS_DATA (1) //! buffer index of first data byte in data response
#define SHA204_STATUS_BYTE_WAKEUP ((uint8_t) 0x11) //! command parse error
#define SHA204_STATUS_BYTE_PARSE ((uint8_t) 0x03) //! command parse error
#define SHA204_STATUS_BYTE_EXEC ((uint8_t) 0x0F) //! command execution error
#define SHA204_STATUS_BYTE_COMM ((uint8_t) 0xFF) //! communication error
/* EEPROM Addresses */
/* Configuration Zone */
#define ADDRESS_SN03 0 // SN[0:3] are bytes 0->3 of configuration zone
#define ADDRESS_RevNum 4 // bytes 4->7 of config zone are RevNum
#define ADDRESS_SN47 8 // SN[4:7] are bytes 8->11 of config zone
#define ADDRESS_SN8 12 // SN[8] is byte 12 of config zone, should be 0xEE
#define ADDRESS_I2CEN 14 // I2C Enable, bit 0 represents I2C enable status
#define ADDRESS_I2CADD 16 // Defines I2C address of SHA204
#define ADDRESS_OTPMODE 18 // Sets the One-time-programmable mode
#define ADDRESS_SELECTOR 19 // Controls writability of Selector
/* from sha204_config.h */
#define CPU_CLOCK_DEVIATION_POSITIVE (1.01)
#define CPU_CLOCK_DEVIATION_NEGATIVE (0.99)
#define SHA204_RETRY_COUNT (1)
#endif

View file

@ -0,0 +1,48 @@
// ----------------------------------------------------------------------------
// ATMEL Microcontroller Software Support - Colorado Springs, CO -
// ----------------------------------------------------------------------------
// DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
// DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// ----------------------------------------------------------------------------
/** \file
* \brief SHA204 Library Return Code Definitions
* \author Atmel Crypto Products
* \date September 27, 2010
*/
#ifndef SHA204_LIB_RETURN_CODES_H
# define SHA204_LIB_RETURN_CODES_H
#include <stddef.h> // data type definitions
/** \todo Use same values for same meanings for SHA204 and AES132.
* */
#define SHA204_SUCCESS ((uint8_t) 0x00) //!< Function succeeded.
#define SHA204_PARSE_ERROR ((uint8_t) 0xD2) //!< response status byte indicates parsing error
#define SHA204_CMD_FAIL ((uint8_t) 0xD3) //!< response status byte indicates command execution error
#define SHA204_STATUS_CRC ((uint8_t) 0xD4) //!< response status byte indicates CRC error
#define SHA204_STATUS_UNKNOWN ((uint8_t) 0xD5) //!< response status byte is unknown
#define SHA204_FUNC_FAIL ((uint8_t) 0xE0) //!< Function could not execute due to incorrect condition / state.
#define SHA204_GEN_FAIL ((uint8_t) 0xE1) //!< unspecified error
#define SHA204_BAD_PARAM ((uint8_t) 0xE2) //!< bad argument (out of range, null pointer, etc.)
#define SHA204_INVALID_ID ((uint8_t) 0xE3) //!< invalid device id, id not set
#define SHA204_INVALID_SIZE ((uint8_t) 0xE4) //!< Count value is out of range or greater than buffer size.
#define SHA204_BAD_CRC ((uint8_t) 0xE5) //!< incorrect CRC received
#define SHA204_RX_FAIL ((uint8_t) 0xE6) //!< Timed out while waiting for response. Number of bytes received is > 0.
#define SHA204_RX_NO_RESPONSE ((uint8_t) 0xE7) //!< Not an error while the Command layer is polling for a command response.
#define SHA204_RESYNC_WITH_WAKEUP ((uint8_t) 0xE8) //!< re-synchronization succeeded, but only after generating a Wake-up
#define SHA204_COMM_FAIL ((uint8_t) 0xF0) //!< Communication with device failed. Same as in hardware dependent modules.
#define SHA204_TIMEOUT ((uint8_t) 0xF1) //!< Timed out while waiting for response. Number of bytes received is 0.
#endif

256
avr/SHA204/SHA204SWI.cpp Normal file
View file

@ -0,0 +1,256 @@
/*
* SHA204SWI.c
* (c) 2014 flabbergast
* Communicate with ATSHA204 using Single-Wire Interface:
* Main file: extends the generic SHA204 class and implements the hardware protocol.
*
* Most of the code is from https://github.com/bettse/arduino_projects/tree/master/libraries/SHA204
* (license below?)
*
* Modifications by flabbergast to run on XMEGA hardware + some "simplifications" in timing.
*/
/*
Copyright 2013 Nusku Networks
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "SHA204.h"
#include "SHA204ReturnCodes.h"
#include "SHA204Definitions.h"
#include "SHA204SWI.h"
#include <util/delay.h>
#include <avr/interrupt.h>
uint16_t SHA204SWI::SHA204_RESPONSE_TIMEOUT() {
return SHA204_RESPONSE_TIMEOUT_VALUE;
}
// atsha204Class Constructor
// nothing to do (pin settings via defines)
SHA204SWI::SHA204SWI() {
}
// power up (powered from appropriate pin)
void SHA204SWI::power_up(void) {
S_POWER_UP;
}
/* SWI bit bang functions */
uint8_t SHA204SWI::chip_wakeup() {
S_PIN_DIR_OUT;
S_PIN_LOW;
_delay_us(SHA204_WAKEUP_PULSE_WIDTH);
S_PIN_HIGH;
_delay_ms(SHA204_WAKEUP_DELAY);
return SHA204_SUCCESS;
}
uint8_t SHA204SWI::sleep() {
return send_byte(SHA204_SWI_FLAG_SLEEP);
}
uint8_t SHA204SWI::idle() {
return send_byte(SHA204_SWI_FLAG_IDLE);
}
uint8_t SHA204SWI::resync(uint8_t size, uint8_t *response) {
// Try to re-synchronize without sending a Wake token
// (step 1 of the re-synchronization process).
_delay_ms(SHA204_SYNC_TIMEOUT);
uint8_t ret_code = receive_response(size, response);
if (ret_code == SHA204_SUCCESS)
return ret_code;
// We lost communication. Send a Wake pulse and try
// to receive a response (steps 2 and 3 of the
// re-synchronization process).
(void) sleep();
ret_code = wakeup(response);
// Translate a return value of success into one
// that indicates that the device had to be woken up
// and might have lost its TempKey.
return (ret_code == SHA204_SUCCESS ? SHA204_RESYNC_WITH_WAKEUP : ret_code);
}
uint8_t SHA204SWI::send_bytes(uint8_t count, uint8_t *buffer) {
uint8_t i, bit_mask;
// Disable interrupts while sending.
cli();
S_PIN_DIR_OUT;
// Wait turn around time.
_delay_us(RX_TX_DELAY);
for (i = 0; i < count; i++) {
for (bit_mask = 1; bit_mask > 0; bit_mask <<= 1) {
if (bit_mask & buffer[i]) {
S_PIN_LOW;
_delay_us(BIT_DELAY); //BIT_DELAY_1;
S_PIN_HIGH;
_delay_us(7*BIT_DELAY); //BIT_DELAY_7;
} else {
// Send a zero bit.
S_PIN_LOW;
_delay_us(BIT_DELAY); //BIT_DELAY_1;
S_PIN_HIGH;
_delay_us(BIT_DELAY); //BIT_DELAY_1;
S_PIN_LOW;
_delay_us(BIT_DELAY); //BIT_DELAY_1;
S_PIN_HIGH;
_delay_us(5*BIT_DELAY); //BIT_DELAY_5;
}
_delay_us(2); // since 8*BIT_DELAY < 37 us (datasheet / Table 7-3)
}
}
sei(); // enable_interrupts();
return SWI_FUNCTION_RETCODE_SUCCESS;
}
uint8_t SHA204SWI::send_byte(uint8_t value) {
return send_bytes(1, &value);
}
uint8_t SHA204SWI::receive_bytes(uint8_t count, uint8_t *buffer) {
uint8_t status = SWI_FUNCTION_RETCODE_SUCCESS;
uint8_t i;
uint8_t bit_mask;
uint8_t pulse_count;
uint16_t timeout_count;
// Disable interrupts while receiving.
cli();
// Configure signal pin as input.
S_PIN_DIR_IN;
// Receive bits and store in buffer.
for (i = 0; i < count; i++) {
for (bit_mask = 1; bit_mask > 0; bit_mask <<= 1) {
pulse_count = 0;
// Make sure that the variable below is big enough.
// Change it to uint16_t if 255 is too small, but be aware that
// the loop resolution decreases on an 8-bit controller in that case.
timeout_count = START_PULSE_TIME_OUT;
// Detect start bit.
while (--timeout_count > 0) {
// Wait for falling edge.
if ((S_PIN_IS_HIGH) == 0)
break;
}
if (timeout_count == 0) {
status = SWI_FUNCTION_RETCODE_TIMEOUT;
break;
}
do {
// Wait for rising edge.
if (S_PIN_IS_HIGH) {
// For an Atmel microcontroller this might be faster than "pulse_count++".
pulse_count = 1;
break;
}
} while (--timeout_count > 0);
if (pulse_count == 0) {
status = SWI_FUNCTION_RETCODE_TIMEOUT;
break;
}
// Trying to measure the time of start bit and calculating the timeout
// for zero bit detection is not accurate enough for an 8 MHz 8-bit CPU.
// (NB by flabbergast: running now on 32MHz XMEGA. so maybe...)
// So let's just wait the maximum time for the falling edge of a zero bit
// to arrive after we have detected the rising edge of the start bit.
timeout_count = ZERO_PULSE_TIME_OUT;
// Detect possible edge indicating zero bit.
do {
if ((S_PIN_IS_HIGH) == 0) {
// For an Atmel microcontroller this might be faster than "pulse_count++".
pulse_count = 2;
break;
}
} while (--timeout_count > 0);
// Wait for rising edge of zero pulse before returning. Otherwise we might interpret
// its rising edge as the next start pulse.
if (pulse_count == 2) {
do {
if (S_PIN_IS_HIGH)
break;
} while (timeout_count-- > 0);
}
// Update byte at current buffer index.
else
buffer[i] |= bit_mask; // received "one" bit
}
if (status != SWI_FUNCTION_RETCODE_SUCCESS)
break;
}
sei(); // enable_interrupts();
if (status == SWI_FUNCTION_RETCODE_TIMEOUT) {
if (i > 0)
// Indicate that we timed out after having received at least one byte.
status = SWI_FUNCTION_RETCODE_RX_FAIL;
}
return status;
}
uint8_t SHA204SWI::receive_response(uint8_t size, uint8_t *response) {
uint8_t count_byte;
uint8_t i;
uint8_t ret_code;
for (i = 0; i < size; i++)
response[i] = 0;
(void) send_byte(SHA204_SWI_FLAG_TX);
ret_code = receive_bytes(size, response);
if (ret_code == SWI_FUNCTION_RETCODE_SUCCESS || ret_code == SWI_FUNCTION_RETCODE_RX_FAIL) {
count_byte = response[SHA204_BUFFER_POS_COUNT];
if ((count_byte < SHA204_RSP_SIZE_MIN) || (count_byte > size))
return SHA204_INVALID_SIZE;
return SHA204_SUCCESS;
}
// Translate error so that the Communication layer
// can distinguish between a real error or the
// device being busy executing a command.
if (ret_code == SWI_FUNCTION_RETCODE_TIMEOUT)
return SHA204_RX_NO_RESPONSE;
else
return SHA204_RX_FAIL;
}
uint8_t SHA204SWI::send_command(uint8_t count, uint8_t * command) {
uint8_t ret_code = send_byte(SHA204_SWI_FLAG_CMD);
if (ret_code != SWI_FUNCTION_RETCODE_SUCCESS)
return SHA204_COMM_FAIL;
return send_bytes(count, command);
}

92
avr/SHA204/SHA204SWI.h Normal file
View file

@ -0,0 +1,92 @@
/*
* SHA204SWI.h
* (c) 2014 flabbergast
* Communicate with ATSHA204 using Single-Wire Interface:
* Main header file.
*
* Most of the code is from https://github.com/bettse/arduino_projects/tree/master/libraries/SHA204
* (license below?)
*
* Modifications by flabbergast to run on XMEGA hardware + some "simplifications" in timing.
*/
/*
Copyright 2013 Nusku Networks
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef SHA204_Library_SWI_h
#define SHA204_Library_SWI_h
#include "SHA204.h"
#include "SHA204SWI_hardware_config.h"
/* bitbang_config.h */
//#define PORT_ACCESS_TIME (630) //! time it takes to toggle the pin at CPU clock of 16 MHz (ns)
//#define START_PULSE_WIDTH (4340) //! width of start pulse (ns)
//#define BIT_DELAY (4) //! delay macro for width of one pulse (start pulse or zero pulse, in ns)
//#define RX_TX_DELAY (15) //! turn around time when switching from receive to transmit
#if (F_CPU == 32000000)
#define START_PULSE_TIME_OUT (510) //! This value is decremented while waiting for the falling edge of a start pulse.
#define ZERO_PULSE_TIME_OUT (52) //! This value is decremented while waiting for the falling edge of a zero pulse.
#elif (F_CPU == 16000000)
#define START_PULSE_TIME_OUT (255) //! This value is decremented while waiting for the falling edge of a start pulse.
#define ZERO_PULSE_TIME_OUT (26) //! This value is decremented while waiting for the falling edge of a zero pulse.
#else
#error "You need to define some constants for your F_CPU speed!"
#endif
// my defs
#define BIT_DELAY 4 // us (zero/one transmission pulse length)
#define RX_TX_DELAY 93 // us (turnaround time from receive to transmit)
/* swi_phys.h */
#define SWI_FUNCTION_RETCODE_SUCCESS ((uint8_t) 0x00) //!< Communication with device succeeded.
#define SWI_FUNCTION_RETCODE_TIMEOUT ((uint8_t) 0xF1) //!< Communication timed out.
#define SWI_FUNCTION_RETCODE_RX_FAIL ((uint8_t) 0xF9) //!< Communication failed after at least one byte was received.
/* sha204_swi.c */
#define SHA204_SWI_FLAG_CMD ((uint8_t) 0x77) //!< flag preceding a command
#define SHA204_SWI_FLAG_TX ((uint8_t) 0x88) //!< flag requesting a response
#define SHA204_SWI_FLAG_IDLE ((uint8_t) 0xBB) //!< flag requesting to go into Idle mode
#define SHA204_SWI_FLAG_SLEEP ((uint8_t) 0xCC) //!< flag requesting to go into Sleep mode
/* from sha204_config.h */
#define SWI_RECEIVE_TIME_OUT ((uint16_t) 163) //! #START_PULSE_TIME_OUT in us instead of loop counts
#define SWI_US_PER_BYTE ((uint16_t) 313) //! It takes 312.5 us to send a byte (9 single-wire bits / 230400 Baud * 8 flag bits).
#define SHA204_SYNC_TIMEOUT ((uint8_t) 85)//! delay before sending a transmit flag in the synchronization routine
class SHA204SWI : public SHA204 {
private:
const static uint16_t SHA204_RESPONSE_TIMEOUT_VALUE = ((uint16_t) SWI_RECEIVE_TIME_OUT + SWI_US_PER_BYTE); //! SWI response timeout is the sum of receive timeout and the time it takes to send the TX flag.
uint16_t SHA204_RESPONSE_TIMEOUT();
uint8_t receive_bytes(uint8_t count, uint8_t *buffer);
uint8_t send_bytes(uint8_t count, uint8_t *buffer);
uint8_t send_byte(uint8_t value);
uint8_t chip_wakeup();
uint8_t receive_response(uint8_t size, uint8_t *response);
uint8_t send_command(uint8_t count, uint8_t * command);
public:
SHA204SWI(void);
void power_up();
uint8_t sleep();
uint8_t idle();
uint8_t resync(uint8_t size, uint8_t *response);
};
#endif

View file

@ -0,0 +1,74 @@
/*
* SHA204SWI_hardware_config.h
* (c) 2014 flabbergast
* Define macros to be used for Single Wire bit-bang communication *
*
* EDIT THIS FILE TO MATCH YOUR HARDWARE CONFIG!
*
*/
#ifndef SHA204SWI_hardware_config_h
#define SHA204SWI_hardware_config_h
// Note: pull-up on the signal pin is assumed to exist (ie internal pullup is not used)
/*************************\
**** FOR XMEGA CHIPS ****
\*************************/
#if (defined(__AVR_ATxmega128A3U__))
// Modify below!
// -> If you use actual GND and VCC pins for power, ignore the _VCC_ and _GND_ settings
// and redefine the S_POWER_UP macro to be blank
#define SHA204_PORT PORTB
#define SHA204_BIT (1 << 2)
#define SHA204_GND_PORT PORTB
#define SHA204_GND_BIT (1 << 3)
#define SHA204_VCC_PORT PORTB
#define SHA204_VCC_BIT (1 << 1)
// Choose the appropriate S_POWER_UP macro according to your power setup
// #define S_POWER_UP {} // use this if using actual GND and VCC supply
#define S_POWER_UP SHA204_GND_PORT.DIRSET = SHA204_GND_BIT; SHA204_GND_PORT.OUTCLR = SHA204_GND_BIT; SHA204_VCC_PORT.DIRSET = SHA204_VCC_BIT; SHA204_VCC_PORT.OUTSET = SHA204_VCC_BIT // use this if powering from PORT pins
// Leave the following code alone
#define S_PIN_DIR_OUT SHA204_PORT.DIRSET = SHA204_BIT
#define S_PIN_DIR_IN SHA204_PORT.DIRCLR = SHA204_BIT
#define S_PIN_HIGH SHA204_PORT.OUTSET = SHA204_BIT
#define S_PIN_LOW SHA204_PORT.OUTCLR = SHA204_BIT
#define S_PIN_IS_HIGH SHA204_PORT.IN & SHA204_BIT
/********************************\
**** FOR AVR8/Arduino CHIPS ****
\********************************/
#else
// Modify below!
// -> If you use actual GND and VCC pins for power, ignore the _VCC_ and _GND_ settings
// and redefine the S_POWER_UP macro to be blank
// -> Have a look at http://arduino.cc/en/Hacking/PinMapping168 to find out
// the correct port/bit from an Arduino pin number (just need to change the
// last letters and the bit, e.g. Arduino's digital pin 7 is "PD7", so one
// would use PORTD, PIND, DDRD and the _BIT would be set to (1<<7).
#define SHA204_PORT PORTB
#define SHA204_PIN PINB
#define SHA204_DDR DDRB
#define SHA204_BIT (1<<1)
#define SHA204_GND_PORT PORTB
#define SHA204_GND_DDR DDRB
#define SHA204_GND_BIT (1 << 0)
#define SHA204_VCC_PORT PORTB
#define SHA204_VCC_DDR DDRB
#define SHA204_VCC_BIT (1 << 2)
// Choose the appropriate S_POWER_UP macro according to your power setup
#define S_POWER_UP {} // use this if using actual GND and VCC supply
// #define S_POWER_UP SHA204_GND_DDR|=SHA204_GND_BIT; SHA204_GND_PORT&=~SHA204_GND_BIT; SHA204_VCC_DDR|=SHA204_VCC_BIT; SHA204_VCC_PORT|=SHA204_VCC_BIT; // use this if powering from PORT pins
// Leave the following code alone
#define S_PIN_DIR_OUT SHA204_DDR |= SHA204_BIT
#define S_PIN_DIR_IN SHA204_DDR &= ~SHA204_BIT
#define S_PIN_HIGH SHA204_PORT |= SHA204_BIT
#define S_PIN_LOW SHA204_PORT &= ~SHA204_BIT
#define S_PIN_IS_HIGH SHA204_PIN & SHA204_BIT
#endif
#endif

84
avr/Timer.c Normal file
View file

@ -0,0 +1,84 @@
/*
* Timer.c
* (c) 2014 flabbergast
* implements Arduino-like millis() function via RTC timer interrupt (on XMEGAs)
* Note: the XMEGA version counts in 10/1024 secs, so not exactly tens of milliseconds (2.4% error :)
* Note: the AVR8 version counts in 10.24 millisecs (wrong the other way than XMEGA :)
*
* Credits:
* - XMEGA code from: http://www.jtronics.de/avr-projekte/xmega-tutorial/xmega-tutorial-real-time-counter.html
*/
#include <avr/interrupt.h>
#include "Timer.h"
volatile uint32_t current_time;
#if (defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega32U2__)) // use TIMER0 compare interrupt to keep track of time
volatile uint8_t helper_counter;
void Timer_Init(void) {
TCCR0B |= (1 << CS00)|(1 << CS01); // prescaler F_CPU/64
TCNT0 = 0; // initalize the counter
helper_counter = 0;
TIMSK0 |= (1 << TOIE0); // enable TIMER0 overflow interrupt (fires every 256 prescaled cycles)
// altogether the int fires every 1.024 ms on F_CPU=16MHz and 2.048 ms on F_CPU=8MHz
}
#if (F_CPU == 16000000)
#define TIMER_HELPER_CONSTANT 10
#elif (F_CPU == 8000000)
#define TIMER_HELPER_CONSTANT 5
#else
#error "Unusual F_CPU: you should define some constants in Timer.c."
#endif
// TIMER0 overflow interrupt handler
ISR(TIMER0_OVF_vect) {
helper_counter++;
if(helper_counter>=TIMER_HELPER_CONSTANT) {
current_time++;
helper_counter = 0;
}
}
#elif (defined(__AVR_ATxmega128A3U__)) // use internal RTC oscillator to generate interrupts
void Timer_Init(void) {
current_time = 0;
//############################### Clock für RTC aktivieren
// Unlock access to protected IO register for 4 cycles
CCP = CCP_IOREG_gc;
// Internen RTC 32768KHz Oszillator aktivieren
OSC.CTRL |= OSC_RC32KEN_bm;
// Internen Oszillator mit 1024khz für RTC verwenden
CLK.RTCCTRL = CLK_RTCSRC_RCOSC_gc|CLK_RTCEN_bm;
// Do not prescale (fire 1024 times / second)
RTC.CTRL = RTC_PRESCALER_DIV1_gc;
// Warten bis Takt und RTC synchronisiert ist
while(RTC.STATUS & RTC_SYNCBUSY_bm);
//############################### RTC --> 1Hz
//Timertopwert einstellen
RTC.PER = 10; // fire an interrupt every 10 / 1024 sec
//Timeroverflow Interrupt mit Interrupt Priorität Hoch einstellen
RTC.INTCTRL |= RTC_OVFINTLVL_HI_gc;
//Timerregister CNT auf 0 stellen
RTC.CNT = 0;
//RTC.COMP = 2; // note: if COMP>PER, no 'compare' interrupt will ever be generated
}
//################################################## ISR RTC 1Hz
ISR(RTC_OVF_vect) {
current_time++;
// testing: toggle LED on E0 approx every 1 sec
//if(0 == (current_time % 102))
// PORTE.OUTTGL = 1;
}
#else
#error "You should define some timer in Timer.c for your ATMEL chip."
#endif
uint32_t millis10(void) {
return current_time;
}

13
avr/Timer.h Normal file
View file

@ -0,0 +1,13 @@
/*
* Timer.h
* (c) 2014 flabbergast
* implements Arduino-like millis() function via RTC timer interrupt (on XMEGAs)
*/
#ifndef _PROJECT_TIMER_H_
#define _PROJECT_TIMER_H_
void Timer_Init(void);
uint32_t millis10(void);
#endif

50
avr/makefile Normal file
View file

@ -0,0 +1,50 @@
#
# LUFA Library
# Copyright (C) Dean Camera, 2014.
#
# dean [at] fourwalledcubicle [dot] com
# www.lufa-lib.org
#
# --------------------------------------
# LUFA Project Makefile.
# --------------------------------------
# Run "make help" for target help.
## Boards
# xmega avr stick
#MCU = atxmega128a3u
#ARCH = XMEGA
#BOARD = USER
#F_CPU = 32000000
#F_USB = 48000000
# atmega32u4 stick
MCU = atmega32u4
ARCH = AVR8
BOARD = OLIMEX32U4
F_CPU = 16000000
F_USB = $(F_CPU)
AVRDUDE_PROGRAMMER = avr109
AVRDUDE_PORT = /dev/tty.usbmodemfd111
# Compile setting
OPTIMIZATION = s
TARGET = sha204_playground
SRC = $(TARGET).cpp LufaLayer.c Descriptors.c Timer.c $(shell find "SHA204" -name "*.cpp") $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS)
LUFA_PATH = LUFA
CC_FLAGS = -DUSE_LUFA_CONFIG_HEADER -IConfig/
LD_FLAGS =
# Default target
all:
# Include LUFA build script makefiles
include $(LUFA_PATH)/Build/lufa_core.mk
include $(LUFA_PATH)/Build/lufa_sources.mk
include $(LUFA_PATH)/Build/lufa_build.mk
include $(LUFA_PATH)/Build/lufa_cppcheck.mk
include $(LUFA_PATH)/Build/lufa_doxygen.mk
include $(LUFA_PATH)/Build/lufa_dfu.mk
include $(LUFA_PATH)/Build/lufa_hid.mk
include $(LUFA_PATH)/Build/lufa_avrdude.mk
include $(LUFA_PATH)/Build/lufa_atprogram.mk

807
avr/sha204_playground.cpp Normal file
View file

@ -0,0 +1,807 @@
/*
* sha204_playground.cpp
* (c) 2014 flabbergast
*
* Firmware for USB capable ATMEL chips (tested on atmega32U4 and
* atxmega128a3u) to communicate with ATSHA204 (single-wire
* interface) interactively, over Serial.
*/
#include "LufaLayer.h"
#include "SHA204/SHA204SWI.h"
#include "SHA204/SHA204Definitions.h" // for constants and such
#include "SHA204/SHA204ReturnCodes.h" // want messages for return codes
/*************************************************************************
* ----------------------- Global variables -----------------------------*
*************************************************************************/
// when this byte is received, switch to binary mode
#define BINARY_MODE_CHAR 0xFD
#define MAX_BUFFER_SIZE 100
volatile uint8_t hexprint_separator = ' ';
volatile uint8_t idle = 0;
/*************************************************************************
* ----------------------- Helper functions -----------------------------*
*************************************************************************/
void hexprint(uint8_t *p, uint16_t length);
void hexprint_noln(uint8_t *p, uint16_t length);
void hexprint_byte(uint8_t b);
void hexprint_byte_sep(uint8_t b);
#define W(s) usb_serial_write_P(PSTR(s))
#define Wl(s) usb_serial_writeln_P(PSTR(s))
uint8_t get_bytes_serial(uint8_t *output, uint16_t len);
void print_help(void);
void print_executing(void);
void print_received_from_sha(uint8_t *rx_buffer);
void print_execute_params(uint8_t opcode, uint8_t param1);
void print_return_code(uint8_t code);
void process_config(uint8_t *config);
void sleep_or_idle(SHA204SWI *sha204);
uint8_t receive_serial_binary_transaction(uint8_t *buffer, uint8_t len);
uint8_t binary_mode_transaction(uint8_t *data, uint8_t rxsize, uint8_t *rx_buffer, SHA204SWI *sha204);
#define BINARY_TRANSACTION_OK 0
#define BINARY_TRANSACTION_RECEIVE_ERROR 1
#define BINARY_TRANSACTION_PARAM_ERROR 2
#define BINARY_TRANSACTION_EXECUTE_ERROR 3
/** Main program entry point. This routine contains the overall program flow, including initial
* setup of all components and the main program loop.
*/
int main(void)
{
/* Variables */
bool button_press_registered = false;
uint32_t button_press_length = 0;
bool dtr,prev_dtr = false;
uint8_t rx_buffer[SHA204_RSP_SIZE_MAX];
uint8_t tx_buffer[MAX_BUFFER_SIZE];
uint8_t configuration_zone[88];
uint8_t r;
uint8_t param1;
uint16_t param2;
uint8_t data1[32];
uint8_t data2[32];
uint8_t data3[14];
SHA204SWI sha204;
sha204.power_up();
/* Initialisation */
init();
/* Must throw away unused bytes from the host, or it will lock up while waiting for the device */
usb_serial_flush_input();
for (;;)
{
// run the task which checks the state of the button
service_button();
// for how long was the button pressed?
button_press_length = button_pressed_for();
// if at least 70ms and we haven't acted on this press yet
if( button_press_length >= 7 && !button_press_registered ) {
// remember that we've acted on the current button press
button_press_registered = true;
// announce the button press over the serial
Wl("Button pressed.");
// maybe do something else...
}
// was the button released after being pressed?
if( button_press_registered && button_press_length < 7 ) {
button_press_registered = false;
}
// check dtr
dtr = usb_serial_dtr();
if( dtr && !prev_dtr ) {
_delay_ms(50);
W("\n\r");
print_help();
}
prev_dtr = dtr;
// main serial processing
if(usb_serial_available() > 0) {
char c = (char)usb_serial_getchar();
if(c==BINARY_MODE_CHAR) { // handle binary mode for one transaction
r = receive_serial_binary_transaction(tx_buffer, MAX_BUFFER_SIZE); // blocking
if(r == BINARY_TRANSACTION_OK)
r = binary_mode_transaction(tx_buffer, SHA204_RSP_SIZE_MAX, rx_buffer, &sha204); // blocking
// transmit the response
usb_serial_putchar(r);
if(r == BINARY_TRANSACTION_OK)
for(r=0; r<rx_buffer[0]; r++)
usb_serial_putchar(rx_buffer[r]);
} else {
if(idle)
Wl("--- I ---");
else
Wl("--- S ---");
switch(c) { // yes, code like this sucks
case 's': // serial number
Wl("Request serial number.");
sha204.wakeup(rx_buffer);
r = sha204.serialNumber(rx_buffer);
print_executing();
print_return_code(r);
Wl("Should get: 01 23 xx xx xx xx xx xx EE");
W("Received SN: ");
hexprint(rx_buffer, 9);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
break;
case 'o': // config zone
Wl("Request and display config zone contents.");
print_executing();
memset(configuration_zone,0,32);
if(!(r=sha204.wakeup(rx_buffer))) { // read first 2 32-byte blocks manually
if(!(r=sha204.read(tx_buffer,rx_buffer,SHA204_ZONE_CONFIG|READ_ZONE_MODE_32_BYTES,0))) {
memcpy(configuration_zone,rx_buffer+1,32);
if(!(r=sha204.read(tx_buffer,rx_buffer,SHA204_ZONE_CONFIG|READ_ZONE_MODE_32_BYTES,32))) {
memcpy(configuration_zone+32, rx_buffer+1, 32);
uint8_t addr = 64; // have to read the rest of the zone in 4-byte blocks
while(addr < 88) {
if((r=sha204.read(tx_buffer,rx_buffer,SHA204_ZONE_CONFIG,addr)))
break;
memcpy(configuration_zone+addr, rx_buffer+1, 4);
addr+=4;
}
}
}
}
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
if(!r)
process_config(configuration_zone);
break;
case 'c': // check_mac
Wl("Calculate and compare MAC (CheckMAC command).");
Wl("Enter mode (1 byte; default 0):");
param1 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param1 = tx_buffer[0];
Wl("Enter Slot ID (1 byte; default 0):");
param2 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param2 = tx_buffer[0];
Wl("Enter client challenge (32 bytes; default 0):");
memset((void *)data1, 0, 32);
get_bytes_serial(data1, 32);
Wl("Enter client response (32 bytes; default 0):");
memset((void *)data2, 0, 32);
get_bytes_serial(data2, 32);
Wl("Enter other data (13 bytes; default 0):");
memset((void *)data3, 0, 13);
get_bytes_serial(data3, 13);
print_execute_params(SHA204_CHECKMAC,param1);
hexprint_byte_sep((uint8_t)param2);
W("data1 ");
hexprint(data1, 32);
W("data2 ");
hexprint(data2, 32);
W("data3 ");
hexprint(data3, 13);
print_executing();
sha204.wakeup(rx_buffer);
r = sha204.check_mac(tx_buffer, rx_buffer, param1, param2, data1, data2, data3);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
print_received_from_sha(rx_buffer);
break;
case 'd': // derive_key
Wl("Combine current key with nonce and store in a key slot (DeriveKey command).");
Wl("Enter random (1 byte: 00 or 04; default 0):");
param1 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param1 = tx_buffer[0];
Wl("Enter Target Slot ID (1 byte; default 0):");
param2 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param2 = tx_buffer[0];
Wl("Enter MAC for validation (0 or 32 bytes; default 0):");
memset((void *)data1, 0, 32);
get_bytes_serial(data1, 32);
print_execute_params(SHA204_DERIVE_KEY,param1);
hexprint_byte_sep((uint8_t)param2);
W("data1 ");
hexprint(data1, 32);
print_executing();
sha204.wakeup(rx_buffer);
r = sha204.derive_key(tx_buffer, rx_buffer, param1, param2, data1);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
print_received_from_sha(rx_buffer);
break;
case 'v': // dev_rev
Wl("Request device revision.");
sha204.wakeup(rx_buffer);
r = sha204.dev_rev(tx_buffer, rx_buffer);
print_executing();
print_return_code(r);
print_received_from_sha(rx_buffer);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
break;
case 'g': // gen_dig
Wl("Compute SHA-256 from TempKey+stored value, store result in TempKey (GenDig command).");
Wl("Enter zone (1 byte: 00/Config or 01/OTP or 02/Data; default 0):");
param1 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param1 = tx_buffer[0];
Wl("Enter Key ID / OTP slot ID (1 byte; default 0):");
param2 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param2 = tx_buffer[0];
Wl("Enter other data (4 bytes when CheckKey, otherwise ignored; default 0):");
memset((void *)data1, 0, 4);
get_bytes_serial(data1, 4);
print_execute_params(SHA204_GENDIG,param1);
hexprint_byte_sep((uint8_t)param2);
W("data1 ");
hexprint(data1, 4);
print_executing();
sha204.wakeup(rx_buffer);
r = sha204.gen_dig(tx_buffer, rx_buffer, param1, param2, data1);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
print_received_from_sha(rx_buffer);
break;
case 'h': // HMAC
Wl("Compute HMAC/SHA-256 digest from key + other info on device (HMAC command).");
Wl("Enter mode (1 byte; default 0):");
param1 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param1 = tx_buffer[0];
Wl("Enter Slot ID (2 bytes; default 0):");
param2 = 0;
if(2 == get_bytes_serial(tx_buffer, 2))
param2 = tx_buffer[0]*256 + tx_buffer[1];
print_execute_params(SHA204_HMAC,param1);
hexprint_byte_sep((uint8_t)(param2>>8));
hexprint_byte((uint8_t)param2);
W("\n\r");
print_executing();
sha204.wakeup(rx_buffer);
r = sha204.hmac(tx_buffer, rx_buffer, param1, param2);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
print_received_from_sha(rx_buffer);
break;
case 'm': // mac
Wl("Compute SHA-256 from key + challenge + other info on device (MAC command).");
Wl("Enter mode (1 byte; default 0):");
param1 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param1 = tx_buffer[0];
Wl("Enter Slot ID (2 bytes; default 0):");
param2 = 0;
if(2 == get_bytes_serial(tx_buffer, 2))
param2 = tx_buffer[0]*256 + tx_buffer[1];
Wl("Enter challenge (32 bytes; default 0):");
memset((void *)data1, 0, 32);
get_bytes_serial(data1, 32);
print_execute_params(SHA204_MAC,param1);
hexprint_byte_sep((uint8_t)(param2>>8));
hexprint_byte_sep((uint8_t)param2);
W("data1 ");
hexprint(data1, 32);
print_executing();
sha204.wakeup(rx_buffer);
r = sha204.mac(tx_buffer, rx_buffer, param1, param2, data1);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
print_received_from_sha(rx_buffer);
break;
case 'n': // nonce
Wl("Generate a nonce for subsequent use by other commands (Nonce command).");
Wl("Enter mode (00 to 03; default 0):");
param1 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param1 = tx_buffer[0];
Wl("Enter input value (20 or 32 bytes (dep on mode); default all 0):");
memset((void *)data1, 0, 32);
get_bytes_serial(data1, 32);
print_execute_params(SHA204_NONCE, param1);
Wl("none");
print_executing();
sha204.wakeup(rx_buffer);
r = sha204.nonce(tx_buffer,rx_buffer,param1,data1);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
print_received_from_sha(rx_buffer);
break;
case 'r': // random
Wl("Generate a random sequence.");
Wl("Enter mode (00 or 01; default 0):");
param1 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param1 = tx_buffer[0];
print_execute_params(SHA204_RANDOM,param1);
Wl("none");
print_executing();
sha204.wakeup(rx_buffer);
r = sha204.random(tx_buffer,rx_buffer,param1);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
print_received_from_sha(rx_buffer);
break;
case 'e': // read
Wl("Read from device.");
Wl("Enter zone (1 byte: 00/Config or 01/OTP or 02/Data, +0x80 to read 32 instead of 4 bytes; default 0):");
param1 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param1 = tx_buffer[0];
Wl("Enter Address (2 bytes; default 0):");
param2 = 0;
if(2 == get_bytes_serial(tx_buffer, 2))
param2 = tx_buffer[0]*256 + tx_buffer[1];
print_execute_params(SHA204_READ,param1);
hexprint_byte_sep((uint8_t)(param2>>8));
hexprint_byte((uint8_t)param2);
W("\n\r");
print_executing();
sha204.wakeup(rx_buffer);
r = sha204.read(tx_buffer, rx_buffer, param1, param2);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
print_received_from_sha(rx_buffer);
break;
case 'w': // write
Wl("Write to device.");
Wl("Enter zone (1 byte: 00/Config or 01/OTP or 02/Data,");
Wl(" +0x80 to write 32 instead of 4 bytes, +0x40 to require encryption; default 0):");
param1 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param1 = tx_buffer[0];
Wl("Enter Address (2 bytes; default 0):");
param2 = 0;
if(2 == get_bytes_serial(tx_buffer, 2))
param2 = tx_buffer[0]*256 + tx_buffer[1];
Wl("Enter data (4 or 32 bytes; default 0):");
memset((void *)data1, 0, 32);
get_bytes_serial(data1, 32);
Wl("Enter MAC to validate address and data (0 or 32 bytes; default 0):");
memset((void *)data2, 0, 32);
get_bytes_serial(data2, 32);
print_execute_params(SHA204_WRITE,param1);
hexprint_byte_sep((uint8_t)(param2>>8));
hexprint_byte_sep((uint8_t)param2);
W("data1 ");
hexprint_noln(data1, 32);
W("data2 ");
hexprint(data2, 32);
print_executing();
sha204.wakeup(rx_buffer);
r = sha204.write(tx_buffer, rx_buffer, param1, param2, data1, data2);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
print_received_from_sha(rx_buffer);
break;
case 'u': // update_extra
Wl("Update 'UserExtra' bytes (84 and 85) in the Conf zone after locking.");
Wl("Enter mode (1 byte: 00->update 84, 01->update 85; default 0):");
param1 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param1 = tx_buffer[0];
Wl("Enter new value (1 byte; default 0):");
param2 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param2 = tx_buffer[0];
print_execute_params(SHA204_UPDATE_EXTRA,param1);
hexprint_byte((uint8_t)param2);
W("\n\r");
print_executing();
sha204.wakeup(rx_buffer);
r = sha204.update_extra(tx_buffer, rx_buffer, param1, param2);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
print_received_from_sha(rx_buffer);
break;
case 'k': // wake
Wl("Test waking up.");
print_executing();
r = sha204.wakeup(rx_buffer);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
Wl("Should receive: 04 11 33 43");
print_received_from_sha(rx_buffer);
break;
case 'L': // lock
Wl("Lock a zone. This is a one time thing! Once you lock a zone, it CAN'T BE UNLOCKED. EVER!");
Wl("Enter zone (1 byte: 00/Config or 01/OTP_or_Data, +0x80 for 'force' (CRC ignored); default 0):");
param1 = 0;
if(1 == get_bytes_serial(tx_buffer, 1))
param1 = tx_buffer[0];
Wl("Enter Summary / CRC-16 of the zone (2 bytes, should be 0 if 'force'; default 0):");
param2 = 0;
if(2 == get_bytes_serial(tx_buffer, 2))
param2 = tx_buffer[0]*256 + tx_buffer[1];
print_execute_params(SHA204_LOCK,param1);
hexprint_byte_sep((uint8_t)(param2>>8));
hexprint_byte((uint8_t)param2);
W("\n\r");
print_executing();
sha204.wakeup(rx_buffer);
r = sha204.lock(tx_buffer, rx_buffer, param1, param2);
if(idle) { sha204.idle(); } else { sha204.sleep(); }
print_return_code(r);
print_received_from_sha(rx_buffer);
break;
case '\r': // enter
case '?': // help
print_help();
break;
case 'I': // switch idle and sleep
W("Switching whether the ATSHA should be put to sleep or to idle mode after commands.\n\rCurrent setting: ");
if(idle) {
idle = 0;
Wl("Sleep.");
}
else {
idle = 1;
Wl("Idle.");
}
break;
default:
break;
}
}
}
/* Must throw away unused bytes from the host, or it will lock up while waiting for the device */
//usb_serial_flush_input();
usb_tasks();
}
}
/* Helper functions implementation */
void hexprint_byte(uint8_t b) {
uint8_t high, low;
low = b & 0xF;
high = b >> 4;
usb_serial_putchar(high+'0'+7*(high/10));
usb_serial_putchar(low+'0'+7*(low/10));
}
void hexprint_byte_sep(uint8_t b) {
hexprint_byte(b);
usb_serial_putchar(hexprint_separator);
}
void hexprint_noln(uint8_t *p, uint16_t length) {
for(uint16_t i=0; i<length; i++) {
hexprint_byte(p[i]);
if(hexprint_separator!=0)
usb_serial_putchar(hexprint_separator);
}
}
void hexprint(uint8_t *p, uint16_t length) {
hexprint_noln(p, length);
usb_serial_write_P(PSTR("\n\r"));
}
void hexprint_4bits(uint8_t b) {
usb_serial_putchar((b&0xF)+'0'+7*((b&0xF)/10));
}
void hexprint_1bit(uint8_t b) {
usb_serial_putchar((b&1)+'0');
}
void print_help(void) {
Wl("*** SHA204 playground [(c) 2014 flabbergast] ***\n\r");
Wl("Raw commands: wa[k]e [c]heckMAC [d]erive_key dev_re[v]ision [g]en_dig [h]MAC");
Wl(" [m]ac [n]once [r]andom r[e]ad [w]rite [u]date_extra");
Wl("Processed commands: [s]erial c[o]nfig_zone");
Wl("Playground config: [I]dle-or-sleep");
Wl("'?' -> this help");
Wl("Dangerous/one-time only! [L]ock\n\r");
Wl("Additional comments:");
Wl(" - Format of ATSHA204 command responses:");
Wl(" <1byte:packet_size> <msg_byte> <msg_byte> ... <1byte:crc_1> <1byte:crc_2>");
Wl(" - ATSHA204 is sent to sleep or to idle mode after every command, select via [I].");
Wl(" - The 'sent packet' info does not always match what's actually exactly sent. It's provided");
Wl(" mainly to check the entered parameters.");
Wl(" - Input, when requested, is expected in (padded) HEX format, e.g. 'AB01' for two bytes: 171 1.\n\r");
}
void print_executing(void) {
W("Executing: ");
}
void print_received_from_sha(uint8_t *rx_buffer) {
W("Received from ATSHA204: ");
hexprint(rx_buffer, rx_buffer[0]);
}
void print_execute_params(uint8_t opcode, uint8_t param1) {
W("Will run with: opcode ");
hexprint_byte_sep(opcode);
W("param1 ");
hexprint_byte_sep(param1);
W("param2 ");
}
uint8_t get_bytes_serial(uint8_t *output, uint16_t len) {
char buffer[2*MAX_BUFFER_SIZE];
uint16_t input_length;
uint16_t i;
uint8_t low, high;
input_length = usb_serial_readline(buffer, 2*len+1, false);
strupr(buffer);
for(i=0; i<input_length; i+=2) {
high = buffer[i] - '0';
if(high > 9)
high -= 7;
low = buffer[i+1] - '0';
if(low > 9)
low -= 7;
output[i/2] = (uint8_t)(low + (high << 4));
}
return (input_length/2);
}
/* Read and Interpret ATSHA204 configuration */
void process_config(uint8_t *config) {
// serial number
W("Serial number: ");
hexprint_noln(config+ADDRESS_SN03, 4);
hexprint(config+ADDRESS_SN47, 5);
// revision number
W("Revision number: ");
hexprint(config+ADDRESS_RevNum,4);
// I2C setup
if(config[ADDRESS_I2CEN]&1) {
W("I2C enabled; Address: ");
hexprint_byte(config[ADDRESS_I2CADD]>>1);
W("\n\r");
} else {
W("SingleWire (I2C disabled); TTL input level: ");
if(config[ADDRESS_I2CADD]&0b1000)
Wl("Vcc");
else
Wl("fixed");
}
// OTP mode
W("OTP mode: ");
switch(config[ADDRESS_OTPMODE]) {
case 0xAA:
Wl("read-only");
break;
case 0x55:
Wl("consumption");
break;
case 0x00:
Wl("legacy");
break;
default:
Wl("reserved value (problem!)");
break;
}
// selector mode
W("Selector: ");
if(!config[ADDRESS_SELECTOR])
Wl("can be updated with UpdateExtra.");
else
Wl("can be updated only if it is 0.");
// User extra
W("User Extra byte: ");
hexprint_byte(config[84]);
W("\n\r");
// Selector
W("Selector byte: ");
hexprint_byte(config[85]);
W("\n\r");
// Lock data
W("Data and OTP zones are ");
if(config[86]==0x55)
Wl("unlocked.");
else
Wl("locked!");
// Lock config
W("Config zone is ");
if(config[87]==0x55)
Wl("unlocked.");
else
Wl("locked!");
// Slots
uint8_t i;
Wl("Configurations of slots: ");
for(i=0; i<16; i++) {
uint8_t addr = 20+(2*i);
W("Slot:");
hexprint_4bits(i);
W(" ReadKey:");
hexprint_4bits(config[addr]); // getting the 2 config bytes LSB first
W(" CheckOnly:");
hexprint_1bit(config[addr]>>4);
W(" SingleUse:");
hexprint_1bit(config[addr]>>5);
W(" EncryptRead:");
hexprint_1bit(config[addr]>>6);
W(" IsSecret:");
hexprint_1bit(config[addr]>>7);
W("\n\r WriteKey:");
hexprint_4bits(config[addr+1]);
W(" WriteConfig:");
hexprint_4bits(config[addr+1]>>4);
W("\n\r");
if(i<8) { // slots 0-7 have extra data
W(" UseFlag:");
hexprint_byte(config[52+(2*i)]);
W(" UpdateCount:");
hexprint_byte(config[53+(2*i)]);
W("\n\r");
}
if(i==15) { // slot15 has limit on usage
W(" LastKeyUse: ");
hexprint(config+68, 16);
}
}
}
uint8_t receive_serial_binary_transaction(uint8_t *buffer, uint8_t len) {
_delay_ms(100); // give the transmitting side the chance to push the rest through
if( usb_serial_available() == 0 ) {
return BINARY_TRANSACTION_RECEIVE_ERROR;
}
uint8_t n_bytes = usb_serial_getchar();
if( n_bytes >= len ) {
return BINARY_TRANSACTION_RECEIVE_ERROR;
}
buffer[0] = n_bytes;
for(uint8_t i=1; i<=n_bytes; i++) {
if( usb_serial_available() == 0 ) {
return BINARY_TRANSACTION_RECEIVE_ERROR;
}
buffer[i] = (uint8_t)usb_serial_getchar();
_delay_ms(3);
}
return BINARY_TRANSACTION_OK;
}
uint8_t binary_mode_transaction(uint8_t *data, uint8_t rxsize, uint8_t *rx_buffer, SHA204SWI *sha204) {
uint8_t i = 0;
uint8_t len;
uint8_t idle;
uint8_t opcode;
uint8_t param1;
uint16_t param2;
uint8_t datalen1=0;
uint8_t data1[32];
uint8_t datalen2=0;
uint8_t data2[32];
uint8_t datalen3=0;
uint8_t data3[14];
// process the input packet
len = data[0];
idle = data[1];
if(len<5)
return BINARY_TRANSACTION_PARAM_ERROR;
opcode = data[2];
param1 = data[3];
param2 = data[4] + 256*data[5];
if(len>5) {
if((datalen1=data[6]) > 32)
return BINARY_TRANSACTION_PARAM_ERROR;
for(i=0; i<datalen1; i++)
data1[i] = data[7+i];
}
if(len>6+datalen1) {
if((datalen2=data[7+datalen1]) > 32)
return BINARY_TRANSACTION_PARAM_ERROR;
for(i=0; i<datalen2; i++)
data2[i] = data[8+datalen1+i];
}
if(len>7+datalen1+datalen2) {
if((datalen3=data[8+datalen1+datalen2]) > 13)
return BINARY_TRANSACTION_PARAM_ERROR;
for(i=0; i<datalen3; i++)
data3[i] = data[9+datalen1+datalen2+i];
}
// run the transaction
sha204->wakeup(data);
i = sha204->execute(opcode, param1, param2,
datalen1, data1, datalen2, data2, datalen3, data3,
len, data, rxsize, rx_buffer);
if(idle)
sha204->idle();
else
sha204->sleep();
if(i != SHA204_SUCCESS)
return BINARY_TRANSACTION_EXECUTE_ERROR;
return BINARY_TRANSACTION_OK;
}
/* Return code stuff */
const char retcode_success[] PROGMEM = "Success.";
const char retcode_parse_error[] PROGMEM = "Parse error.";
const char retcode_cmd_fail[] PROGMEM = "Command execution error.";
const char retcode_status_crc[] PROGMEM = "CRC error.";
const char retcode_status_unknown[] PROGMEM = "Unknown error.";
const char retcode_func_fail[] PROGMEM = "Couldn't execute due to wrong condition/state.";
const char retcode_gen_fail[] PROGMEM = "Unspecified error.";
const char retcode_bad_param[] PROGMEM = "Bad parameter.";
const char retcode_invalid_id[] PROGMEM = "Invalid device ID.";
const char retcode_invalid_size[] PROGMEM = "Out of range error.";
const char retcode_bad_crc[] PROGMEM = "Bad CRC received.";
const char retcode_rx_fail[] PROGMEM = "Timeout while waiting for a response (got >0 bytes).";
const char retcode_rx_no_response[] PROGMEM = "Timeout (not an error while busy).";
const char retcode_resync_with_wakeup[] PROGMEM = "Resync OK after wakeup.";
const char retcode_comm_fail[] PROGMEM = "Communication failed";
const char retcode_timeout[] PROGMEM = "Timeout while waiting for a response (got no bytes).";
const char retcode_unknown[] PROGMEM = "Unknown error message.";
void print_return_code(uint8_t code) {
const char *p PROGMEM;
switch(code) {
case(SHA204_SUCCESS):
p = retcode_success;
break;
case(SHA204_PARSE_ERROR):
p = retcode_parse_error;
break;
case(SHA204_CMD_FAIL):
p = retcode_cmd_fail;
break;
case(SHA204_STATUS_CRC):
p = retcode_status_crc;
break;
case(SHA204_STATUS_UNKNOWN):
p = retcode_status_unknown;
break;
case(SHA204_FUNC_FAIL):
p = retcode_func_fail;
break;
case(SHA204_GEN_FAIL):
p = retcode_gen_fail;
break;
case(SHA204_BAD_PARAM):
p = retcode_bad_param;
break;
case(SHA204_INVALID_ID):
p = retcode_invalid_id;
break;
case(SHA204_INVALID_SIZE):
p = retcode_invalid_size;
break;
case(SHA204_BAD_CRC):
p = retcode_bad_crc;
break;
case(SHA204_RX_FAIL):
p = retcode_rx_fail;
break;
case(SHA204_RX_NO_RESPONSE):
p = retcode_rx_no_response;
break;
case(SHA204_RESYNC_WITH_WAKEUP):
p = retcode_resync_with_wakeup;
break;
case(SHA204_COMM_FAIL):
p = retcode_comm_fail;
break;
case(SHA204_TIMEOUT):
p = retcode_timeout;
break;
default:
p = retcode_unknown;
break;
}
usb_serial_writeln_P(p);
}

1
talk_to_sha204/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
keys.ini

131
talk_to_sha204/README.md Normal file
View file

@ -0,0 +1,131 @@
# talk_to_sha204.py
This python script gives access to some of the functionality of the
ATSHA204, by talking to the `sha204_playground` firmware. It requires
python 2.7, and its `pyserial` and `pyCrypto` libraries.
It was inspired by [hashlet] and sort-of emulates its functionality (and
it's perhaps more buggy). The point is that [hashlet] requires a
Raspberry Pi or a BeagleBone Black for talking to ATSHA204. This script
talks to ATSHA204 chips through another ATMEL chip, e.g. Arduino, so it
works on PCs and laptops as well (well, I haven't tried it on
Windows...)
## Basic usage
Before explaining what various commands do, it is useful to have a look
at [ATSHA204 datasheet]. Here's an excerpt: an ATSHA204's storage has 3
parts: "configuration zone" (where ATSHA's configuration parameters are
stored), "data zone" (where "keys" are stored), "OTP zone" (readable,
but mostly not-writable area). These zones can be "unlocked" (factory
state, things can be set up) or "locked" (when the zones are no longer
writable (mostly), some keys can be also not-readable ("secret")).
Locking is a one-time thing.
By "personalizing", I mean writing the desired configuration to ATSHA,
locking the configuration zone, writing random keys to the data zone and
locking it. There's space for 16 keys on an ATSHA, the access to them is
governed individually by some settings stored in the config zone.
An important thing to understand is that once the data zone is locked,
some keys will be not readable, or changeable, some will be readable and
writable, some only in a complicated way ("encrypted read/write").
So, when locking is done by this script, the keys are written to a file
(`keys.ini` by default). It is a good idea to retain this file!
The list of all command-line parameters can be printed by
talk_to_sha204.py -h
The script talks to the firmware via a serial port: the path to the
port can be either set on command line (`-s`) or in `talk_to_sha204.ini`
config file.
One can set the verbosity of the output with the `-V` switch. Accepted
values are `error`,`warning`,`info`,`debug`. Default is `error`, which
means that the script is mostly silent and only informs about errors
(which usually means the execution is interrupted). The `debug` level
is relatively verbose about what's happening.
Here's a list of some commands accepted by `talk_to_sha204.py`:
### status
Prints 'factory' (nothing locked) or 'personalized' (everything locked)
or 'neither' (config locked, data not).
### show_config
Prints the contents of the configuration zone of the ATSHA in a readable
format (see the datasheet for explanation of the individual entries).
### personalize
This will write the configuration to ATSHA (see `personalize.ini` file),
lock the config zone; then write keys to the data zone, something to the
OTP zone and lock these zones.
The keys will be read from `keys.ini` file if it exists, any keys that
can't be found will be generated randomly. All the keys will then be
written to `keys.ini`.
The contents of the OTP zone can be configured in `personalize.ini`.
### random
This returns random 32 bytes (supplied by ATSHA). Note that before the
configuration zone is locked, ATSHA returns always the same fixed bytes.
### mac
Generates a MAC digest from a file or stdin. The data used to compute
the digest include the sha256 hash of the data (file or stdin) and one
of secret keys stored on the ATSHA (so it can be verified only when one
knows that secret key - so either on the ATSHA (see `check_mac`), or
knowing the contents of the `keys.ini` file (see `offline_mac`).
Example:
talk_to_sha204.py --file README.md mac
It returns something like:
data_sha256 : 94864636b6f0481d90d16229c07796a666ef377eeececeaa7d5d267ecdbd8787
mac : 1ae94ec8bd41bb57d429a0603d57cd1fb7af75c09db44cb9172a008e93c4da8d
The first thing is the sha256 hash of the contents of the `README.md`
file, this is then fed to ATSHA204 chip, which computes the resulting
`mac`.
### check_mac
Used for verification of a MAC. The MAC and "challenge" (data used to
compute the MAC in the first place; in our case it would be the
`data_sha256` produced by the `mac` command). The script returns '0' on
success and '1' on no-match or error. Silently. To see some output, we
also need to increase the verbosity level to (at least) `info`.
talk_to_sha204.py --mac 1ae94ec8bd41bb57d429a0603d57cd1fb7af75c09db44cb9172a008e93c4da8d --challenge 94864636b6f0481d90d16229c07796a666ef377eeececeaa7d5d267ecdbd8787 --verbosity info check_mac
Result:
INFO:root:Response: match!
### offline_mac
The `check_mac` command uses ATSHA to verify the MAC. To verify the MAC
without the chip, one needs to have the correct contents of `Slot0` in
the `keys.ini` file; then `offline_mac` command can be used. Example:
talk_to_sha204.py --mac 1ae94ec8bd41bb57d429a0603d57cd1fb7af75c09db44cb9172a008e93c4da8d --challenge 94864636b6f0481d90d16229c07796a666ef377eeececeaa7d5d267ecdbd8787 --verbosity info offline_mac
Result:
INFO:root:Response: match!
The point is that this can be used on, say, a server without the ATSHA
chip present (but with the `keys.ini` file available).
[hashlet]: https://github.com/cryptotronix/hashlet
[ATSHA204 datasheet]: http://www.atmel.com/Images/Atmel-8740-CryptoAuth-ATSHA204-Datasheet.pdf

View file

@ -0,0 +1,30 @@
[ConfigZone]
I2Cenable = no
I2Caddress = 0x64
# this OTPmode (0xAA - read-only) differs from the factory value (0x55 - consumption)
OTPmode = 0xAA
SelectorMode = 0x00
## example configuration for individual slots
## can use [Slot0] to [Slot15]
[Slot0]
ReadKey = 15
CheckOnly = False
SingleUse = False
EncryptRead = False
IsSecret = True
WriteConfig = 0b1000
WriteKey = 0
# the next two options only work for keys 0-7
UseFlag = 0xff
UpdateCount = 0x00
# the next option only works with key 15 (note it should a hex string!)
KeyUse = ffffffffffffffffffffffffffffffff
# use 'keys.ini' file to specify slot contents if required
[OTPzone]
OTPcontents = "flabbergast __DATE__ "
## Notes for OTPcontents:
## - expected form is a string enclosed in double quotes (")
## - will be padded by 0xFF's to 64 bytes, or cut short if longer
## - __DATE__ placeholder will be replaced by current date in YYYYMMDD format

View file

@ -0,0 +1,3 @@
[Communication]
SerialPort = /dev/ttyACM0

690
talk_to_sha204/talk_to_sha204.py Executable file
View file

@ -0,0 +1,690 @@
#!/usr/bin/env python2
# talk_to_sha204.py
# (c) 2014 flabbergast
#
# Communicate with ATSHA204 via the binary mode of sha204_playground firmware.
#
import serial
from Crypto.Hash import SHA256
import binascii
import time
import os
import sys
import logging
import argparse
import ConfigParser
import pprint
BINARY_TRANSACTION_CODE = chr(0xFD)
# command op-code definitions
SHA204_CHECKMAC = chr(0x28)
SHA204_DERIVE_KEY = chr(0x1C)
SHA204_DEVREV = chr(0x30)
SHA204_GENDIG = chr(0x15)
SHA204_HMAC = chr(0x11)
SHA204_LOCK = chr(0x17)
SHA204_MAC = chr(0x08)
SHA204_NONCE = chr(0x16)
SHA204_PAUSE = chr(0x01)
SHA204_RANDOM = chr(0x1B)
SHA204_READ = chr(0x02)
SHA204_UPDATE_EXTRA = chr(0x20)
SHA204_WRITE = chr(0x12)
# firmware binary mode return codes
BINARY_MODE_RETURN_CODES = {
0: 'BINARY_TRANSACTION_OK',
1: 'BINARY_TRANSACTION_RECEIVE_ERROR',
2: 'BINARY_TRANSACTION_PARAM_ERROR',
3: 'BINARY_TRANSACTION_EXECUTE_ERROR',
99: 'PROBLEM WITH SERIAL COMMUNICATION (BUFFERING?)'
}
# idle versus sleep instruction
REQUEST_IDLE = chr(1)
REQUEST_SLEEP = chr(0)
# Parameters for MAC/checkMAC/offlineMAC
MAC_SLOT = 0
MAC_MODE = chr(0)
def MAC_OTHER_DATA(slot):
return SHA204_MAC+MAC_MODE+chr(slot)+b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
#########################################
### Low-Level communication functions ###
#########################################
def crc16(data):
crc_register = 0
polynomial = 0x8005
for c in data:
for shift_register in 1, 2, 4, 8, 16, 32, 64, 128:
data_bit = 1 if (ord(c) & shift_register) > 0 else 0
crc_bit = crc_register >> 15
crc_register = (crc_register << 1) & 0xFFFF
if (data_bit ^ crc_bit) != 0:
crc_register ^= polynomial
#return crc_register
return chr(crc_register & 0xFF) + chr(crc_register >> 8)
class TransactionError(Exception):
def __init__(self, message, value=0):
self.message = message
self.value = value
def __str__(self):
return repr(self.message + ': ' + BINARY_MODE_RETURN_CODES[self.value])
def do_transaction(buf, serport):
# buf is assumed to have the following format:
# 1 byte: idle or sleep after command?
# 1 byte: opcode
# 1 byte: param1
# 2 bytes: param2
# --> from this point on, parameters go in pairs (length, data), and they are optional
# 1 byte: datalen1
# datalen1 bytes: data1
# 1 byte: datalen2
# datalen2 bytes: data2
# 1 byte: datalen3
# datalen3 bytes: data3
if args.dry_run and buf[1] in [SHA204_WRITE, SHA204_LOCK, SHA204_UPDATE_EXTRA]:
logging.info("Dry run! Not sending " + binascii.hexlify(buf))
return chr(0)
message = b'' + BINARY_TRANSACTION_CODE + chr(len(buf)) + buf
serport.write(message)
status = serport.read(1) # read on byte, with timeout
if status != chr(0):
raise TransactionError("Firmware returned", ord(status))
response_length = ord(serport.read(1)) # next byte is message length, this byte included
response = serport.read(response_length - 1) # read the rest of the message (timeout!)
if len(response) != response_length - 1:
raise TransactionError("Serial communication problem: did not receive the whole response", 99)
crc = crc16(chr(response_length) + response[0:-2])
if crc != response[-2:]:
raise TransactionError("CRC error", 99)
# flush the port to clean up the pipes
serport.flushInput()
serport.flushOutput()
return response[0:-2]
###############################
### Mid-level communication ###
###############################
def receive_config_area(serport):
config = do_transaction(REQUEST_SLEEP+SHA204_READ + b'\x80\x00\x00', serport)
config += do_transaction(REQUEST_SLEEP+SHA204_READ + b'\x80\x08\x00', serport)
for i in range(0x10, 0x16):
config += do_transaction(REQUEST_SLEEP+SHA204_READ + b'\x00' + chr(i) + b'\x00', serport)
return config
def read_config_area(serport):
logging.debug("Reading and processing config area of ATSHA...")
try:
return receive_config_area(serport)
except TransactionError, e:
logging.error("ERROR communicating with firmware/ATSHA: " + str(e))
exit(1)
def write_config_word(address, word, serport):
if len(word) != 4:
logging.error("Trying to write something else that 4 bytes to config area!")
exit(1)
logging.debug("Writing to config zone, word address " + hex(address) + ", word: 0x" + binascii.hexlify(word))
try:
response = do_transaction(REQUEST_SLEEP+SHA204_WRITE + b'\x00' + chr(address) + b'\x00\x04' + word, serport)
except TransactionError, e:
logging.error("ERROR communicating with firmware/ATSHA: " + str(e))
exit(1)
else:
logging.debug("Got response: " + ("OK" if response == chr(0) else "not OK"))
return response
def write_otp_area(contents, serport):
if len(contents) != 64:
logging.error("Trying to write something else that 64 bytes to OTP!")
exit(1)
logging.debug("Writing to OTP zone, word address 0x0, data: 0x" + binascii.hexlify(contents[0:32]))
try:
response = do_transaction(REQUEST_SLEEP+SHA204_WRITE + b'\x81\x00\x00\x20' + contents[0:32], serport)
except TransactionError, e:
logging.error("ERROR communicating with firmware/ATSHA: " + str(e))
exit(1)
else:
logging.debug("Got response: " + ("OK" if response == chr(0) else "not OK"))
logging.debug("Writing to OTP zone, word address 0x8, data: 0x" + binascii.hexlify(contents[32:64]))
try:
response = do_transaction(REQUEST_SLEEP+SHA204_WRITE + b'\x81\x08\x00\x20' + contents[32:64], serport)
except TransactionError, e:
logging.error("ERROR communicating with firmware/ATSHA: " + str(e))
exit(1)
else:
logging.debug("Got response: " + ("OK" if response == chr(0) else "not OK"))
return response
def update_config_area(config, serport):
config_new = process_config_modifications(config)
# determine differences and write them
for i in range(0, 22):
have_changed = False
for j in range(0, 4):
if config_new[4 * i + j] != config[4 * i + j]:
logging.info("Changing byte in config zone at position " + hex(4 * i + j) + ", from " + hex(
ord(config[4 * i + j])) + " to " + hex(ord(config_new[4 * i + j])))
have_changed = True
if have_changed:
write_config_word(i, config_new[4 * i:4 * i + 4], serport)
return config_new
def do_lock_config_area(expected_config, serport):
logging.info("Verifying the config zone contents.")
config_verify = read_config_area(ser_port)
if not (expected_config == config_verify):
logging.error(
"The contents of the config zone is not what is expected! Problem with ATSHA or dry run? Not locking.")
exit(1)
else:
logging.info("Locking the config zone!")
crc = crc16(expected_config)
try:
response = do_transaction(REQUEST_SLEEP+SHA204_LOCK + b'\x00' + crc, serport)
except TransactionError, e:
logging.error("ERROR communicating with firmware/ATSHA: " + str(e))
exit(1)
else:
logging.debug("Got response: " + ("OK" if response == chr(0) else "not OK"))
return response
def do_lock_data_otp_area(expected_data, expected_otp, serport):
logging.info("Locking the data and OTP zones!")
crc = crc16(expected_data + expected_otp)
try:
response = do_transaction(REQUEST_SLEEP+SHA204_LOCK + b'\x01' + crc, serport)
except TransactionError, e:
logging.error("ERROR communicating with firmware/ATSHA: " + str(e))
exit(1)
else:
logging.debug("Got response: " + ("OK" if response == chr(0) else "not OK"))
return response
def get_random(serport):
try:
response = do_transaction(REQUEST_SLEEP+SHA204_RANDOM + b'\x00\x00\x00', serport)
except TransactionError, e:
logging.error("ERROR communicating with firmware/ATSHA: " + str(e))
exit(1)
else:
return response
def write_data_slot(slot, contents, serport):
if len(contents) != 32 or slot < 0 or slot > 15:
logging.error("Something went wrong, the call to write_data_slot has wrong params!")
exit(1)
logging.debug("Writing to data zone, slot " + hex(slot) + ", data: 0x" + binascii.hexlify(contents))
try:
response = do_transaction(REQUEST_SLEEP+SHA204_WRITE + b'\x82' + chr(slot * 8) + b'\x00\x20' + contents, serport)
except TransactionError, e:
logging.error("ERROR communicating with firmware/ATSHA: " + str(e))
exit(1)
else:
logging.debug("Got response: " + ("OK" if response == chr(0) else "not OK"))
return response
def mac(challenge, slot, serport):
if len(challenge) != 32 or slot < 0 or slot > 15:
logging.error("Something went wrong, the call to mac has wrong params!")
exit(1)
logging.debug("Calling the mac command, challenge "+binascii.hexlify(challenge)+", slot "+hex(slot))
try:
response = do_transaction(REQUEST_SLEEP+SHA204_MAC+MAC_MODE+chr(slot)+b'\x00\x20' + challenge, serport)
except TransactionError, e:
logging.error("ERROR communicating with firmware/ATSHA: " + str(e))
exit(1)
else:
if len(response) != 32:
logging.error("Received an unexpected response from mac command: "+binascii.hexlify(response))
else:
print("data_sha256 : "+binascii.hexlify(challenge))
print("mac : "+binascii.hexlify(response))
def check_mac(challenge, mac, slot, serport):
if len(challenge) != 32 or len(mac) != 32 or slot < 0 or slot > 15:
logging.error("Something went wrong, the call to mac has wrong params!")
exit(1)
logging.debug("Calling the checkmac command, challenge "+binascii.hexlify(challenge)+", mac "+binascii.hexlify(mac)+" slot "+hex(slot))
try:
response = do_transaction(REQUEST_SLEEP+SHA204_CHECKMAC+MAC_MODE+chr(slot)+b'\x00\x20' + challenge + b'\x20' + mac + b'\x0D' + MAC_OTHER_DATA(slot), serport)
except TransactionError, e:
logging.error("ERROR communicating with firmware/ATSHA: " + str(e))
exit(1)
else:
if response == chr(0):
logging.info("Response: match!");
exit(0)
else:
logging.info("Response: no match!");
exit(1)
#######################
### Data processing ###
#######################
def process_config_area(config):
c = {'serial': binascii.hexlify(config[0:4] + config[8:13]), 'revision': binascii.hexlify(config[4:8]),
'I2Cenable': (ord(config[14]) & 1 == 1), 'I2Caddress': hex(ord(config[16]) >> 1)}
if config[18] == b'\xAA':
c['OTPmode'] = "read-only"
elif config[18] == b'\x55':
c['OTPmode'] = "consumption"
elif config[18] == b'\x00':
c['OTPmode'] = "legacy"
else:
c['OTPmode'] = "error!"
if config[19] == b'\x00':
c['SelectorMode'] = "Selector can be updated with UpdateExtra"
else:
c['SelectorMode'] = "Selector can be updated only when 0"
c['UserExtra'] = binascii.hexlify(config[84])
c['Selector'] = binascii.hexlify(config[85])
c['DataOTPlocked'] = (config[86] != b'\x55')
c['ConfigLocked'] = (config[87] != b'\x55')
c['slotConfigs'] = []
for i in range(0, 16):
slot = {'Index': i, 'ReadKey': ord(config[20 + 2 * i]) & 0xF,
'CheckOnly': (ord(config[20 + 2 * i]) & (1 << 4) > 0),
'SingleUse': (ord(config[20 + 2 * i]) & (1 << 5) > 0),
'EncryptRead': (ord(config[20 + 2 * i]) & (1 << 6) > 0),
'IsSecret': (ord(config[20 + 2 * i]) & (1 << 7) > 0), 'WriteKey': ord(config[21 + 2 * i]) & 0xF,
'WriteConfig': b'0b' + bin((ord(config[21 + 2 * i]) & 0xF0) >> 4)[2:].zfill(4)}
if i < 8:
slot['UseFlag'] = binascii.hexlify(config[52 + 2 * i])
slot['UpdateCount'] = binascii.hexlify(config[53 + 2 * i])
if i == 15:
slot['KeyUse'] = binascii.hexlify(config[68:84])
c['slotConfigs'].append(slot)
return c
def get_ini_bool(ini, section, option):
try:
t = ini.getboolean(section, option)
except ValueError:
logging.error("[" + section + "]->" + option + " value can't be parsed correctly from the config file.")
return None
else:
return t
def get_ini_int(ini, section, option, possibilites=range(0, 256)):
try:
t = int(ini.get(section, option), 0)
except ValueError:
logging.error("[" + section + "]->" + option + " value can't be parsed correctly from the config file.")
return None
else:
if t not in possibilites:
logging.error("[" + section + "]->" + option + " value can't be parsed correctly from the config file.")
return None
else:
return t
def get_ini_str(ini, section, option, validate=(lambda x: True)):
try:
t = ini.get(section, option)
except ValueError:
logging.error("[" + section + "]->" + option + " value can't be parsed correctly from the config file.")
return None
else:
if not validate(t):
logging.error("[" + section + "]->" + option + " value can't be parsed correctly from the config file.")
return None
else:
return t
def validate_hexlified_data(string, length):
try:
t = binascii.unhexlify(string)
except TypeError:
return False
else:
if len(t) != length:
return False
else:
return True
def validate_key_use(key_use):
return validate_hexlified_data(key_use, 16)
def validate_slot_contents(contents):
return validate_hexlified_data(contents, 32)
def process_config_modifications(config):
if len(config) != 88:
logging.error("Something's wrong with the config (incorrect length?).")
exit(1)
personalize_config = ConfigParser.SafeConfigParser()
if os.path.isfile(args.personalize_file):
personalize_config.read(args.personalize_file)
else:
logging.warning("Personalize file not found. Proceeding with current ATSHA configuration.")
return config
config_list = list(config)
if personalize_config.has_option('ConfigZone', 'I2Cenable'):
t = get_ini_bool(personalize_config, 'ConfigZone', 'I2Cenable')
if t is not None:
config_list[14] = (chr(1) if t else chr(0))
if personalize_config.has_option('ConfigZone', 'I2Caddress'):
t = get_ini_int(personalize_config, 'ConfigZone', 'I2Caddress', range(0, 128))
if t is not None:
config_list[16] = chr(t << 1)
if personalize_config.has_option('ConfigZone', 'OTPmode'):
t = get_ini_int(personalize_config, 'ConfigZone', 'OTPmode', [0xAA, 0x55, 0])
if t is not None:
config_list[18] = chr(t)
if personalize_config.has_option('ConfigZone', 'SelectorMode'):
t = get_ini_int(personalize_config, 'ConfigZone', 'SelectorMode')
if t is not None:
config_list[19] = chr(t)
for i in range(0, 16):
slot_sect = 'Slot' + str(i)
slot_addr = 20 + 2 * i
if personalize_config.has_section(slot_sect):
if personalize_config.has_option(slot_sect, 'ReadKey'):
t = get_ini_int(personalize_config, slot_sect, 'ReadKey', range(0, 16))
if t is not None:
config_list[slot_addr] = chr((ord(config_list[slot_addr]) & 0xF0) | t)
if personalize_config.has_option(slot_sect, 'CheckOnly'):
t = get_ini_bool(personalize_config, slot_sect, 'CheckOnly')
if t is not None:
if t:
config_list[slot_addr] = chr(ord(config_list[slot_addr]) | (1 << 4))
else:
config_list[slot_addr] = chr(ord(config_list[slot_addr]) & (0xFF & ~(1 << 4)))
if personalize_config.has_option(slot_sect, 'SingleUse'):
t = get_ini_bool(personalize_config, slot_sect, 'SingleUse')
if t is not None:
if t:
config_list[slot_addr] = chr(ord(config_list[slot_addr]) | (1 << 5))
else:
config_list[slot_addr] = chr(ord(config_list[slot_addr]) & (0xFF & ~(1 << 5)))
if personalize_config.has_option(slot_sect, 'EncryptRead'):
t = get_ini_bool(personalize_config, slot_sect, 'EncryptRead')
if t is not None:
if t:
config_list[slot_addr] = chr(ord(config_list[slot_addr]) | (1 << 6))
else:
config_list[slot_addr] = chr(ord(config_list[slot_addr]) & (0xFF & ~(1 << 6)))
if personalize_config.has_option(slot_sect, 'IsSecret'):
t = get_ini_bool(personalize_config, slot_sect, 'IsSecret')
if t is not None:
if t:
config_list[slot_addr] = chr(ord(config_list[slot_addr]) | (1 << 7))
else:
config_list[slot_addr] = chr(ord(config_list[slot_addr]) & (0xFF & ~(1 << 7)))
if personalize_config.has_option(slot_sect, 'WriteKey'):
t = get_ini_int(personalize_config, slot_sect, 'WriteKey', range(0, 16))
if t is not None:
config_list[slot_addr + 1] = chr((ord(config_list[slot_addr + 1]) & 0xF0) | t)
if personalize_config.has_option(slot_sect, 'WriteConfig'):
t = get_ini_int(personalize_config, slot_sect, 'WriteConfig', range(0, 16))
if t is not None:
config_list[slot_addr + 1] = chr((ord(config_list[slot_addr + 1]) & 0x0F) | (t << 4))
if i < 8:
useflag_addr = 52 + 2 * i
if personalize_config.has_option(slot_sect, 'UseFlag'):
t = get_ini_int(personalize_config, slot_sect, 'UseFlag')
if t is not None:
config_list[useflag_addr] = chr(t)
if personalize_config.has_option(slot_sect, 'UpdateCount'):
t = get_ini_int(personalize_config, slot_sect, 'UpdateCount')
if t is not None:
config_list[useflag_addr + 1] = chr(t)
if i == 15:
if personalize_config.has_option(slot_sect, 'KeyUse'):
t = get_ini_str(personalize_config, slot_sect, 'KeyUse', validate_key_use)
if t is not None:
t = binascii.unhexlify(t)
for j in range(0, 16):
config_list[68 + j] = t[j]
return b''.join(config_list)
#########################
### Command functions ###
#########################
def lock_config(serport):
config_bin = read_config_area(serport)
config_area = process_config_area(config_bin)
if config_area['ConfigLocked']:
logging.error("Config zone already locked!")
exit(1)
config_bin_new = update_config_area(config_bin, serport)
if chr(0) == do_lock_config_area(config_bin_new, serport):
print "Config zone locked!"
def lock_data(serport):
config_area = process_config_area(read_config_area(serport))
if not config_area['ConfigLocked']:
logging.error("Config zone needs to be locked before locking data and OTP!")
exit(1)
elif config_area['DataOTPlocked']:
logging.error("Data and OTP zones are already locked!")
exit(1)
keys = {}
keys_ini = ConfigParser.ConfigParser()
# read keys from file if present
if (not args.random_keys) and os.path.isfile(args.keys_file):
keys_ini.read(args.keys_file)
for i in range(0, 16):
if keys_ini.has_option('Keys', 'Slot' + str(i)):
t = get_ini_str(keys_ini, 'Keys', 'Slot' + str(i), validate_slot_contents)
if t is not None:
keys[i] = binascii.unhexlify(t)
logging.info("Read " + str(len(keys)) + " from " + args.keys_file)
# fill in the other ones randomly
for i in range(0, 16):
if i not in keys:
keys[i] = get_random(serport)
# prepare ini for writing
if not keys_ini.has_section('Keys'):
keys_ini.add_section('Keys')
for i in range(0, 16):
keys_ini.set('Keys', 'Slot' + str(i), binascii.hexlify(keys[i]))
# write keys_ini
with open(args.keys_file, 'w') as f:
keys_ini.write(f)
# write the keys into slots
whole_data = b''
for i in range(0, 16):
write_data_slot(i, keys[i], serport)
whole_data += keys[i] # collect all the slots into one variable, used later for locking
# OTP stuff
otp = b'\xFF' * 64
if not os.path.isfile(args.personalize_file):
logging.warning("Personalize file not found. OTP zone will be filled with 0xFF's.")
else:
personalize_config = ConfigParser.SafeConfigParser()
personalize_config.read(args.personalize_file)
if not personalize_config.has_option('OTPzone', 'OTPcontents'):
logging.warning(
"OTP zone contents not set in " + args.personalize_file + ". It will be filled with 0xFF's.")
else:
otp = get_ini_str(personalize_config, 'OTPzone', 'OTPcontents')[1:-1].replace('__DATE__', time.strftime('%Y%m%d'))[0:64].ljust(64, chr(0xFF))
write_otp_area(otp, serport)
# lock!
if chr(0) == do_lock_data_otp_area(whole_data, otp, serport):
print "Data+OTP zones locked!"
def check_mac_offline(challenge, mac, slot):
# try to get the key
keys = {}
keys_ini = ConfigParser.ConfigParser()
if not os.path.isfile(args.keys_file):
logging.error("Need the keys_file for offline MAC checking!")
exit(1)
keys_ini.read(args.keys_file)
if not keys_ini.has_option('Keys', 'Slot' + str(slot)):
logging.error("Need the slot "+hex(slot)+" contents in "+args.keys_file+" file for offline MAC checking!")
exit(1)
key = get_ini_str(keys_ini, 'Keys', 'Slot' + str(slot), validate_slot_contents)
if key is None:
logging.error("Need the slot "+hex(slot)+" contents in "+args.keys_file+" file to be valid for offline MAC checking!")
exit(1)
key = binascii.unhexlify(key)
other_data = MAC_OTHER_DATA(slot)
full_challenge = key + challenge + other_data[0:4]+(b'\x00'*8)+other_data[4:7]+b'\xEE'+other_data[7:11]+b'\x01\x23'+other_data[11:13]
computed_mac = SHA256.new(full_challenge)
if computed_mac.digest() == mac:
logging.info("Response: match!");
exit(0)
else:
logging.info("Response: no match!");
exit(1)
#################
### Main code ###
#################
# parse command-line arguments
parser = argparse.ArgumentParser(description="Talk to ATSHA204 using sha204_playground firmware.",
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("command", choices=['status', 'show_config', 'lock_config', 'lock_data', 'personalize', 'random',
'mac', 'check_mac', 'offline_mac'], help='Command')
parser.add_argument('-n', '--dry-run', dest='dry_run', action='store_true', help="Do not do actual write or lock.")
parser.add_argument('-c', '--config-file', dest='config_file', nargs='?', default='talk_to_sha204.ini',
help="Path to config file.")
parser.add_argument('-s', '--serial-port', dest='serial_path', nargs='?', help='Path to serial port.')
parser.add_argument('-p', '--personalize-file', dest='personalize_file', nargs='?', default='personalize.ini',
help="Path to file with data for personalizing.")
parser.add_argument('-k', '--keys-file', dest='keys_file', nargs='?', default='keys.ini',
help="Path to file with slot contents (any new random keys will be added on personalize or lock_data).")
parser.add_argument('--random-keys', dest='random_keys', action='store_true',
help="Do not read keys-file even if present, generate random keys (keys-file will be overwritten!).")
parser.add_argument('-f', '--file', dest='file', nargs='?', help="Path to file to be used for MAC. Uses stdin if no file given.")
parser.add_argument('-m', '--mac', dest='mac', nargs='?', help="MAC to be checked; for check_mac or offline_mac.")
parser.add_argument('-C', '--challenge', dest='challenge', nargs='?', help="SHA256 of data (challenge) for check_mac or offline_mac.")
parser.add_argument('-V', '--verbosity', dest='verbosity', nargs='?', default='error',
choices=['error', 'warning', 'info', 'debug'], help="Verbosity level.")
args = parser.parse_args() # args are used as a global variable
# set up logging
log_level = getattr(logging, args.verbosity.upper(), None)
if not isinstance(log_level, int):
raise ValueError('Invalid log level: %s' % args.verbosity)
logging.basicConfig(level=log_level)
# parse config file
script_config = ConfigParser.ConfigParser()
if os.path.isfile(args.config_file):
script_config.read(args.config_file)
# do offline operations before doing anything with the serial port
if args.command == 'offline_mac':
# get params
try:
checkmac_challenge = binascii.unhexlify(args.challenge)
checkmac_mac = binascii.unhexlify(args.mac)
if len(checkmac_mac) != 32 or len(checkmac_challenge) != 32:
raise TypeError("Incorrect length (should be 32 bytes / 64 hex chars).")
except TypeError, e:
logging.error("Problem processing MAC (--mac) or challenge (--challenge) parameters: "+str(e))
exit(1)
else:
check_mac_offline(checkmac_challenge, checkmac_mac, MAC_SLOT)
# open serial port
serial_path = ''
if script_config.has_option('Communication', 'SerialPort'):
serial_path = script_config.get('Communication', 'SerialPort')
if args.serial_path:
serial_path = args.serial_path
if not os.path.exists(serial_path):
logging.error("Specify a valid path to serial port either in the config file or on command line!")
exit(1)
ser_port = serial.Serial(port=serial_path, baudrate=115200, timeout=2) # timeout 2 seconds!
time.sleep(2) # putting 2 so that Arduinos have time to run the bootloader, exit from it and start the sketch
# for avr-gcc/LUFA, 0.5 is enough
ser_port.flushInput() # throw away the message received after connecting (DTR)
# pretty printer
pp = pprint.PrettyPrinter(indent=2)
if args.command == "show_config":
config_area = process_config_area(read_config_area(ser_port))
pp.pprint(config_area)
exit(0)
elif args.command == "status":
config_area = process_config_area(read_config_area(ser_port))
logging.info("Data and OTP: " + ("locked" if config_area['DataOTPlocked'] else "unlocked"))
logging.info("Config area: " + ("locked" if config_area['ConfigLocked'] else "unlocked"))
if config_area['DataOTPlocked'] and config_area['ConfigLocked']:
print("personalized")
elif not config_area['DataOTPlocked'] and not config_area['ConfigLocked']:
print("factory")
else:
print("neither")
exit(0)
elif args.command == 'lock_config':
lock_config(ser_port)
elif args.command == 'random':
print(binascii.hexlify(get_random(ser_port)))
elif args.command == 'lock_data':
lock_data(ser_port)
elif args.command == 'personalize':
config_area = process_config_area(read_config_area(ser_port))
if not config_area['ConfigLocked']:
lock_config(ser_port)
if not config_area['DataOTPlocked']:
lock_data(ser_port)
print("personalized")
elif args.command == 'mac':
mac_challenge = SHA256.new()
with open(args.file,'r') if args.file else sys.stdin as f:
while True:
chunk = f.read(8192)
if len(chunk) == 0:
break
mac_challenge.update(chunk)
mac(mac_challenge.digest(), MAC_SLOT, ser_port)
elif args.command == 'check_mac':
try:
checkmac_challenge = binascii.unhexlify(args.challenge)
checkmac_mac = binascii.unhexlify(args.mac)
if len(checkmac_mac) != 32 or len(checkmac_challenge) != 32:
raise TypeError("Incorrect length (should be 32 bytes / 64 hex chars).")
except TypeError, e:
logging.error("Problem processing MAC (--mac) or challenge (--challenge) parameters: "+str(e))
exit(1)
else:
check_mac(checkmac_challenge, checkmac_mac, MAC_SLOT, ser_port)
exit(0)