28 changes: 14 additions & 14 deletions src/BLETypedCharacteristics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,72 +21,72 @@

#include "BLETypedCharacteristics.h"

BLEBoolCharacteristic::BLEBoolCharacteristic(const char* uuid, unsigned char properties) :
BLEBoolCharacteristic::BLEBoolCharacteristic(const char* uuid, unsigned int properties) :
BLETypedCharacteristic<bool>(uuid, properties)
{
}

BLEBooleanCharacteristic::BLEBooleanCharacteristic(const char* uuid, unsigned char properties) :
BLEBooleanCharacteristic::BLEBooleanCharacteristic(const char* uuid, unsigned int properties) :
BLETypedCharacteristic<bool>(uuid, properties)
{
}

BLECharCharacteristic::BLECharCharacteristic(const char* uuid, unsigned char properties) :
BLECharCharacteristic::BLECharCharacteristic(const char* uuid, unsigned int properties) :
BLETypedCharacteristic<char>(uuid, properties)
{
}

BLEUnsignedCharCharacteristic::BLEUnsignedCharCharacteristic(const char* uuid, unsigned char properties) :
BLEUnsignedCharCharacteristic::BLEUnsignedCharCharacteristic(const char* uuid, unsigned int properties) :
BLETypedCharacteristic<unsigned char>(uuid, properties)
{
}

BLEByteCharacteristic::BLEByteCharacteristic(const char* uuid, unsigned char properties) :
BLEByteCharacteristic::BLEByteCharacteristic(const char* uuid, unsigned int properties) :
BLETypedCharacteristic<byte>(uuid, properties)
{
}

BLEShortCharacteristic::BLEShortCharacteristic(const char* uuid, unsigned char properties) :
BLEShortCharacteristic::BLEShortCharacteristic(const char* uuid, unsigned int properties) :
BLETypedCharacteristic<short>(uuid, properties)
{
}

BLEUnsignedShortCharacteristic::BLEUnsignedShortCharacteristic(const char* uuid, unsigned char properties) :
BLEUnsignedShortCharacteristic::BLEUnsignedShortCharacteristic(const char* uuid, unsigned int properties) :
BLETypedCharacteristic<unsigned short>(uuid, properties)
{
}

BLEWordCharacteristic::BLEWordCharacteristic(const char* uuid, unsigned char properties) :
BLEWordCharacteristic::BLEWordCharacteristic(const char* uuid, unsigned int properties) :
BLETypedCharacteristic<word>(uuid, properties)
{
}

BLEIntCharacteristic::BLEIntCharacteristic(const char* uuid, unsigned char properties) :
BLEIntCharacteristic::BLEIntCharacteristic(const char* uuid, unsigned int properties) :
BLETypedCharacteristic<int>(uuid, properties)
{
}

BLEUnsignedIntCharacteristic::BLEUnsignedIntCharacteristic(const char* uuid, unsigned char properties) :
BLEUnsignedIntCharacteristic::BLEUnsignedIntCharacteristic(const char* uuid, unsigned int properties) :
BLETypedCharacteristic<unsigned int>(uuid, properties)
{
}

BLELongCharacteristic::BLELongCharacteristic(const char* uuid, unsigned char properties) :
BLELongCharacteristic::BLELongCharacteristic(const char* uuid, unsigned int properties) :
BLETypedCharacteristic<long>(uuid, properties)
{
}

BLEUnsignedLongCharacteristic::BLEUnsignedLongCharacteristic(const char* uuid, unsigned char properties) :
BLEUnsignedLongCharacteristic::BLEUnsignedLongCharacteristic(const char* uuid, unsigned int properties) :
BLETypedCharacteristic<unsigned long>(uuid, properties)
{
}

BLEFloatCharacteristic::BLEFloatCharacteristic(const char* uuid, unsigned char properties) :
BLEFloatCharacteristic::BLEFloatCharacteristic(const char* uuid, unsigned int properties) :
BLETypedCharacteristic<float>(uuid, properties)
{
}

BLEDoubleCharacteristic::BLEDoubleCharacteristic(const char* uuid, unsigned char properties) :
BLEDoubleCharacteristic::BLEDoubleCharacteristic(const char* uuid, unsigned int properties) :
BLETypedCharacteristic<double>(uuid, properties)
{
}
28 changes: 14 additions & 14 deletions src/BLETypedCharacteristics.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,72 +24,72 @@

class BLEBoolCharacteristic : public BLETypedCharacteristic<bool> {
public:
BLEBoolCharacteristic(const char* uuid, unsigned char properties);
BLEBoolCharacteristic(const char* uuid, unsigned int permissions);
};

class BLEBooleanCharacteristic : public BLETypedCharacteristic<boolean> {
public:
BLEBooleanCharacteristic(const char* uuid, unsigned char properties);
BLEBooleanCharacteristic(const char* uuid, unsigned int permissions);
};

class BLECharCharacteristic : public BLETypedCharacteristic<char> {
public:
BLECharCharacteristic(const char* uuid, unsigned char properties);
BLECharCharacteristic(const char* uuid, unsigned int permissions);
};

class BLEUnsignedCharCharacteristic : public BLETypedCharacteristic<unsigned char> {
public:
BLEUnsignedCharCharacteristic(const char* uuid, unsigned char properties);
BLEUnsignedCharCharacteristic(const char* uuid, unsigned int permissions);
};

class BLEByteCharacteristic : public BLETypedCharacteristic<byte> {
public:
BLEByteCharacteristic(const char* uuid, unsigned char properties);
BLEByteCharacteristic(const char* uuid, unsigned int permissions);
};

class BLEShortCharacteristic : public BLETypedCharacteristic<short> {
public:
BLEShortCharacteristic(const char* uuid, unsigned char properties);
BLEShortCharacteristic(const char* uuid, unsigned int permissions);
};

class BLEUnsignedShortCharacteristic : public BLETypedCharacteristic<unsigned short> {
public:
BLEUnsignedShortCharacteristic(const char* uuid, unsigned char properties);
BLEUnsignedShortCharacteristic(const char* uuid, unsigned int permissions);
};

class BLEWordCharacteristic : public BLETypedCharacteristic<word> {
public:
BLEWordCharacteristic(const char* uuid, unsigned char properties);
BLEWordCharacteristic(const char* uuid, unsigned int permissions);
};

class BLEIntCharacteristic : public BLETypedCharacteristic<int> {
public:
BLEIntCharacteristic(const char* uuid, unsigned char properties);
BLEIntCharacteristic(const char* uuid, unsigned int permissions);
};

class BLEUnsignedIntCharacteristic : public BLETypedCharacteristic<unsigned int> {
public:
BLEUnsignedIntCharacteristic(const char* uuid, unsigned char properties);
BLEUnsignedIntCharacteristic(const char* uuid, unsigned int permissions);
};

class BLELongCharacteristic : public BLETypedCharacteristic<long> {
public:
BLELongCharacteristic(const char* uuid, unsigned char properties);
BLELongCharacteristic(const char* uuid, unsigned int permissions);
};

class BLEUnsignedLongCharacteristic : public BLETypedCharacteristic<unsigned long> {
public:
BLEUnsignedLongCharacteristic(const char* uuid, unsigned char properties);
BLEUnsignedLongCharacteristic(const char* uuid, unsigned int permissions);
};

class BLEFloatCharacteristic : public BLETypedCharacteristic<float> {
public:
BLEFloatCharacteristic(const char* uuid, unsigned char properties);
BLEFloatCharacteristic(const char* uuid, unsigned int permissions);
};

class BLEDoubleCharacteristic : public BLETypedCharacteristic<double> {
public:
BLEDoubleCharacteristic(const char* uuid, unsigned char properties);
BLEDoubleCharacteristic(const char* uuid, unsigned int permissions);
};

#endif
18 changes: 11 additions & 7 deletions src/local/BLELocalCharacteristic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,21 @@

#include "BLELocalCharacteristic.h"

BLELocalCharacteristic::BLELocalCharacteristic(const char* uuid, uint8_t properties, int valueSize, bool fixedLength) :
BLELocalCharacteristic::BLELocalCharacteristic(const char* uuid, uint16_t permissions, int valueSize, bool fixedLength) :
BLELocalAttribute(uuid),
_properties(properties),
_properties((uint8_t)(permissions&0x000FF)),
_valueSize(min(valueSize, 512)),
_valueLength(0),
_fixedLength(fixedLength),
_handle(0x0000),
_broadcast(false),
_written(false),
_cccdValue(0x0000)
_cccdValue(0x0000),
_permissions((uint8_t)((permissions&0xFF00)>>8))
{
memset(_eventHandlers, 0x00, sizeof(_eventHandlers));

if (properties & (BLENotify | BLEIndicate)) {
if (permissions & (BLENotify | BLEIndicate)) {
BLELocalDescriptor* cccd = new BLELocalDescriptor("2902", (uint8_t*)&_cccdValue, sizeof(_cccdValue));

_descriptors.add(cccd);
Expand All @@ -51,12 +52,11 @@ BLELocalCharacteristic::BLELocalCharacteristic(const char* uuid, uint8_t propert
_value = (uint8_t*)malloc(valueSize);
}

BLELocalCharacteristic::BLELocalCharacteristic(const char* uuid, uint8_t properties, const char* value) :
BLELocalCharacteristic(uuid, properties, strlen(value))
BLELocalCharacteristic::BLELocalCharacteristic(const char* uuid, uint16_t permissions, const char* value) :
BLELocalCharacteristic(uuid, permissions, strlen(value))
{
writeValue(value);
}

BLELocalCharacteristic::~BLELocalCharacteristic()
{
for (unsigned int i = 0; i < descriptorCount(); i++) {
Expand Down Expand Up @@ -84,6 +84,10 @@ uint8_t BLELocalCharacteristic::properties() const
return _properties;
}

uint8_t BLELocalCharacteristic::permissions() const {
return _permissions;
}

int BLELocalCharacteristic::valueSize() const
{
return _valueSize;
Expand Down
6 changes: 4 additions & 2 deletions src/local/BLELocalCharacteristic.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,14 @@ class BLELocalDescriptor;

class BLELocalCharacteristic : public BLELocalAttribute {
public:
BLELocalCharacteristic(const char* uuid, uint8_t properties, int valueSize, bool fixedLength = false);
BLELocalCharacteristic(const char* uuid, uint8_t properties, const char* value);
BLELocalCharacteristic(const char* uuid, uint16_t permissions, int valueSize, bool fixedLength = false);
BLELocalCharacteristic(const char* uuid, uint16_t permissions, const char* value);
virtual ~BLELocalCharacteristic();

virtual enum BLEAttributeType type() const;

uint8_t properties() const;
uint8_t permissions() const;

int valueSize() const;
const uint8_t* value() const;
Expand Down Expand Up @@ -75,6 +76,7 @@ class BLELocalCharacteristic : public BLELocalAttribute {

private:
uint8_t _properties;
uint8_t _permissions;
int _valueSize;
uint8_t* _value;
uint16_t _valueLength;
Expand Down
103 changes: 103 additions & 0 deletions src/local/BLELocalDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ int BLELocalDevice::begin()
end();
return 0;
}
if (HCI.setLeEventMask(0x00000000000003FF) != 0) {
end();
return 0;
}

uint16_t pktLen;
uint8_t maxPkt;
Expand All @@ -117,6 +121,59 @@ int BLELocalDevice::begin()
return 0;
}

/// The HCI should allow automatic address resolution.

// // If we have callbacks to remember bonded devices:
// if(HCI._getIRKs!=0){
// uint8_t nIRKs = 0;
// uint8_t** BADDR_Type = new uint8_t*;
// uint8_t*** BADDRs = new uint8_t**;
// uint8_t*** IRKs = new uint8_t**;
// uint8_t* memcheck;


// if(!HCI._getIRKs(&nIRKs, BADDR_Type, BADDRs, IRKs)){
// Serial.println("error");
// }
// for(int i=0; i<nIRKs; i++){
// Serial.print("Baddr type: ");
// Serial.println((*BADDR_Type)[i]);
// Serial.print("BADDR:");
// for(int k=0; k<6; k++){
// Serial.print(", 0x");
// Serial.print((*BADDRs)[i][k],HEX);
// }
// Serial.println();
// Serial.print("IRK:");
// for(int k=0; k<16; k++){
// Serial.print(", 0x");
// Serial.print((*IRKs)[i][k],HEX);
// }
// Serial.println();

// // save this
// uint8_t zeros[16];
// for(int k=0; k<16; k++) zeros[15-k] = 0;

// // HCI.leAddResolvingAddress((*BADDR_Type)[i],(*BADDRs)[i],(*IRKs)[i], zeros);

// delete[] (*BADDRs)[i];
// delete[] (*IRKs)[i];
// }
// delete[] (*BADDR_Type);
// delete BADDR_Type;
// delete[] (*BADDRs);
// delete BADDRs;
// delete[] (*IRKs);
// delete IRKs;

// memcheck = new uint8_t[1];
// Serial.print("nIRK location: 0x");
// Serial.println((int)memcheck,HEX);
// delete[] memcheck;

// }

GATT.begin();

return 1;
Expand Down Expand Up @@ -157,6 +214,16 @@ bool BLELocalDevice::connected() const
return ATT.connected();
}

/*
* Whether there is at least one paired device
*/
bool BLELocalDevice::paired()
{
HCI.poll();

return ATT.paired();
}

bool BLELocalDevice::disconnect()
{
return ATT.disconnect();
Expand Down Expand Up @@ -338,6 +405,42 @@ void BLELocalDevice::setTimeout(unsigned long timeout)
ATT.setTimeout(timeout);
}

/*
* Control whether pairing is allowed or rejected
* Use true/false or the Pairable enum
*/
void BLELocalDevice::setPairable(uint8_t pairable)
{
L2CAPSignaling.setPairingEnabled(pairable);
}

/*
* Whether pairing is currently allowed
*/
bool BLELocalDevice::pairable()
{
return L2CAPSignaling.isPairingEnabled();
}

void BLELocalDevice::setGetIRKs(int (*getIRKs)(uint8_t* nIRKs, uint8_t** BADDR_type, uint8_t*** BADDRs, uint8_t*** IRKs)){
HCI._getIRKs = getIRKs;
}
void BLELocalDevice::setGetLTK(int (*getLTK)(uint8_t* BADDR, uint8_t* LTK)){
HCI._getLTK = getLTK;
}
void BLELocalDevice::setStoreLTK(int (*storeLTK)(uint8_t*, uint8_t*)){
HCI._storeLTK = storeLTK;
}
void BLELocalDevice::setStoreIRK(int (*storeIRK)(uint8_t*, uint8_t*)){
HCI._storeIRK = storeIRK;
}
void BLELocalDevice::setDisplayCode(void (*displayCode)(uint32_t confirmationCode)){
HCI._displayCode = displayCode;
}
void BLELocalDevice::setBinaryConfirmPairing(bool (*binaryConfirmPairing)()){
HCI._binaryConfirmPairing = binaryConfirmPairing;
}

void BLELocalDevice::debug(Stream& stream)
{
HCI.debug(stream);
Expand Down
28 changes: 28 additions & 0 deletions src/local/BLELocalDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@
#include "BLEService.h"
#include "BLEAdvertisingData.h"

enum Pairable {
NO = 0,
YES = 1,
ONCE = 2,
};

class BLELocalDevice {
public:
BLELocalDevice();
Expand Down Expand Up @@ -81,6 +87,28 @@ class BLELocalDevice {
virtual void debug(Stream& stream);
virtual void noDebug();

virtual void setPairable(uint8_t pairable);
virtual bool pairable();
virtual bool paired();

// address - The mac to store
// IRK - The IRK to store with this mac
virtual void setStoreIRK(int (*storeIRK)(uint8_t* address, uint8_t* IRK));
// nIRKs - the number of IRKs being provided.
// BDAddrType - an array containing the type of each address (0 public, 1 static random)
// BDAddrs - an array containing the list of addresses
virtual void setGetIRKs(int (*getIRKs)(uint8_t* nIRKs, uint8_t** BDAddrType, uint8_t*** BDAddrs, uint8_t*** IRKs));
// address - the address to store [6 bytes]
// LTK - the LTK to store with this mac [16 bytes]
virtual void setStoreLTK(int (*storeLTK)(uint8_t* address, uint8_t* LTK));
// address - The mac address needing its LTK
// LTK - 16 octet LTK for the mac address
virtual void setGetLTK(int (*getLTK)(uint8_t* address, uint8_t* LTK));

virtual void setDisplayCode(void (*displayCode)(uint32_t confirmationCode));
virtual void setBinaryConfirmPairing(bool (*binaryConfirmPairing)());
uint8_t BDaddress[6];

protected:
virtual BLEAdvertisingData& getAdvertisingData();
virtual BLEAdvertisingData& getScanResponseData();
Expand Down
5 changes: 3 additions & 2 deletions src/remote/BLERemoteCharacteristic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@
#include "BLERemoteCharacteristic.h"

BLERemoteCharacteristic::BLERemoteCharacteristic(const uint8_t uuid[], uint8_t uuidLen, uint16_t connectionHandle,
uint16_t startHandle, uint8_t properties, uint16_t valueHandle) :
uint16_t startHandle, uint16_t permissions, uint16_t valueHandle) :
BLERemoteAttribute(uuid, uuidLen),
_connectionHandle(connectionHandle),
_startHandle(startHandle),
_properties(properties),
_properties((uint8_t)(permissions & 0x00FF)),
_permissions((uint8_t)((permissions & 0xFF00)>>8)),
_valueHandle(valueHandle),
_value(NULL),
_valueLength(0),
Expand Down
4 changes: 3 additions & 1 deletion src/remote/BLERemoteCharacteristic.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@

class BLERemoteCharacteristic : public BLERemoteAttribute {
public:
BLERemoteCharacteristic(const uint8_t uuid[], uint8_t uuidLen, uint16_t connectionHandle, uint16_t startHandle, uint8_t properties, uint16_t valueHandle);
BLERemoteCharacteristic(const uint8_t uuid[], uint8_t uuidLen, uint16_t connectionHandle, uint16_t startHandle, uint16_t permissions, uint16_t valueHandle);
virtual ~BLERemoteCharacteristic();

uint8_t properties() const;
uint8_t permissions() const;

const uint8_t* value() const;
int valueLength() const;
Expand Down Expand Up @@ -66,6 +67,7 @@ class BLERemoteCharacteristic : public BLERemoteAttribute {
uint16_t _connectionHandle;
uint16_t _startHandle;
uint8_t _properties;
uint8_t _permissions;
uint16_t _valueHandle;

uint8_t* _value;
Expand Down
262 changes: 256 additions & 6 deletions src/utility/ATT.cpp

Large diffs are not rendered by default.

37 changes: 36 additions & 1 deletion src/utility/ATT.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@
#include <Arduino.h>

#include "BLEDevice.h"
#include "keyDistribution.h"

#define ATT_CID 0x0004
#define BLE_CTL 0x0008

#if DM_CONN_MAX
#define ATT_MAX_PEERS DM_CONN_MAX // Mbed + Cordio
Expand All @@ -34,6 +36,17 @@
#define ATT_MAX_PEERS 8
#endif

enum PEER_ENCRYPTION {
NO_ENCRYPTION = 0,
PAIRING_REQUEST = 1 << 0,
REQUESTED_ENCRYPTION = 1 << 1,
SENT_PUBKEY = 1 << 2,
DH_KEY_CALULATED = 1 << 3,
RECEIVED_DH_CHECK = 1 << 4,
SENT_DH_CHECK = 1 << 5,
ENCRYPTED_AES = 1 << 7
};

class BLERemoteDevice;

class ATTClass {
Expand Down Expand Up @@ -62,6 +75,8 @@ class ATTClass {
virtual bool connected() const;
virtual bool connected(uint8_t addressType, const uint8_t address[6]) const;
virtual bool connected(uint16_t handle) const;
virtual bool paired() const;
virtual bool paired(uint16_t handle) const;
virtual uint16_t mtu(uint16_t handle) const;

virtual bool disconnect();
Expand All @@ -76,7 +91,24 @@ class ATTClass {
virtual int readReq(uint16_t connectionHandle, uint16_t handle, uint8_t responseBuffer[]);
virtual int writeReq(uint16_t connectionHandle, uint16_t handle, const uint8_t* data, uint8_t dataLen, uint8_t responseBuffer[]);
virtual void writeCmd(uint16_t connectionHandle, uint16_t handle, const uint8_t* data, uint8_t dataLen);

virtual int setPeerEncryption(uint16_t connectionHandle, uint8_t encryption);
uint8_t getPeerEncryption(uint16_t connectionHandle);
uint16_t getPeerEncrptingConnectionHandle();
virtual int getPeerAddr(uint16_t connectionHandle, uint8_t peerAddr[]);
virtual int getPeerAddrWithType(uint16_t connectionHandle, uint8_t peerAddr[]);
virtual int setPeerIOCap(uint16_t connectionHandle, uint8_t IOCap[]);
virtual int getPeerIOCap(uint16_t connectionHandle, uint8_t IOCap[]);
virtual int getPeerResolvedAddress(uint16_t connectionHandle, uint8_t* resolvedAddress);
uint8_t holdBuffer[64];
uint8_t writeBuffer[64];
uint8_t holdBufferSize;
uint8_t writeBufferSize;
virtual int processWriteBuffer();
KeyDistribution remoteKeyDistribution;
KeyDistribution localKeyDistribution;
uint8_t peerIRK[16];
/// This is just a random number... Not sure it has use unless privacy mode is active.
uint8_t localIRK[16] = {0x54,0x83,0x63,0x7c,0xc5,0x1e,0xf7,0xec,0x32,0xdd,0xad,0x51,0x89,0x4b,0x9e,0x07};
private:
virtual void error(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]);
virtual void mtuReq(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]);
Expand Down Expand Up @@ -117,8 +149,11 @@ class ATTClass {
uint8_t role;
uint8_t addressType;
uint8_t address[6];
uint8_t resolvedAddress[6];
uint16_t mtu;
BLERemoteDevice* device;
uint8_t encryption;
uint8_t IOCap[3];
} _peers[ATT_MAX_PEERS];

volatile bool _cnf;
Expand Down
965 changes: 891 additions & 74 deletions src/utility/HCI.cpp

Large diffs are not rendered by default.

71 changes: 68 additions & 3 deletions src/utility/HCI.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,35 @@
#define _HCI_H_

#include <Arduino.h>
#include "bitDescriptions.h"

#include "L2CAPSignaling.h"

#define OGF_LINK_CTL 0x01
#define OGF_HOST_CTL 0x03
#define OGF_INFO_PARAM 0x04
#define OGF_STATUS_PARAM 0x05
#define OGF_LE_CTL 0x08

enum LE_COMMAND {
ENCRYPT = 0x0017,
RANDOM = 0x0018,
LONG_TERM_KEY_REPLY = 0x001A,
LONG_TERM_KEY_NEGATIVE_REPLY = 0x1B,
READ_LOCAL_P256 = 0x0025,
GENERATE_DH_KEY_V1 = 0x0026,
GENERATE_DH_KEY_V2 = 0x005E
};
enum LE_META_EVENT {
CONN_COMPLETE = 0x01,
ADVERTISING_REPORT = 0x02,
LONG_TERM_KEY_REQUEST = 0x05,
REMOTE_CONN_PARAM_REQ = 0x06,
READ_LOCAL_P256_COMPLETE = 0x08,
GENERATE_DH_KEY_COMPLETE = 0x09
};
String metaEventToString(LE_META_EVENT event);
String commandToString(LE_COMMAND command);

class HCIClass {
public:
Expand All @@ -36,12 +65,14 @@ class HCIClass {
virtual int reset();
virtual int readLocalVersion(uint8_t& hciVer, uint16_t& hciRev, uint8_t& lmpVer,
uint16_t& manufacturer, uint16_t& lmpSubVer);

virtual int readBdAddr(uint8_t addr[6]);
virtual int readBdAddr();

virtual int readRssi(uint16_t handle);

virtual int setEventMask(uint64_t eventMask);

virtual int setLeEventMask(uint64_t leEventMask);
virtual int readLeBufferSize(uint16_t& pktLen, uint8_t& maxPkt);
virtual int leSetRandomAddress(uint8_t addr[6]);
virtual int leSetAdvertisingParameters(uint16_t minInterval, uint16_t maxInterval,
Expand All @@ -62,7 +93,22 @@ class HCIClass {
virtual int leConnUpdate(uint16_t handle, uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t supervisionTimeout);
virtual int leCancelConn();

virtual int leEncrypt(uint8_t* Key, uint8_t* plaintext, uint8_t* status, uint8_t* ciphertext);
// Generate a 64 bit random number
virtual int leRand(uint8_t rand[]);
virtual AuthReq localAuthreq();
virtual uint8_t localIOCap();

virtual int saveNewAddress(uint8_t addressType, uint8_t* address, uint8_t* peerIrk, uint8_t* remoteIrk);
virtual int leAddResolvingAddress(uint8_t addressType, uint8_t* address, uint8_t* peerIrk, uint8_t* remoteIrk);
virtual int leStopResolvingAddresses();
virtual int leStartResolvingAddresses();
virtual int leReadPeerResolvableAddress(uint8_t peerAddressType, uint8_t* peerIdentityAddress, uint8_t* peerResolvableAddress);

virtual int readStoredLKs();
virtual int readStoredLK(uint8_t BD_ADDR[], uint8_t read_all = 0);
virtual int writeLK(uint8_t peerAddress[], uint8_t LK[]);
virtual int tryResolveAddress(uint8_t* BDAddr, uint8_t* address);

virtual int sendAclPkt(uint16_t handle, uint8_t cid, uint8_t plen, void* data);

Expand All @@ -71,8 +117,27 @@ class HCIClass {
virtual void debug(Stream& stream);
virtual void noDebug();

private:
// TODO: Send command be private again & use ATT implementation of send command within ATT.
virtual int sendCommand(uint16_t opcode, uint8_t plen = 0, void* parameters = NULL);
uint8_t remotePublicKeyBuffer[64];
uint8_t localPublicKeyBuffer[64];
uint8_t remoteDHKeyCheckBuffer[16];
uint8_t Na[16];
uint8_t Nb[16];
uint8_t DHKey[32];
uint8_t localAddr[6];
uint8_t LTK[16];
virtual int getLTK(uint8_t* address, uint8_t* LTK);
virtual int storeLTK(uint8_t* address, uint8_t* LTK);
virtual int storeIRK(uint8_t* address, uint8_t* IRK);
int (*_storeIRK)(uint8_t* address, uint8_t* peerIrk) = 0;
int (*_getIRKs)(uint8_t* nIRKs,uint8_t** BADDR_type, uint8_t*** BADDRs, uint8_t*** IRKs) = 0;
int (*_storeLTK)(uint8_t*, uint8_t*) = 0;
int (*_getLTK)(uint8_t*, uint8_t*) = 0;
void (*_displayCode)(uint32_t confirmationCode) = 0;
bool (*_binaryConfirmPairing)() = 0;

private:

virtual void handleAclDataPkt(uint8_t plen, uint8_t pdata[]);
virtual void handleNumCompPkts(uint16_t handle, uint16_t numPkts);
Expand Down
345 changes: 342 additions & 3 deletions src/utility/L2CAPSignaling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,21 @@
*/

#include "HCI.h"

#include "ATT.h"
#include "btct.h"
#include "L2CAPSignaling.h"

#include "keyDistribution.h"
#include "bitDescriptions.h"
#define CONNECTION_PARAMETER_UPDATE_REQUEST 0x12
#define CONNECTION_PARAMETER_UPDATE_RESPONSE 0x13

//#define _BLE_TRACE_

L2CAPSignalingClass::L2CAPSignalingClass() :
_minInterval(0),
_maxInterval(0),
_supervisionTimeout(0)
_supervisionTimeout(0),
_pairing_enabled(1)
{
}

Expand Down Expand Up @@ -108,6 +113,331 @@ void L2CAPSignalingClass::handleData(uint16_t connectionHandle, uint8_t dlen, ui
connectionParameterUpdateResponse(connectionHandle, identifier, length, data);
}
}
void L2CAPSignalingClass::handleSecurityData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[])
{
struct __attribute__ ((packed)) L2CAPSignalingHdr {
uint8_t code;
uint8_t data[64];
} *l2capSignalingHdr = (L2CAPSignalingHdr*)data;
#ifdef _BLE_TRACE_
Serial.print("dlen: ");
Serial.println(dlen);
#endif
uint8_t code = l2capSignalingHdr->code;

#ifdef _BLE_TRACE_
Serial.print("handleSecurityData: code: 0x");
Serial.println(code, HEX);
Serial.print("rx security:");
btct.printBytes(data,dlen);
#endif
if (code == CONNECTION_PAIRING_REQUEST) {

if (isPairingEnabled()){
if (_pairing_enabled >= 2) _pairing_enabled = 0; // 2 = pair once only

// 0x1
struct __attribute__ ((packed)) PairingRequest {
uint8_t ioCapability;
uint8_t oobDataFlag;
uint8_t authReq;
uint8_t maxEncSize;
uint8_t initiatorKeyDistribution;
uint8_t responderKeyDistribution;
} *pairingRequest = (PairingRequest*)l2capSignalingHdr->data;

KeyDistribution responseKD = KeyDistribution();
responseKD.setIdKey(true);

ATT.remoteKeyDistribution = responseKD;// KeyDistribution(pairingRequest->initiatorKeyDistribution);
ATT.localKeyDistribution = responseKD; //KeyDistribution(pairingRequest->responderKeyDistribution);
// KeyDistribution rkd(pairingRequest->responderKeyDistribution);
AuthReq req(pairingRequest->authReq);
#ifdef _BLE_TRACE_
Serial.print("Req has properties: ");
Serial.print(req.Bonding()?"bonding, ":"no bonding, ");
Serial.print(req.CT2()?"CT2, ":"no CT2, ");
Serial.print(req.KeyPress()?"KeyPress, ":"no KeyPress, ");
Serial.print(req.MITM()?"MITM, ":"no MITM, ");
Serial.print(req.SC()?"SC, ":"no SC, ");
#endif

uint8_t peerIOCap[3];
peerIOCap[0] = pairingRequest->authReq;
peerIOCap[1] = pairingRequest->oobDataFlag;
peerIOCap[2] = pairingRequest->ioCapability;
ATT.setPeerIOCap(connectionHandle, peerIOCap);
ATT.setPeerEncryption(connectionHandle, ATT.getPeerEncryption(connectionHandle) | PEER_ENCRYPTION::PAIRING_REQUEST);
#ifdef _BLE_TRACE_
Serial.print("Peer encryption : 0b");
Serial.println(ATT.getPeerEncryption(connectionHandle), BIN);
#endif
struct __attribute__ ((packed)) PairingResponse {
uint8_t code;
uint8_t ioCapability;
uint8_t oobDataFlag;
uint8_t authReq;
uint8_t maxEncSize;
uint8_t initiatorKeyDistribution;
uint8_t responderKeyDistribution;
} response = { CONNECTION_PAIRING_RESPONSE, HCI.localIOCap(), 0, HCI.localAuthreq().getOctet(), 0x10, responseKD.getOctet(), responseKD.getOctet()};

HCI.sendAclPkt(connectionHandle, SECURITY_CID, sizeof(response), &response);

} else {
// Pairing not enabled
uint8_t ret[2] = {CONNECTION_PAIRING_FAILED, 0x05}; // reqect pairing
HCI.sendAclPkt(connectionHandle, SECURITY_CID, sizeof(ret), ret);
ATT.setPeerEncryption(connectionHandle, NO_ENCRYPTION);
}
}
else if (code == CONNECTION_PAIRING_RANDOM)
{
struct __attribute__ ((packed)) PairingRandom {
uint8_t Na[16];
} *pairingRandom = (PairingRandom*)l2capSignalingHdr->data;
for(int i=0; i<16; i++){
HCI.Na[15-i] = pairingRandom->Na[i];
}
#ifdef _BLE_TRACE_
Serial.println("[Info] Pairing random.");
#endif
struct __attribute__ ((packed)) PairingResponse {
uint8_t code;
uint8_t Nb[16];
} response = { CONNECTION_PAIRING_RANDOM, 0};
for(int i=0; i< 16; i++) response.Nb[15-i] = HCI.Nb[i];

HCI.sendAclPkt(connectionHandle, SECURITY_CID, sizeof(response), &response);

// We now have all needed for compare value
uint8_t g2Result[4];
uint8_t U[32];
uint8_t V[32];

for(int i=0; i<32; i++){
U[31-i] = HCI.remotePublicKeyBuffer[i];
V[31-i] = HCI.localPublicKeyBuffer[i];
}

btct.g2(U,V,HCI.Na,HCI.Nb, g2Result);
uint32_t result = 0;
for(int i=0; i<4; i++) result += g2Result[3-i] << 8*i;

#ifdef _BLE_TRACE_
Serial.print("U : ");
btct.printBytes(U,32);
Serial.print("V : ");
btct.printBytes(V,32);
Serial.print("X : ");
btct.printBytes(X,16);
Serial.print("Y : ");
btct.printBytes(Y,16);
Serial.print("g2res : ");
btct.printBytes(g2Result,4);
Serial.print("Result : ");
Serial.println(result);
#endif

if(HCI._displayCode!=0){
HCI._displayCode(result%1000000);
}
if(HCI._binaryConfirmPairing!=0){
if(!HCI._binaryConfirmPairing()){
#ifdef _BLE_TRACE_
Serial.println("User rejection");
#endif
uint8_t rejection[2];
rejection[0] = CONNECTION_PAIRING_FAILED;
rejection[1] = 0x0C; // Numeric comparison failed
HCI.sendAclPkt(connectionHandle, SECURITY_CID, 2, rejection);
ATT.setPeerEncryption(connectionHandle, PEER_ENCRYPTION::NO_ENCRYPTION);
}else{
#ifdef _BLE_TRACE_
Serial.println("User did confirm");
#endif
}
}
}
else if (code == CONNECTION_PAIRING_RESPONSE)
{
}
else if(code == CONNECTION_PAIRING_FAILED)
{
struct __attribute__ ((packed)) PairingFailed
{
uint8_t code;
uint8_t reason;
} *pairingFailed = (PairingFailed*)data;
#ifdef _BLE_TRACE_
Serial.print("Pairing failed with code: 0x");
Serial.println(pairingFailed->reason,HEX);
#endif
ATT.setPeerEncryption(connectionHandle, PEER_ENCRYPTION::NO_ENCRYPTION);
}
else if (code == CONNECTION_IDENTITY_INFORMATION){
struct __attribute__ ((packed)) IdentityInformation {
uint8_t code;
uint8_t PeerIRK[16];
} *identityInformation = (IdentityInformation*)data;
for(int i=0; i<16; i++) ATT.peerIRK[15-i] = identityInformation->PeerIRK[i];
#ifdef _BLE_TRACE_
Serial.println("Saved peer IRK");
#endif
}
else if (code == CONNECTION_IDENTITY_ADDRESS){
struct __attribute__ ((packed)) IdentityAddress {
uint8_t code;
uint8_t addressType;
uint8_t address[6];
} *identityAddress = (IdentityAddress*)data;
// we can save this information now.
uint8_t peerAddress[6];
for(int i; i<6; i++) peerAddress[5-i] = identityAddress->address[i];

HCI.saveNewAddress(identityAddress->addressType, peerAddress, ATT.peerIRK, ATT.localIRK);
if(HCI._storeLTK!=0){
HCI._storeLTK(peerAddress, HCI.LTK);
}
}
else if (code == CONNECTION_PAIRING_PUBLIC_KEY){
/// Received a public key
struct __attribute__ ((packed)) ConnectionPairingPublicKey {
uint8_t x[32];
uint8_t y[32];
} *connectionPairingPublicKey = (ConnectionPairingPublicKey*)l2capSignalingHdr->data;
struct __attribute__ ((packed)) GenerateDHKeyCommand {
uint8_t x[32];
uint8_t y[32];
} generateDHKeyCommand = {
0x00,
0x00,
};
memcpy(generateDHKeyCommand.x,connectionPairingPublicKey->x,32);
memcpy(generateDHKeyCommand.y,connectionPairingPublicKey->y,32);
struct __attribute__ ((packed)) ReadPublicKeyCommand {
uint8_t code;
} readPublicKeyCommand = {
LE_COMMAND::READ_LOCAL_P256,
};

if(ATT.setPeerEncryption(connectionHandle, ATT.getPeerEncryption(connectionHandle) | PEER_ENCRYPTION::REQUESTED_ENCRYPTION)){
#ifdef _BLE_TRACE_
Serial.println("[Info] Pairing public key");
Serial.println("Requested encryption stored.");
#endif
}else{
#ifdef _BLE_TRACE_
Serial.println("[Info] Pairing public key");
Serial.print("Failed to store encryption request with handle: 0x");
Serial.println(connectionHandle,HEX);
#endif
}

memcpy(HCI.remotePublicKeyBuffer,&generateDHKeyCommand,sizeof(generateDHKeyCommand));
HCI.sendCommand( (OGF_LE_CTL << 10 )| LE_COMMAND::READ_LOCAL_P256, 0);
}
else if(code == CONNECTION_PAIRING_DHKEY_CHECK)
{
uint8_t RemoteDHKeyCheck[16];
for(int i=0; i<16; i++) RemoteDHKeyCheck[15-i] = l2capSignalingHdr->data[i];


#ifdef _BLE_TRACE_
Serial.println("[Info] DH Key check");
Serial.print("Remote DHKey Check: ");
btct.printBytes(RemoteDHKeyCheck, 16);
#endif



uint8_t encryptionState = ATT.getPeerEncryption(connectionHandle) | PEER_ENCRYPTION::RECEIVED_DH_CHECK;
ATT.setPeerEncryption(connectionHandle, encryptionState);
if((encryptionState & PEER_ENCRYPTION::DH_KEY_CALULATED) == 0){
#ifdef _BLE_TRACE_
Serial.println("DHKey not yet ready, will calculate f5, f6 later");
#endif
// store RemoteDHKeyCheck for later check
memcpy(HCI.remoteDHKeyCheckBuffer,RemoteDHKeyCheck,16);

} else {
// We've already calculated the DHKey so we can calculate our check and send it.
smCalculateLTKandConfirm(connectionHandle, RemoteDHKeyCheck);

}
}
}

void L2CAPSignalingClass::smCalculateLTKandConfirm(uint16_t handle, uint8_t expectedEa[])
{ // Authentication stage 2: LTK Calculation

uint8_t localAddress[7];
uint8_t remoteAddress[7];
ATT.getPeerAddrWithType(handle, remoteAddress);

HCI.readBdAddr();
memcpy(&localAddress[1],HCI.localAddr,6);
localAddress[0] = 0; // IOT 33 uses a static address // TODO: confirm for Nano BLE

// Compute the LTK and MacKey
uint8_t MacKey[16];
btct.f5(HCI.DHKey, HCI.Na, HCI.Nb, remoteAddress, localAddress, MacKey, HCI.LTK);

// Compute Ea and Eb
uint8_t Ea[16];
uint8_t Eb[16];
uint8_t R[16];
uint8_t MasterIOCap[3];
uint8_t SlaveIOCap[3] = {HCI.localAuthreq().getOctet(), 0x0, HCI.localIOCap()};

ATT.getPeerIOCap(handle, MasterIOCap);
for(int i=0; i<16; i++) R[i] = 0;

btct.f6(MacKey, HCI.Na,HCI.Nb,R, MasterIOCap, remoteAddress, localAddress, Ea);
btct.f6(MacKey, HCI.Nb,HCI.Na,R, SlaveIOCap, localAddress, remoteAddress, Eb);

#ifdef _BLE_TRACE_
Serial.println("Calculate and confirm LTK via f5, f6:");
Serial.print("DHKey : "); btct.printBytes(HCI.DHKey,32);
Serial.print("Na : "); btct.printBytes(HCI.Na,16);
Serial.print("Nb : "); btct.printBytes(HCI.Nb,16);
Serial.print("MacKey : "); btct.printBytes(MacKey,16);
Serial.print("LTK : "); btct.printBytes(HCI.LTK,16);
Serial.print("Expected Ea: "); btct.printBytes(expectedEa, 16);
Serial.print("Ea : "); btct.printBytes(Ea, 16);
Serial.print("Eb : "); btct.printBytes(Eb,16);
Serial.print("Local Addr : "); btct.printBytes(localAddress, 7);
Serial.print("LocalIOCap : "); btct.printBytes(SlaveIOCap, 3);
Serial.print("MasterAddr : "); btct.printBytes(remoteAddress, 7);
Serial.print("MasterIOCAP: "); btct.printBytes(MasterIOCap, 3);
#endif

// Check if Ea = expectedEa
if (memcmp(Ea, expectedEa, 16) == 0){
// Check ok
// Send our confirmation value to complete authentication stage 2
uint8_t ret[17];
ret[0] = CONNECTION_PAIRING_DHKEY_CHECK;
for(int i=0; i<sizeof(Eb); i++){
ret[sizeof(Eb)-i] = Eb[i];
}
HCI.sendAclPkt(handle, SECURITY_CID, sizeof(ret), ret );
uint8_t encryption = ATT.getPeerEncryption(handle) | PEER_ENCRYPTION::SENT_DH_CHECK;
ATT.setPeerEncryption(handle, encryption);
#ifdef _BLE_TRACE_
Serial.println("DHKey check ok - send Eb back");
#endif

} else {
// Check failed, abort pairing
uint8_t ret[2] = {CONNECTION_PAIRING_FAILED, 0x0B}; // 0x0B = DHKey Check Failed
HCI.sendAclPkt(handle, SECURITY_CID, sizeof(ret), ret);
ATT.setPeerEncryption(handle, NO_ENCRYPTION);
#ifdef _BLE_TRACE_
Serial.println("Error: DHKey check failed - Aborting");
#endif
}
}

void L2CAPSignalingClass::removeConnection(uint8_t /*handle*/, uint16_t /*reason*/)
{
Expand All @@ -124,6 +454,15 @@ void L2CAPSignalingClass::setSupervisionTimeout(uint16_t supervisionTimeout)
_supervisionTimeout = supervisionTimeout;
}

void L2CAPSignalingClass::setPairingEnabled(uint8_t enabled)
{
_pairing_enabled = enabled;
}
bool L2CAPSignalingClass::isPairingEnabled()
{
return _pairing_enabled > 0;
}

void L2CAPSignalingClass::connectionParameterUpdateRequest(uint16_t handle, uint8_t identifier, uint8_t dlen, uint8_t data[])
{
struct __attribute__ ((packed)) L2CAPConnectionParameterUpdateRequest {
Expand Down
39 changes: 39 additions & 0 deletions src/utility/L2CAPSignaling.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,33 @@
#include <Arduino.h>

#define SIGNALING_CID 0x0005
#define SECURITY_CID 0x0006


#define CONNECTION_PAIRING_REQUEST 0x01
#define CONNECTION_PAIRING_RESPONSE 0x02
#define CONNECTION_PAIRING_CONFIRM 0x03
#define CONNECTION_PAIRING_RANDOM 0x04
#define CONNECTION_PAIRING_FAILED 0x05
#define CONNECTION_ENCRYPTION_INFORMATION 0x06
#define CONNECTION_MASTER_IDENTIFICATION 0x07
#define CONNECTION_IDENTITY_INFORMATION 0x08
#define CONNECTION_IDENTITY_ADDRESS 0x09
#define CONNECTION_SIGNING_INFORMATION 0x0A
#define CONNECTION_SECURITY_REQUEST 0x0B
#define CONNECTION_PAIRING_PUBLIC_KEY 0x0C
#define CONNECTION_PAIRING_DHKEY_CHECK 0x0D
#define CONNECTION_PAIRING_KEYPRESS 0x0E

#define IOCAP_DISPLAY_ONLY 0x00
#define IOCAP_DISPLAY_YES_NO 0x01
#define IOCAP_KEYBOARD_ONLY 0x02
#define IOCAP_NO_INPUT_NO_OUTPUT 0x03
#define IOCAP_KEYBOARD_DISPLAY 0x04


#define LOCAL_AUTHREQ 0b00101101
// #define LOCAL_IOCAP IOCAP_DISPLAY_ONLY // will use JustWorks pairing

class L2CAPSignalingClass {
public:
Expand All @@ -36,20 +63,32 @@ class L2CAPSignalingClass {

virtual void handleData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]);

virtual void handleSecurityData(uint16_t connectionHandle, uint8_t dlen, uint8_t data[]);

virtual void removeConnection(uint8_t handle, uint16_t reason);

virtual void setConnectionInterval(uint16_t minInterval, uint16_t maxInterval);

virtual void setSupervisionTimeout(uint16_t supervisionTimeout);

virtual void setPairingEnabled(uint8_t enabled);
virtual bool isPairingEnabled();



virtual void smCalculateLTKandConfirm(uint16_t handle, uint8_t expectedEa[]);


private:
virtual void connectionParameterUpdateRequest(uint16_t handle, uint8_t identifier, uint8_t dlen, uint8_t data[]);
virtual void connectionParameterUpdateResponse(uint16_t handle, uint8_t identifier, uint8_t dlen, uint8_t data[]);


private:
uint16_t _minInterval;
uint16_t _maxInterval;
uint16_t _supervisionTimeout;
uint8_t _pairing_enabled;
};

extern L2CAPSignalingClass& L2CAPSignaling;
Expand Down
30 changes: 30 additions & 0 deletions src/utility/bitDescriptions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include "bitDescriptions.h"


#define BONDING_BIT 0b00000001
#define MITM_BIT 0b00000100
#define SC_BIT 0b00001000
#define KEYPRESS_BIT 0b00010000
#define CT2_BIT 0b00100000


AuthReq::AuthReq(){}
AuthReq::AuthReq(uint8_t octet):_octet(octet){}
bool AuthReq::Bonding(){ return (_octet & BONDING_BIT)>0;}
bool AuthReq::MITM(){ return (_octet & MITM_BIT)>0;}
bool AuthReq::SC(){ return (_octet & SC_BIT)>0;}
bool AuthReq::KeyPress(){ return (_octet & KEYPRESS_BIT)>0;}
bool AuthReq::CT2(){ return (_octet & CT2_BIT)>0;}


void AuthReq::setBonding(bool state) { _octet= state? _octet|BONDING_BIT : _octet&~BONDING_BIT;}
void AuthReq::setMITM(bool state) { _octet= state? _octet|MITM_BIT : _octet&~MITM_BIT;}
void AuthReq::setSC(bool state){ _octet= state? _octet|SC_BIT : _octet&~SC_BIT;}
void AuthReq::setKeyPress(bool state){ _octet= state? _octet|KEYPRESS_BIT : _octet&~KEYPRESS_BIT;}
void AuthReq::setCT2(bool state){ _octet= state? _octet|CT2_BIT : _octet&~CT2_BIT;}

uint8_t _octet;


void AuthReq::setOctet( uint8_t octet){_octet = octet;}
uint8_t AuthReq::getOctet() {return _octet;}
41 changes: 41 additions & 0 deletions src/utility/bitDescriptions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#ifndef _BIT_DESCRIPTIONS_H_
#define _BIT_DESCRIPTIONS_H_
#include <Arduino.h>

class AuthReq{
public:
AuthReq();
AuthReq(uint8_t octet);
void setOctet( uint8_t octet);
uint8_t getOctet();


// The Bonding_Flags field is a 2-bit field that indicates the type of bonding being requested by the initiating device
bool Bonding();
// The MITM field is a 1-bit flag that is set to one if the device is requesting MITM protection
bool MITM();
// The SC field is a 1 bit flag. If LE Secure Connections pairing is supported by the device, then the SC field shall be set to 1, otherwise it shall be set to 0.
bool SC();
// The keypress field is a 1-bit flag that is used only in the Passkey Entry protocol and shall be ignored in other protocols.
bool KeyPress();
// The CT2 field is a 1-bit flag that shall be set to 1 upon transmission to indicate support for the h7 function.
bool CT2();

void setBonding(bool state);
void setMITM(bool state);
void setSC(bool state);
void setKeyPress(bool state);
void setCT2(bool state);
private:
uint8_t _octet;
};

enum IOCap {
DisplayOnly,
DisplayYesNo,
KeyboardOnly,
NoInputNoOutput,
KeyboardDisplay
};

#endif
390 changes: 390 additions & 0 deletions src/utility/btct.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,390 @@
#include "btct.h"
#include <Arduino.h>
#include "HCI.h"
#include "ArduinoBLE.h"
BluetoothCryptoToolbox::BluetoothCryptoToolbox(){}
// In step 1, AES-128 with key K is applied to an all-zero input block.
// In step 2, K1 is derived through the following operation:
// If the most significant bit of L is equal to 0, K1 is the left-shift
// of L by 1 bit.
// Otherwise, K1 is the exclusive-OR of const_Rb and the left-shift of L
// by 1 bit.
// In step 3, K2 is derived through the following operation:
// If the most significant bit of K1 is equal to 0, K2 is the left-shift
// of K1 by 1 bit.
// Otherwise, K2 is the exclusive-OR of const_Rb and the left-shift of
// K1 by 1 bit.
// In step 4, (K1,K2) := Generate_Subkey(K) is returned.
unsigned char const_Rb[16] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87
};

#define DHKEY_LENGTH 32
#define N_LEN 16
#define ADDR_LEN 6
#define LEN_LTK 16
#define LEN_MAC_KEY 16

void BluetoothCryptoToolbox::printBytes(uint8_t bytes[], uint8_t length){
for(int i=0; i<length; i++){
if(i>0){
Serial.print(", 0x");
}else{
Serial.print("0x");
}
Serial.print(bytes[i],HEX);
}
Serial.print('\n');
}
int BluetoothCryptoToolbox::f5(uint8_t DHKey[],uint8_t N_master[], uint8_t N_slave[],
uint8_t BD_ADDR_master[], uint8_t BD_ADDR_slave[], uint8_t MacKey[], uint8_t LTK[])
{
uint8_t SALT[16] = {0x6C, 0x88, 0x83, 0x91, 0xAA, 0xF5, 0xA5, 0x38, 0x60, 0x37, 0x0B, 0xDB, 0x5A, 0x60, 0x83, 0xBE};
uint8_t keyID[4] = {0x62, 0x74, 0x6c, 0x65};
uint8_t length[2];
length[0] = 0x01;
length[1] = 0x00;
#ifdef _BLE_TRACE_
Serial.print("Starting f5 calculation");
Serial.print("Using DHKey: ");
printBytes(DHKey, DHKEY_LENGTH);
Serial.print("Using N_Master: ");
printBytes(N_master, N_LEN);
Serial.print("Using N_Slave: ");
printBytes(N_slave, N_LEN);

Serial.println("Using BD_ADDR_MASTER: ");
printBytes(BD_ADDR_master, ADDR_LEN);
Serial.println("Using BD_ADDR_SLAVE: ");
printBytes(BD_ADDR_slave, ADDR_LEN);
#endif

uint8_t ADD_M[7];
uint8_t ADD_S[7];
uint8_t T[16];

for(int i=0; i<6; i++){
ADD_M[1+i] = BD_ADDR_master[i];
ADD_M[0] = 0x00;
ADD_S[i+1] = BD_ADDR_slave[i];
ADD_S[0] = 0x00;
}
struct __attribute__ ((packed)) CmacInput
{
uint8_t counter;
uint8_t keyID[4];
uint8_t N1[16];
uint8_t N2[16];
uint8_t A1[7];
uint8_t A2[7];
uint8_t length[2];
} cmacInput = {0,0,0,0,0,0,0};
cmacInput.counter = 0;
memcpy(cmacInput.keyID, keyID, 4);
memcpy(cmacInput.N1,N_master,16);
memcpy(cmacInput.N2,N_slave,16);
memcpy(cmacInput.A1,BD_ADDR_master,7);
memcpy(cmacInput.A2,BD_ADDR_slave,7);
memcpy(cmacInput.length,length,2);
AES_CMAC(SALT, DHKey, 32, T);

AES_CMAC(T, (uint8_t*)&cmacInput,sizeof(cmacInput), MacKey);
cmacInput.counter=1;
AES_CMAC(T, (uint8_t*)&cmacInput, sizeof(cmacInput), LTK);

return 1;
}
int BluetoothCryptoToolbox::f6(uint8_t W[], uint8_t N1[],uint8_t N2[],uint8_t R[], uint8_t IOCap[], uint8_t A1[], uint8_t A2[], uint8_t Ex[])
{
struct __attribute__ ((packed)) F6Input
{
uint8_t N1[16];
uint8_t N2[16];
uint8_t R[16];
uint8_t IOCap[3];
uint8_t A1[7];
uint8_t A2[7];
} f6Input = {0,0,0,0,0,0};

memcpy(f6Input.N1, N1, 16);
memcpy(f6Input.N2, N2, 16);
memcpy(f6Input.R, R, 16);
memcpy(f6Input.IOCap, IOCap, 3);
memcpy(f6Input.A1, A1, 7);
memcpy(f6Input.A2, A2, 7);


AES_CMAC(W, (uint8_t*)&f6Input, sizeof(f6Input),Ex);
return 1;
}
// AES_CMAC from RFC
int BluetoothCryptoToolbox::ah(uint8_t k[16], uint8_t r[3], uint8_t* result)
{
uint8_t r_[16];
int i=0;
for(i=0; i<16; i++) r_[i] = 0;
for(i=0; i<3; i++) r_[i+13] = r[i];
uint8_t intermediate[16];
AES_128(k,r_,intermediate);
for(i=0; i<3; i++){
result[i] = intermediate[i+13];
}
return 1;
}
void BluetoothCryptoToolbox::testAh()
{
uint8_t irk[16] = {0xec,0x02,0x34,0xa3,0x57,0xc8,0xad,0x05,0x34,0x10,0x10,0xa6,0x0a,0x39,0x7d,0x9b};
uint8_t r[3] = {0x70,0x81,0x94};
uint8_t expected_AES[16] = {0x15,0x9d,0x5f,0xb7,0x2e,0xbe,0x23,0x11,0xa4,0x8c,0x1b,0xdc,0xc4,0x0d,0xfb,0xaa};
uint8_t expected_final[3] = {0x0d,0xfb,0xaa};

for(int i=0; i<3; i++) r[2-i] = expected_final[3+i];
uint8_t ourResult[3];
ah(irk, expected_final, ourResult);


Serial.print("Expected : ");
printBytes(&expected_final[3], 3);
Serial.print("Actual : ");
printBytes(ourResult, 3);
}

int BluetoothCryptoToolbox::g2(uint8_t U[], uint8_t V[], uint8_t X[], uint8_t Y[], uint8_t out[4])
{
struct __attribute__ ((packed)) CmacInput {
uint8_t U[32];
uint8_t V[32];
uint8_t Y[16];
} cmacInput= {0,0,0};
memcpy(cmacInput.U,U,32);
memcpy(cmacInput.V,V,32);
memcpy(cmacInput.Y,Y,16);
uint8_t intermediate[16];
AES_CMAC(X,(uint8_t*)&cmacInput,sizeof(CmacInput),intermediate);
memcpy(out,&intermediate[12],4);
return 1;
}
void BluetoothCryptoToolbox::testg2(){
uint8_t U[32] = {0x20,0xb0,0x03,0xd2,0xf2,0x97,0xbe,0x2c,0x5e,0x2c,0x83,0xa7,0xe9,0xf9,0xa5,0xb9,0xef,0xf4,0x91,0x11,0xac,0xf4,0xfd,0xdb,0xcc,0x03,0x01,0x48,0x0e,0x35,0x9d,0xe6};
uint8_t V[32] = {0x55,0x18,0x8b,0x3d,0x32,0xf6,0xbb,0x9a,0x90,0x0a,0xfc,0xfb,0xee,0xd4,0xe7,0x2a,0x59,0xcb,0x9a,0xc2,0xf1,0x9d,0x7c,0xfb,0x6b,0x4f,0xdd,0x49,0xf4,0x7f,0xc5,0xfd};
uint8_t X[16] = {0xd5,0xcb,0x84,0x54,0xd1,0x77,0x73,0x3e,0xff,0xff,0xb2,0xec,0x71,0x2b,0xae,0xab};
uint8_t Y[16] = {0xa6,0xe8,0xe7,0xcc,0x25,0xa7,0x5f,0x6e,0x21,0x65,0x83,0xf7,0xff,0x3d,0xc4,0xcf};
uint8_t AES[16] = {0x15,0x36,0xd1,0x8d,0xe3,0xd2,0x0d,0xf9,0x9b,0x70,0x44,0xc1,0x2f,0x9e,0xd5,0xba};
uint8_t out[4];


uint32_t expected = 0;
g2(U,V,X,Y,out);
uint32_t result = 0;
for(int i=0; i<4; i++) result += out[i] << 8*i;

Serial.print("Expected : ");
Serial.println(expected);
Serial.print("Result : ");
Serial.println(result);
Serial.println();

}

void BluetoothCryptoToolbox::AES_CMAC ( unsigned char *key, unsigned char *input, int length,
unsigned char *mac )
{
unsigned char X[16],Y[16], M_last[16], padded[16];
unsigned char K1[16], K2[16];
int n, i, flag;
generateSubkey(key,K1,K2);

n = (length+15) / 16; /* n is number of rounds */

if ( n == 0 ) {
n = 1;
flag = 0;
} else {
if ( (length%16) == 0 ) { /* last block is a complete block */
flag = 1;
} else { /* last block is not complete block */
flag = 0;
}
}

if ( flag ) { /* last block is complete block */
xor_128(&input[16*(n-1)],K1,M_last);
} else {
padding(&input[16*(n-1)],padded,length%16);
xor_128(padded,K2,M_last);
}

for ( i=0; i<16; i++ ) X[i] = 0;
for ( i=0; i<n-1; i++ ) {
xor_128(X,&input[16*i],Y); /* Y := Mi (+) X */
AES_128(key,Y,X); /* X := AES-128(KEY, Y); */
}

xor_128(X,M_last,Y);
AES_128(key,Y,X);

for ( i=0; i<16; i++ ) {
mac[i] = X[i];
}
}
// Paddinng function from RFC
void BluetoothCryptoToolbox::padding( unsigned char *lastb, unsigned char *pad, int length )
{
int j;
/* original last block */
for ( j=0; j<16; j++ ) {
if ( j < length ) {
pad[j] = lastb[j];
} else if ( j == length ) {
pad[j] = 0x80;
} else {
pad[j] = 0x00;
}
}
}
// Generate subkey from RFC
void BluetoothCryptoToolbox::generateSubkey(uint8_t* key, uint8_t* K1, uint8_t* K2){
unsigned char L[16];
unsigned char Z[16];
unsigned char tmp[16];
int i;

for ( i=0; i<16; i++ ) Z[i] = 0;

AES_128(key,Z,L);

if ( (L[0] & 0x80) == 0 ) { /* If MSB(L) = 0, then K1 = L << 1 */
leftshift_onebit(L,K1);
} else { /* Else K1 = ( L << 1 ) (+) Rb */

leftshift_onebit(L,tmp);
xor_128(tmp,const_Rb,K1);
}

if ( (K1[0] & 0x80) == 0 ) {
leftshift_onebit(K1,K2);
} else {
leftshift_onebit(K1,tmp);
xor_128(tmp,const_Rb,K2);
}
return;
}
// Use BLE AES function - restart bluetooth if crash
int BluetoothCryptoToolbox::AES_128(uint8_t* key, uint8_t* data_in, uint8_t* data_out){
uint8_t status = 0;
int n = 0;
int tries = 30;
while(HCI.leEncrypt(key,data_in, &status, data_out)!=1&&n<tries){
Serial.print("AES failed... retrying: ");
Serial.println(n);
BLE.end();
delay(200);
BLE.begin();
n++;
delay(100*n);
}
if(n==tries){
Serial.println("something went wrong with AES.");
return 0;
}
return 1;
}
// Tests AES CMAC
#ifdef _BLE_TRACE_
void BluetoothCryptoToolbox::test(){
unsigned char L[16];
unsigned char Z[16];
unsigned char tmp[16];
int i;

for ( i=0; i<16; i++ ) Z[i] = 0x00;
uint8_t k[16] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c};

Serial.println("AES Plaintext:");
for(int i=0; i<16; i++){
Serial.print(" 0x");
Serial.print(Z[i],HEX);
}
Serial.println(".");
uint8_t expected_aes[16] = {0x7d, 0xf7, 0x6b, 0x0c, 0x1a, 0xb8, 0x99, 0xb3, 0x3e, 0x42, 0xf0, 0x47, 0xb9, 0x1b, 0x54, 0x6f};

AES_128(k, Z, L);
for(int i=0; i<16; i++){
Serial.print(" 0x");
Serial.print(L[i],HEX);
}
Serial.println(".");
for(int i=0; i<16; i++){
Serial.print(" 0x");
Serial.print(expected_aes[i],HEX);
}
Serial.println(".");

uint8_t k1[16];
uint8_t k2[16];
generateSubkey(k,k1,k2);
uint8_t expected_k1[16] = {0xfb, 0xee, 0xd6, 0x18, 0x35, 0x71, 0x33, 0x66, 0x7c, 0x85, 0xe0, 0x8f, 0x72, 0x36, 0xa8, 0xde};
uint8_t expected_k2[16] = {0xf7, 0xdd, 0xac, 0x30, 0x6a, 0xe2, 0x66, 0xcc, 0xf9, 0x0b, 0xc1, 0x1e, 0xe4, 0x6d, 0x51, 0x3b};

for(int i=0; i<16; i++){
Serial.print(" 0x");
Serial.print(k2[i],HEX);
}
Serial.println(".");
for(int i=0; i<16; i++){
Serial.print(" 0x");
Serial.print(expected_k2[i],HEX);
}
Serial.println(".");
for(int i=0; i<16; i++){
Serial.print(" 0x");
Serial.print(k1[i],HEX);
}
Serial.println(".");
for(int i=0; i<16; i++){
Serial.print(" 0x");
Serial.print(expected_k1[i],HEX);
}
Serial.println(".");

uint8_t m[40] = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11};
uint8_t mac[16];
uint8_t expected_mac[16] = {0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27};
AES_CMAC(k,m,40,mac);

for(int i=0; i<16; i++){
Serial.print(" 0x");
Serial.print(mac[i],HEX);
}
Serial.println(".");
for(int i=0; i<16; i++){
Serial.print(" 0x");
Serial.print(expected_mac[i],HEX);
}
Serial.println(".");
}
#endif
// From RFC
void BluetoothCryptoToolbox::leftshift_onebit(unsigned char *input,unsigned char *output)
{
int i;
unsigned char overflow = 0;

for ( i=15; i>=0; i-- ) {
output[i] = input[i] << 1;
output[i] |= overflow;
overflow = (input[i] & 0x80)?1:0;
}
return;
}
// From RFC
void BluetoothCryptoToolbox::xor_128(unsigned char *a, unsigned char *b, unsigned char *out)
{
int i;
for (i=0;i<16; i++)
{
out[i] = a[i] ^ b[i];
}
}
BluetoothCryptoToolbox btct;
30 changes: 30 additions & 0 deletions src/utility/btct.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#ifndef _BTCT_H_
#define _BTCT_H_
#include <Arduino.h>

// Implementation of functions defined in BTLE standard
class BluetoothCryptoToolbox{
public:
BluetoothCryptoToolbox();
void printBytes(uint8_t bytes[], uint8_t length);
void generateSubkey(uint8_t* K, uint8_t* K1, uint8_t* K2);
void AES_CMAC ( unsigned char *key, unsigned char *input, int length,
unsigned char *mac );
int f5(uint8_t DHKey[],uint8_t N_master[], uint8_t N_slave[],
uint8_t BD_ADDR_master[], uint8_t BD_ADDR_slave[], uint8_t MacKey[], uint8_t LTK[]);
int f6(uint8_t W[], uint8_t N1[],uint8_t N2[],uint8_t R[], uint8_t IOCap[], uint8_t A1[], uint8_t A2[], uint8_t Ex[]);
int g2(uint8_t U[], uint8_t V[], uint8_t X[], uint8_t Y[], uint8_t out[4]);
int ah(uint8_t k[16], uint8_t r[3], uint8_t result[3]);
void test();
void testF5();
void testF6();
void testAh();
void testg2();
private:
int AES_128(uint8_t key[], uint8_t data_in[], uint8_t data_out[]);
void leftshift_onebit(unsigned char *input,unsigned char *output);
void xor_128(unsigned char *a, unsigned char *b, unsigned char *out);
void padding ( unsigned char *lastb, unsigned char *pad, int length );
};
extern BluetoothCryptoToolbox btct;
#endif
24 changes: 24 additions & 0 deletions src/utility/keyDistribution.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include "keyDistribution.h"

KeyDistribution::KeyDistribution():_octet(0){}
KeyDistribution::KeyDistribution(uint8_t octet):_octet(octet){}

#define ENCKEY 0b00000001
#define IDKEY 0b00000010
#define SIGNKEY 0b00000100
#define LINKKEY 0b00001000
void KeyDistribution::setOctet( uint8_t octet){_octet = octet;}
uint8_t KeyDistribution::getOctet() {return _octet;}
// Ignored when SMP is on LE transport
bool KeyDistribution::EncKey(){ return (_octet & ENCKEY)>0;}
// Device shall distribute IRK using Identity information command followed by its address using Identity address information
bool KeyDistribution::IdKey(){ return (_octet & IDKEY)>0;}
// Device shall distribute CSRK using signing information command
bool KeyDistribution::SignKey(){ return (_octet & SIGNKEY)>0;}
// Device would like to derive BR/EDR from LTK
bool KeyDistribution::LinkKey(){ return (_octet & LINKKEY)>0;}

void KeyDistribution::setEncKey(bool state) { _octet= state? _octet|ENCKEY : _octet&~ENCKEY;}
void KeyDistribution::setIdKey(bool state) { _octet= state? _octet|IDKEY : _octet&~IDKEY;}
void KeyDistribution::setSignKey(bool state){ _octet= state? _octet|SIGNKEY : _octet&~SIGNKEY;}
void KeyDistribution::setLinkKey(bool state){ _octet= state? _octet|LINKKEY : _octet&~LINKKEY;}
29 changes: 29 additions & 0 deletions src/utility/keyDistribution.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#ifndef _KEY_DISTRIBUTION_H_
#define _KEY_DISTRIBUTION_H_
#include <Arduino.h>

class KeyDistribution{
public:
KeyDistribution();
KeyDistribution(uint8_t octet);
void setOctet( uint8_t octet);
uint8_t getOctet();
// Ignored when SMP is on LE transport
bool EncKey();
// Device shall distribute IRK using Identity information command followed by its address using Identity address information
bool IdKey();
// Device shall distribute CSRK using signing information command
bool SignKey();
// Device would like to derive BR/EDR from LTK
bool LinkKey();

void setEncKey(bool state);
void setIdKey(bool state);
void setSignKey(bool state);
void setLinkKey(bool state);
private:
uint8_t _octet;
// 1. IRK by the slave2. BD ADDR by the slave3. CSRK by the slave4. IRK by the master5. BD_ADDR by the master6. CSRK by the master
};

#endif