Skip to content

Instantly share code, notes, and snippets.

@ghedo
Last active November 26, 2019 12:35
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save ghedo/6751045 to your computer and use it in GitHub Desktop.
Save ghedo/6751045 to your computer and use it in GitHub Desktop.
Arduino library for the ATSHA204 authentication chip
/*
* Arduino library for the ATSHA204 authentication chip.
*
* The ATSHA204 is a tiny and low-power authentication chip. This library
* implements a simple interface to access most of the chip functionality from
* an Arduino.
*
* Note that only the Single-Wire Interface (SWI) is currently supported.
*
* FEATURES:
* + Raw memory read
* + Config and Data memory lock
* + Random number generator
* + Nonce generation
* + Key derivation
*
* GETTING STARTED:
*
* #include <ATSHA204.h>
*
* #define ATSHA204_PIN 9
*
* ATSHA204 sha204(ATSHA204_PIN);
*
* int rc;
*
* void setup() {
* Serial.begin(9600);
*
* if (sha204.wake())
* while (1); // error
*
* //sha204.lock(0);
* }
*
* void loop() {
* int i;
* uint8_t buf[32];
*
* rc = sha204.wake();
* if (rc) Serial.println("wake(): error");
*
* // check if config memory is locked
* rc = sha204.read(0, 0x15, buf, 4);
* if (rc) Serial.println("read(): error");
*
* if (buf[3] == 0x55)
* Serial.println("Config is not locked");
*
* // generate and print random numbers
* Serial.print("Random numbers: ");
*
* rc = sha204.rand(1, buf);
* if (rc) Serial.println("rand(): error");
*
* for (i = 0; i < 32; i++)
* Serial.print(buf[i], HEX);
*
* Serial.println("");
*
* delay(5000);
* }
*
* TODO
* + Raw memory write
* + HMAC generation
* + Digest generation
* + ...
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS 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.
*/
#include <Arduino.h>
#include "ATSHA204.h"
#include <avr/cpufunc.h>
#include <string.h>
enum ATSHA204_FLAGS {
FLAG_COMMAND = 0x77,
FLAG_TRANSMIT = 0x88,
FLAG_IDLE = 0xbb,
FLAG_SLEEP = 0xcc
};
enum ATSHA204_ZONES {
CONFIG,
DATA,
OTP
};
enum ATSHA204_OPCODES {
OP_DERIVE_KEY = 0x1C,
OP_DEV_REV = 0x30,
OP_GEN_DIG = 0x15,
OP_HMAC = 0x11,
OP_CHECK_MAC = 0x28,
OP_LOCK = 0x17,
OP_MAC = 0x18,
OP_NONCE = 0x16,
OP_PAUSE = 0x01,
OP_RANDOM = 0x1B,
OP_READ = 0x02,
OP_WRITE = 0x12
};
enum ATSHA204_ERRNO {
ERR_SUCCESS = 0x00,
ERR_MISCOMPARE = 0x01,
ERR_PARSE = 0x03,
ERR_EXECUTION = 0x0f,
ERR_WAKE = 0x11,
ERR_CRC = 0xff
};
#define DELAY_WLO (60 + 20) /* min 60 us */
#define DELAY_WHI (2.5 + 0.5) /* min 2.5 ms */
#define DELAY_BIT (4) /* ~230.4 kbps */
#define DELAY_RTX (15)
#define DELAY_SYN (85)
void crc16(uint8_t *buf, uint8_t len, uint8_t *crc) {
uint8_t i;
uint16_t crc16 = 0;
for (i = 0; i < len; i++) {
uint8_t shift;
for (shift = 0x01; shift > 0x00; shift <<= 1) {
uint8_t data_bit = (buf[i] & shift) ? 1 : 0;
uint8_t crc_bit = crc16 >> 15;
crc16 <<= 1;
if ((data_bit ^ crc_bit) != 0)
crc16 ^= 0x8005;
}
}
crc[0] = (uint8_t) (crc16 & 0x00FF);
crc[1] = (uint8_t) (crc16 >> 8);
}
ATSHA204::ATSHA204(int pin) {
pin_bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
port_ddr = portModeRegister(port);
port_out = portOutputRegister(port);
port_in = portInputRegister(port);
}
void ATSHA204::setPin(uint8_t is_high) {
*port_ddr |= pin_bit;
if (is_high)
*port_out |= pin_bit;
else
*port_out &= ~pin_bit;
}
int ATSHA204::send(uint8_t *buf, uint8_t len) {
uint8_t i, bit_mask;
noInterrupts();
*port_out |= pin_bit;
*port_ddr |= pin_bit;
delayMicroseconds(DELAY_RTX);
for (i = 0; i < len; i++) {
for (bit_mask = 1; bit_mask > 0; bit_mask <<= 1) {
if (bit_mask & buf[i]) {
/* One token */
*port_out &= ~pin_bit;
delayMicroseconds(DELAY_BIT);
*port_out |= pin_bit;
delayMicroseconds(DELAY_BIT * 7);
} else {
/* Zero token */
*port_out &= ~pin_bit;
delayMicroseconds(DELAY_BIT);
*port_out |= pin_bit;
delayMicroseconds(DELAY_BIT);
*port_out &= ~pin_bit;
delayMicroseconds(DELAY_BIT);
*port_out |= pin_bit;
delayMicroseconds(DELAY_BIT * 5);
}
}
}
interrupts();
return 0;
}
int ATSHA204::recv(uint8_t *buf, uint8_t len) {
uint8_t status = 0;
uint8_t i;
uint8_t recv_flag = FLAG_TRANSMIT;
send(&recv_flag, 1);
noInterrupts();
*port_ddr &= ~pin_bit;
for (i = 0; i < len; i++) {
uint8_t bit_mask;
for (bit_mask = 1; bit_mask > 0; bit_mask <<= 1) {
uint8_t pulse_count = 0;
uint8_t timeout_count = 255;
/* Start pulse */
while (--timeout_count > 0) {
if ((*port_in & pin_bit) == 0) /* falling */
break;
}
if (timeout_count == 0) {
status = -1;
break;
}
do {
if ((*port_in & pin_bit) != 0) { /* rising */
pulse_count = 1;
break;
}
} while (--timeout_count > 0);
if (pulse_count == 0) {
status = -1;
break;
}
timeout_count = 26;
do {
if ((*port_in & pin_bit) == 0) {
pulse_count = 2;
break;
}
} while (--timeout_count > 0);
if (pulse_count == 2) {
do {
if ((*port_in & pin_bit) != 0)
break;
} while (timeout_count-- > 0);
} else
buf[i] |= bit_mask; /* one token */
}
if (status != 0)
break;
}
interrupts();
return status;
}
int ATSHA204::wake() {
uint8_t wake_resp[4];
wake_resp[0] = 0;
wake_resp[1] = 0;
wake_resp[2] = 0;
wake_resp[3] = 0;
setPin(0);
delayMicroseconds(DELAY_WLO);
setPin(1);
delay(DELAY_WHI);
recv(wake_resp, 4);
if ((wake_resp[0] == 4) && (wake_resp[1] == ERR_WAKE))
return 0;
else
return -1;
}
int ATSHA204::sleep() {
uint8_t flag = FLAG_SLEEP;
return send(&flag, 1);
}
int ATSHA204::sync(uint8_t *buf, uint8_t len) {
int rc;
uint8_t sleep = FLAG_SLEEP;
delay(DELAY_SYN);
rc = recv(buf, len);
if (!rc) return rc;
this -> sleep();
return wake();
}
int ATSHA204::exec(uint8_t *tx, uint8_t tx_len,
uint8_t *rx, uint8_t rx_len,
uint8_t exec_delay, uint8_t max_delay) {
uint8_t rx_crc[2];
uint8_t cmd_flag = FLAG_COMMAND;
crc16(tx, tx_len - 2, tx + (tx_len - 2));
send(&cmd_flag, 1);
send(tx, tx_len);
delay(exec_delay);
/* FIXME: use max_delay */
do {
recv(rx, rx_len);
} while (rx[0] == 0);
if (rx[0] != rx_len)
return -1;
crc16(rx, (rx_len - 2), rx_crc);
if ((rx_crc[0] == rx[rx_len - 2]) && (rx_crc[1] == rx[rx_len - 1]))
return 0;
/* crc check failed */
return -1;
}
int ATSHA204::serial(uint8_t *buf) {
int rc, i = 0;
uint8_t tmp[4];
/* memory barrier to avoid buf writes re-ordering */
__asm__ __volatile__("":::"memory");
rc = read(0, 0, buf, 4);
if (rc) return rc;
i += 4;
rc = read(0, 0x02, buf + i, 4);
if (rc) return rc;
i += 4;
rc = read(0, 0x03, tmp, 4);
if (rc) return rc;
buf[i] = tmp[0];
return 0;
}
int ATSHA204::devRev(uint8_t *buf) {
int rc;
uint8_t cmd[7];
uint8_t resp[7];
memset(resp, 0x00, 7);
cmd[0] = 7;
cmd[1] = OP_DEV_REV;
cmd[2] = 0;
cmd[3] = 0;
cmd[4] = 0;
rc = exec(cmd, cmd[0], resp, 7, 1, 2);
memcpy(buf, resp + 1, 4);
return rc;
}
int ATSHA204::read(uint8_t zone, uint16_t addr, uint8_t *buf, uint8_t len) {
int rc;
uint8_t cmd[7];
uint8_t resp[len + 3];
memset(resp, 0x00, len + 3);
if (len == 32)
zone |= (1 << 8);
cmd[0] = 7;
cmd[1] = OP_READ;
cmd[2] = zone;
cmd[3] = addr;
cmd[4] = 0;
rc = exec(cmd, cmd[0], resp, len + 3, 1, 4);
memcpy(buf, resp + 1, len);
return rc;
}
int ATSHA204::write(uint8_t zone, uint8_t addr,
uint8_t *buf, uint8_t len, uint8_t *mac) {
int rc;
uint8_t cmd[72];
uint8_t resp[4];
memset(resp, 0x00, 4);
uint8_t data_size = len;
if ((len != 4) || (len != 32))
return -1;
if (len == 32)
zone |= (1 << 8);
if (mac)
data_size += 32;
cmd[0] = 7 + data_size;
cmd[1] = OP_READ;
cmd[2] = zone;
cmd[3] = addr;
cmd[4] = 0;
memcpy(cmd + 5, buf, len);
if (mac)
memcpy(cmd + 5 + len, mac, 32);
rc = exec(cmd, cmd[0], resp, 4, 4, 42);
if (cmd[1] != ERR_SUCCES)
return cmd[1];
return rc;
}
int ATSHA204::lock(uint8_t zone) {
uint8_t cmd[7];
uint8_t resp[7];
memset(resp, 0x00, 7);
/* FIXME: check CRC */
zone |= (1 << 8);
cmd[0] = 7;
cmd[1] = OP_LOCK;
cmd[2] = zone;
cmd[3] = 0;
cmd[4] = 0;
return exec(cmd, cmd[0], resp, 7, 5, 24);
}
int ATSHA204::rand(uint8_t update, uint8_t *buf) {
int rc;
uint8_t cmd[7];
uint8_t resp[35];
memset(resp, 0x00, 35);
cmd[0] = 7;
cmd[1] = OP_RANDOM;
cmd[2] = !update;
cmd[3] = 0;
cmd[4] = 0;
rc = exec(cmd, cmd[0], resp, 35, 11, 50);
memcpy(buf, resp + 1, 32);
return rc;
}
int ATSHA204::nonce(uint8_t mode, uint8_t *in, uint8_t *out) {
int rc;
uint8_t cmd[39];
uint8_t resp[35];
uint8_t data_size = 0;
uint8_t resp_size = 0;
switch (mode) {
case 0:
case 1:
data_size = 20;
resp_size = 35;
break;
case 3:
data_size = 32;
resp_size = 4;
break;
default:
return -1;
}
memset(resp, 0x00, resp_size);
cmd[0] = 7 + data_size;
cmd[1] = OP_NONCE;
cmd[2] = mode;
cmd[3] = 0;
cmd[4] = 0;
memcpy(cmd + 5, in, data_size);
rc = exec(cmd, cmd[0], resp, resp_size, 11, 50);
memcpy(out, resp + 1, resp_size - 3);
return rc;
}
int ATSHA204::deriveKey(uint8_t random, uint8_t target, uint8_t *mac) {
int rc;
uint8_t cmd[39];
uint8_t data_size = 0;
uint8_t resp[4];
memset(resp, 0x00, 4);
cmd[0] = 7 + data_size;
cmd[1] = OP_DERIVE_KEY;
cmd[2] = random;
cmd[3] = 0;
cmd[4] = 0;
rc = exec(cmd, cmd[0], resp, 4, 14, 62);
if (resp[1])
return resp[1];
return rc;
}
/*
* Arduino library for the ATSHA204 authentication chip.
*
* Copyright (c) 2013, Alessandro Ghedini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS 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.
*/
#ifndef ATSHA204_H
#define ATSHA204_H
class ATSHA204 {
public:
ATSHA204(int pin);
int sleep();
int wake();
int sync(uint8_t *buf, uint8_t len);
int serial(uint8_t *buf);
int devRev(uint8_t *buf);
int read(uint8_t zone, uint16_t addr,
uint8_t *buf, uint8_t len);
int write(uint8_t zone, uint8_t addr,
uint8_t *buf, uint8_t len, uint8_t *mac);
int lock(uint8_t zone);
int rand(uint8_t update, uint8_t *buf);
int nonce(uint8_t mode, uint8_t *in, uint8_t *out);
int deriveKey(uint8_t random, uint8_t target, uint8_t *macW);
private:
int pin;
uint8_t pin_bit;
volatile uint8_t *port_ddr, *port_out, *port_in;
void setPin(uint8_t is_high);
int send(uint8_t *buf, uint8_t len);
int recv(uint8_t *buf, uint8_t len);
int exec(uint8_t *tx, uint8_t tx_len,
uint8_t *rx, uint8_t rx_len,
uint8_t exec_delay, uint8_t max_delay);
};
#endif
@rafacouto
Copy link

Just a bug:

OP_MAC = 0x08,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment