Merge pull request #50 from merlokk/fido

fido authenticators comands
This commit is contained in:
RFID Research Group 2018-11-13 18:32:09 +01:00 committed by GitHub
commit 6faec7e981
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
50 changed files with 8448 additions and 63 deletions

View file

@ -80,6 +80,9 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
- Added 'hf emv' commands (@merlokk) - Added 'hf emv' commands (@merlokk)
- lots of bug fixes (many many) - lots of bug fixes (many many)
- Changed hf mfp security. Now it works in all the modes. (drHatson) - Changed hf mfp security. Now it works in all the modes. (drHatson)
- Added `hf fido` commands that work with FIDO U2F authenticators (@merlokk)
- Added mbedtls instead of old polarssl (@merlokk)
- Added jansson (@merlokk)
### Fixed ### Fixed
- Changed driver file proxmark3.inf to support both old and new Product/Vendor IDs (piwi) - Changed driver file proxmark3.inf to support both old and new Product/Vendor IDs (piwi)
- Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok) - Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok)

View file

@ -108,7 +108,7 @@ int EPA_APDU(uint8_t *apdu, size_t length, uint8_t *response)
switch(iso_type) switch(iso_type)
{ {
case 'a': case 'a':
return iso14_apdu(apdu, (uint16_t) length, response); return iso14_apdu(apdu, (uint16_t) length, response, NULL);
break; break;
case 'b': case 'b':
return iso14443b_apdu(apdu, length, response); return iso14443b_apdu(apdu, length, response);

View file

@ -2212,15 +2212,21 @@ b8 b7 b6 b5 b4 b3 b2 b1
b5,b6 = 00 - DESELECT b5,b6 = 00 - DESELECT
11 - WTX 11 - WTX
*/ */
int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) { int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data, uint8_t *res) {
uint8_t parity[MAX_PARITY_SIZE] = {0x00}; uint8_t parity[MAX_PARITY_SIZE] = {0x00};
uint8_t real_cmd[cmd_len + 4]; uint8_t real_cmd[cmd_len + 4];
if (cmd_len) {
// ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02 // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02
real_cmd[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00) real_cmd[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00)
// put block number into the PCB // put block number into the PCB
real_cmd[0] |= iso14_pcb_blocknum; real_cmd[0] |= iso14_pcb_blocknum;
memcpy(real_cmd + 1, cmd, cmd_len); memcpy(real_cmd + 1, cmd, cmd_len);
} else {
// R-block. ACK
real_cmd[0] = 0xA2; // r-block + ACK
real_cmd[0] |= iso14_pcb_blocknum;
}
AddCrc14A(real_cmd, cmd_len + 1); AddCrc14A(real_cmd, cmd_len + 1);
ReaderTransmit(real_cmd, cmd_len + 3, NULL); ReaderTransmit(real_cmd, cmd_len + 3, NULL);
@ -2260,6 +2266,10 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) {
iso14_pcb_blocknum ^= 1; iso14_pcb_blocknum ^= 1;
} }
// if we received I-block with chaining we need to send ACK and receive another block of data
if (res)
*res = data_bytes[0];
// crc check // crc check
if (len >=3 && !check_crc(CRC_14443_A, data_bytes, len)) { if (len >=3 && !check_crc(CRC_14443_A, data_bytes, len)) {
return -1; return -1;
@ -2320,8 +2330,9 @@ void ReaderIso14443a(UsbCommand *c) {
iso14a_set_timeout(timeout); iso14a_set_timeout(timeout);
if ((param & ISO14A_APDU)) { if ((param & ISO14A_APDU)) {
arg0 = iso14_apdu(cmd, len, buf); uint8_t res;
cmd_send(CMD_ACK, arg0, 0, 0, buf, sizeof(buf)); arg0 = iso14_apdu(cmd, len, buf, &res);
cmd_send(CMD_ACK, arg0, res, 0, buf, sizeof(buf));
} }
if ((param & ISO14A_RAW)) { if ((param & ISO14A_RAW)) {

View file

@ -114,7 +114,7 @@ extern void ReaderTransmitPar(uint8_t *frame, uint16_t len, uint8_t *par, uint32
extern int ReaderReceive(uint8_t *receivedAnswer, uint8_t *par); extern int ReaderReceive(uint8_t *receivedAnswer, uint8_t *par);
extern void iso14443a_setup(uint8_t fpga_minor_mode); extern void iso14443a_setup(uint8_t fpga_minor_mode);
extern int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data); extern int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data, uint8_t *res);
extern int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *resp_data, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats); extern int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *resp_data, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats);
extern int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades); extern int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades);
extern void iso14a_set_trigger(bool enable); extern void iso14a_set_trigger(bool enable);

View file

@ -25,10 +25,12 @@ OBJDIR = obj
LDLIBS = -L/opt/local/lib -L/usr/local/lib -lreadline -lpthread -lm LDLIBS = -L/opt/local/lib -L/usr/local/lib -lreadline -lpthread -lm
LUALIB = ../liblua/liblua.a LUALIB = ../liblua/liblua.a
JANSSONLIBPATH = ./jansson
JANSSONLIB = $(JANSSONLIBPATH)/libjansson.a
MBEDTLSLIBPATH = ../common/mbedtls MBEDTLSLIBPATH = ../common/mbedtls
MBEDTLSLIB = $(MBEDTLSLIBPATH)/libmbedtls.a MBEDTLSLIB = $(MBEDTLSLIBPATH)/libmbedtls.a
LDFLAGS = $(ENV_LDFLAGS) LDFLAGS = $(ENV_LDFLAGS)
INCLUDES_CLIENT = -I. -I../include -I../common -I../common/polarssl -I../zlib -I../uart -I/opt/local/include -I../liblua -I$(MBEDTLSLIBPATH) INCLUDES_CLIENT = -I. -I../include -I../common -I../common/polarssl -I../zlib -I../uart -I/opt/local/include -I../liblua -I$(MBEDTLSLIBPATH) -I$(JANSSONLIBPATH)
CFLAGS = $(ENV_CFLAGS) -std=c99 -D_ISOC99_SOURCE -DPRESETS $(INCLUDES_CLIENT) -Wall -g -O3 CFLAGS = $(ENV_CFLAGS) -std=c99 -D_ISOC99_SOURCE -DPRESETS $(INCLUDES_CLIENT) -Wall -g -O3
CXXFLAGS = -I../include -Wall -O3 CXXFLAGS = -I../include -Wall -O3
@ -105,6 +107,7 @@ CMDSRCS = crapto1/crapto1.c \
crapto1/crypto1.c \ crapto1/crypto1.c \
mfkey.c \ mfkey.c \
tea.c \ tea.c \
fido/additional_ca.c \
polarssl/des.c \ polarssl/des.c \
crypto/libpcrypto.c\ crypto/libpcrypto.c\
crypto/asn1utils.c\ crypto/asn1utils.c\
@ -139,6 +142,7 @@ CMDSRCS = crapto1/crapto1.c \
emv/tlv.c \ emv/tlv.c \
emv/emv_tags.c \ emv/emv_tags.c \
emv/dol.c \ emv/dol.c \
emv/emvjson.c\
emv/emvcore.c \ emv/emvcore.c \
emv/test/crypto_test.c\ emv/test/crypto_test.c\
emv/test/sda_test.c\ emv/test/sda_test.c\
@ -162,6 +166,7 @@ CMDSRCS = crapto1/crapto1.c \
hardnested/hardnested_bruteforce.c \ hardnested/hardnested_bruteforce.c \
cmdhfmfdes.c \ cmdhfmfdes.c \
cmdhftopaz.c \ cmdhftopaz.c \
cmdhffido.c \
cmdhffelica.c \ cmdhffelica.c \
cmdhw.c \ cmdhw.c \
cmdlf.c \ cmdlf.c \
@ -256,12 +261,12 @@ WINBINS = $(patsubst %, %.exe, $(BINS))
CLEAN = $(BINS) $(WINBINS) $(COREOBJS) $(CMDOBJS) $(OBJCOBJS) $(ZLIBOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(OBJDIR)/*.o *.moc.cpp ui/ui_overlays.h lualibs/usb_cmd.lua lualibs/mf_default_keys.lua CLEAN = $(BINS) $(WINBINS) $(COREOBJS) $(CMDOBJS) $(OBJCOBJS) $(ZLIBOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(OBJDIR)/*.o *.moc.cpp ui/ui_overlays.h lualibs/usb_cmd.lua lualibs/mf_default_keys.lua
# need to assign dependancies to build these first... # need to assign dependancies to build these first...
all: lua_build mbedtls_build $(BINS) all: lua_build jansson_build mbedtls_build $(BINS)
all-static: LDLIBS:=-static $(LDLIBS) all-static: LDLIBS:=-static $(LDLIBS)
all-static: proxmark3 flasher fpga_compress all-static: proxmark3 flasher fpga_compress
proxmark3: LDLIBS+=$(LUALIB) $(QTLDLIBS) $(MBEDTLSLIB) proxmark3: LDLIBS+=$(LUALIB) $(JANSSONLIB) $(MBEDTLSLIB) $(QTLDLIBS)
proxmark3: $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(OBJCOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(ZLIBOBJS) lualibs/usb_cmd.lua lualibs/mf_default_keys.lua proxmark3: $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(OBJCOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(ZLIBOBJS) lualibs/usb_cmd.lua lualibs/mf_default_keys.lua
$(LD) $(LDFLAGS) $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(OBJCOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(ZLIBOBJS) $(LDLIBS) -o $@ $(LD) $(LDFLAGS) $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(OBJCOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(ZLIBOBJS) $(LDLIBS) -o $@
@ -288,6 +293,7 @@ lualibs/mf_default_keys.lua : default_keys.dic
clean: clean:
$(RM) $(CLEAN) $(RM) $(CLEAN)
cd ../liblua && make clean cd ../liblua && make clean
cd $(JANSSONLIBPATH) && make clean
cd $(MBEDTLSLIBPATH) && make clean cd $(MBEDTLSLIBPATH) && make clean
tarbin: $(BINS) tarbin: $(BINS)
@ -297,6 +303,10 @@ lua_build:
@echo Compiling liblua, using platform $(LUAPLATFORM) @echo Compiling liblua, using platform $(LUAPLATFORM)
cd ../liblua && make $(LUAPLATFORM) cd ../liblua && make $(LUAPLATFORM)
jansson_build:
@echo Compiling jansson
cd $(JANSSONLIBPATH) && make all
mbedtls_build: mbedtls_build:
@echo Compiling mbedtls @echo Compiling mbedtls
cd $(MBEDTLSLIBPATH) && make all cd $(MBEDTLSLIBPATH) && make all

View file

@ -121,6 +121,7 @@ static command_t CommandTable[] = {
{"mfu", CmdHFMFUltra, 1, "{ MIFARE Ultralight RFIDs... }"}, {"mfu", CmdHFMFUltra, 1, "{ MIFARE Ultralight RFIDs... }"},
{"mfdes", CmdHFMFDes, 1, "{ MIFARE Desfire RFIDs... }"}, {"mfdes", CmdHFMFDes, 1, "{ MIFARE Desfire RFIDs... }"},
{"topaz", CmdHFTopaz, 1, "{ TOPAZ (NFC Type 1) RFIDs... }"}, {"topaz", CmdHFTopaz, 1, "{ TOPAZ (NFC Type 1) RFIDs... }"},
{"fido", CmdHFFido, 1, "{ FIDO and FIDO2 authenticators... }"},
{"list", CmdTraceList, 0, "List protocol data in trace buffer"}, {"list", CmdTraceList, 0, "List protocol data in trace buffer"},
{"tune", CmdHFTune, 0, "Continuously measure HF antenna tuning"}, {"tune", CmdHFTune, 0, "Continuously measure HF antenna tuning"},
{"search", CmdHFSearch, 1, "Search for known HF tags [preliminary]"}, {"search", CmdHFSearch, 1, "Search for known HF tags [preliminary]"},

View file

@ -31,6 +31,7 @@
#include "cmdhftopaz.h" // TOPAZ #include "cmdhftopaz.h" // TOPAZ
#include "cmdhffelica.h" // ISO18092 / FeliCa #include "cmdhffelica.h" // ISO18092 / FeliCa
#include "emv/cmdemv.h" // EMV #include "emv/cmdemv.h" // EMV
#include "cmdhffido.h" // FIDO authenticators
#include "cmdtrace.h" // trace list #include "cmdtrace.h" // trace list
extern int CmdHF(const char *Cmd); extern int CmdHF(const char *Cmd);

View file

@ -767,20 +767,20 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav
return 0; return 0;
} }
int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { int CmdExchangeAPDU(uint8_t *datain, int datainlen, bool activateField, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, bool *chaining) {
uint16_t cmdc = 0; uint16_t cmdc = 0;
*chaining = false;
if (activateField) { if (activateField) {
cmdc |= ISO14A_CONNECT; cmdc |= ISO14A_CONNECT;
} }
if (leaveSignalON)
cmdc |= ISO14A_NO_DISCONNECT;
// "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes // "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes
// https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size // https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size
// here length USB_CMD_DATA_SIZE=512 // here length USB_CMD_DATA_SIZE=512
// timeout must be authomatically set by "get ATS" // timeout must be authomatically set by "get ATS"
UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_APDU | cmdc, (datainlen & 0xFFFF), 0}}; UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, (datainlen & 0xFFFF), 0}};
memcpy(c.d.asBytes, datain, datainlen); memcpy(c.d.asBytes, datain, datainlen);
SendCommand(&c); SendCommand(&c);
@ -789,11 +789,12 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea
if (activateField) { if (activateField) {
if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
PrintAndLogEx(NORMAL, "APDU ERROR: Proxmark connection timeout."); PrintAndLogEx(ERR, "APDU: Proxmark connection timeout.");
return 1; return 1;
} }
if (resp.arg[0] != 1) { if (resp.arg[0] != 1) {
PrintAndLogEx(NORMAL, "APDU ERROR: Proxmark error %d.", resp.arg[0]); PrintAndLogEx(ERR, "APDU: Proxmark error %d.", resp.arg[0]);
DropField();
return 1; return 1;
} }
} }
@ -801,49 +802,79 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
recv = resp.d.asBytes; recv = resp.d.asBytes;
int iLen = resp.arg[0]; int iLen = resp.arg[0];
uint8_t res = resp.arg[1];
*dataoutlen = iLen - 2; int dlen = iLen - 2;
if (*dataoutlen < 0) if (dlen < 0)
*dataoutlen = 0; dlen = 0;
*dataoutlen += dlen;
if (maxdataoutlen && *dataoutlen > maxdataoutlen) { if (maxdataoutlen && *dataoutlen > maxdataoutlen) {
PrintAndLogEx(NORMAL, "APDU ERROR: Buffer too small(%d). Needs %d bytes", *dataoutlen, maxdataoutlen); PrintAndLogEx(ERR, "APDU: Buffer too small(%d). Needs %d bytes", *dataoutlen, maxdataoutlen);
return 2; return 2;
} }
memcpy(dataout, recv, *dataoutlen);
if(!iLen) { if(!iLen) {
PrintAndLogEx(NORMAL, "APDU ERROR: No APDU response."); PrintAndLogEx(ERR, "APDU: No APDU response.");
return 1; return 1;
} }
// check apdu length
if (iLen < 4 && iLen >= 0) {
PrintAndLogEx(ERR, "APDU: Small APDU response. Len=%d", iLen);
return 2;
}
// check block TODO // check block TODO
if (iLen == -2) { if (iLen == -2) {
PrintAndLogEx(NORMAL, "APDU ERROR: Block type mismatch."); PrintAndLogEx(ERR, "APDU: Block type mismatch.");
return 2; return 2;
} }
memcpy(dataout, recv, dlen);
// chaining
if ((res & 0x10) != 0) {
*chaining = true;
}
// CRC Check // CRC Check
if (iLen == -1) { if (iLen == -1) {
PrintAndLogEx(NORMAL, "APDU ERROR: ISO 14443A CRC error."); PrintAndLogEx(ERR, "APDU: ISO 14443A CRC error.");
return 3; return 3;
} }
// check apdu length
if (iLen < 4) {
PrintAndLogEx(NORMAL, "APDU ERROR: Small APDU response. Len=%d", iLen);
return 2;
}
} else { } else {
PrintAndLogEx(NORMAL, "APDU ERROR: Reply timeout."); PrintAndLogEx(ERR, "APDU: Reply timeout.");
return 4; return 4;
} }
return 0; return 0;
} }
int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
*dataoutlen = 0;
bool chaining = false;
int res = CmdExchangeAPDU(datain, datainlen, activateField, dataout, maxdataoutlen, dataoutlen, &chaining);
while (chaining) {
// I-block with chaining
res = CmdExchangeAPDU(NULL, 0, false, &dataout[*dataoutlen], maxdataoutlen, dataoutlen, &chaining);
if (res) {
if (!leaveSignalON)
DropField();
return 100;
}
}
if (!leaveSignalON)
DropField();
return 0;
}
int CmdHF14AAPDU(const char *cmd) { int CmdHF14AAPDU(const char *cmd) {
uint8_t data[USB_CMD_DATA_SIZE]; uint8_t data[USB_CMD_DATA_SIZE];
int datalen = 0; int datalen = 0;

704
client/cmdhffido.c Normal file
View file

@ -0,0 +1,704 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2018 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// High frequency MIFARE Plus commands
//-----------------------------------------------------------------------------
//
// Documentation here:
//
// FIDO Alliance specifications
// https://fidoalliance.org/download/
// FIDO NFC Protocol Specification v1.0
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-nfc-protocol-v1.2-ps-20170411.html
// FIDO U2F Raw Message Formats
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html
//-----------------------------------------------------------------------------
#include "cmdhffido.h"
#include <inttypes.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <jansson.h>
#include "comms.h"
#include "cmdmain.h"
#include "util.h"
#include "ui.h"
#include "proxmark3.h"
#include "cmdhf14a.h"
#include "mifare.h"
#include "emv/emvcore.h"
#include "emv/emvjson.h"
#include "emv/dump.h"
#include "cliparser/cliparser.h"
#include "crypto/asn1utils.h"
#include "crypto/libpcrypto.h"
#include "fido/additional_ca.h"
#include "mbedtls/x509_crt.h"
#include "mbedtls/x509.h"
#include "mbedtls/pk.h"
static int CmdHelp(const char *Cmd);
int FIDOSelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
uint8_t data[] = {0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01};
return EMVSelect(ActivateField, LeaveFieldON, data, sizeof(data), Result, MaxResultLen, ResultLen, sw, NULL);
}
int FIDOExchange(sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
int res = EMVExchange(true, apdu, Result, MaxResultLen, ResultLen, sw, NULL);
if (res == 5) // apdu result (sw) not a 0x9000
res = 0;
// software chaining
while (!res && (*sw >> 8) == 0x61) {
size_t oldlen = *ResultLen;
res = EMVExchange(true, (sAPDU){0x00, 0xC0, 0x00, 0x00, 0x00, NULL}, &Result[oldlen], MaxResultLen - oldlen, ResultLen, sw, NULL);
if (res == 5) // apdu result (sw) not a 0x9000
res = 0;
*ResultLen += oldlen;
if (*ResultLen > MaxResultLen)
return 100;
}
return res;
}
int FIDORegister(uint8_t *params, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
return FIDOExchange((sAPDU){0x00, 0x01, 0x03, 0x00, 64, params}, Result, MaxResultLen, ResultLen, sw);
}
int FIDOAuthentication(uint8_t *params, uint8_t paramslen, uint8_t controlb, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
return FIDOExchange((sAPDU){0x00, 0x02, controlb, 0x00, paramslen, params}, Result, MaxResultLen, ResultLen, sw);
}
int FIDO2GetInfo(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
uint8_t data[] = {0x04};
return FIDOExchange((sAPDU){0x80, 0x10, 0x00, 0x00, sizeof(data), data}, Result, MaxResultLen, ResultLen, sw);
}
int CmdHFFidoInfo(const char *cmd) {
if (cmd && strlen(cmd) > 0)
PrintAndLog("WARNING: command don't have any parameters.\n");
// info about 14a part
CmdHF14AInfo("");
// FIDO info
PrintAndLog("--------------------------------------------");
SetAPDULogging(false);
uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
if (res) {
DropField();
return res;
}
if (sw != 0x9000) {
if (sw)
PrintAndLog("Not a FIDO card! APDU response: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
else
PrintAndLog("APDU exchange error. Card returns 0x0000.");
DropField();
return 0;
}
if (!strncmp((char *)buf, "U2F_V2", 7)) {
if (!strncmp((char *)buf, "FIDO_2_0", 8)) {
PrintAndLog("FIDO2 authenricator detected. Version: %.*s", len, buf);
} else {
PrintAndLog("FIDO authenricator detected (not standard U2F).");
PrintAndLog("Non U2F authenticator version:");
dump_buffer((const unsigned char *)buf, len, NULL, 0);
}
} else {
PrintAndLog("FIDO U2F authenricator detected. Version: %.*s", len, buf);
}
res = FIDO2GetInfo(buf, sizeof(buf), &len, &sw);
DropField();
if (res) {
return res;
}
if (sw != 0x9000) {
PrintAndLog("FIDO2 version not exists (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
return 0;
}
PrintAndLog("FIDO2 version: (%d)", len);
dump_buffer((const unsigned char *)buf, len, NULL, 0);
return 0;
}
json_t *OpenJson(int paramnum, char *fname, void* argtable[], bool *err) {
json_t *root = NULL;
json_error_t error;
*err = false;
uint8_t jsonname[250] ={0};
char *cjsonname = (char *)jsonname;
int jsonnamelen = 0;
// CLIGetStrWithReturn(paramnum, jsonname, &jsonnamelen);
if (CLIParamStrToBuf(arg_get_str(paramnum), jsonname, sizeof(jsonname), &jsonnamelen)) {
CLIParserFree();
return NULL;
}
// current path + file name
if (!strstr(cjsonname, ".json"))
strcat(cjsonname, ".json");
if (jsonnamelen) {
strcpy(fname, get_my_executable_directory());
strcat(fname, cjsonname);
if (access(fname, F_OK) != -1) {
root = json_load_file(fname, 0, &error);
if (!root) {
PrintAndLog("ERROR: json error on line %d: %s", error.line, error.text);
*err = true;
return NULL;
}
if (!json_is_object(root)) {
PrintAndLog("ERROR: Invalid json format. root must be an object.");
json_decref(root);
*err = true;
return NULL;
}
} else {
root = json_object();
}
}
return root;
}
int CmdHFFidoRegister(const char *cmd) {
uint8_t data[64] = {0};
int chlen = 0;
uint8_t cdata[250] = {0};
int applen = 0;
uint8_t adata[250] = {0};
json_t *root = NULL;
CLIParserInit("hf fido reg",
"Initiate a U2F token registration. Needs two 32-byte hash number. \nchallenge parameter (32b) and application parameter (32b).",
"Usage:\n\thf fido reg -> execute command with 2 parameters, filled 0x00\n"
"\thf fido reg 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters"
"\thf fido reg -p s0 s1 -> execute command with plain parameters");
void* argtable[] = {
arg_param_begin,
arg_lit0("aA", "apdu", "show APDU reqests and responses"),
arg_litn("vV", "verbose", 0, 2, "show technical data. vv - show full certificates data"),
arg_lit0("pP", "plain", "send plain ASCII to challenge and application parameters instead of HEX"),
arg_str0("jJ", "json", "fido.json", "JSON input / output file name for parameters."),
arg_str0(NULL, NULL, "<HEX/ASCII challenge parameter (32b HEX/1..16 chars)>", NULL),
arg_str0(NULL, NULL, "<HEX/ASCII application parameter (32b HEX/1..16 chars)>", NULL),
arg_param_end
};
CLIExecWithReturn(cmd, argtable, true);
bool APDULogging = arg_get_lit(1);
bool verbose = arg_get_lit(2);
bool verbose2 = arg_get_lit(2) > 1;
bool paramsPlain = arg_get_lit(3);
char fname[250] = {0};
bool err;
root = OpenJson(4, fname, argtable, &err);
if(err)
return 1;
if (root) {
size_t jlen;
JsonLoadBufAsHex(root, "$.ChallengeParam", data, 32, &jlen);
JsonLoadBufAsHex(root, "$.ApplicationParam", &data[32], 32, &jlen);
}
if (paramsPlain) {
memset(cdata, 0x00, 32);
CLIGetStrWithReturn(5, cdata, &chlen);
if (chlen && chlen > 16) {
PrintAndLog("ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", chlen);
return 1;
}
} else {
CLIGetHexWithReturn(5, cdata, &chlen);
if (chlen && chlen != 32) {
PrintAndLog("ERROR: challenge parameter length must be 32 bytes only.");
return 1;
}
}
if (chlen)
memmove(data, cdata, 32);
if (paramsPlain) {
memset(adata, 0x00, 32);
CLIGetStrWithReturn(6, adata, &applen);
if (applen && applen > 16) {
PrintAndLog("ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", applen);
return 1;
}
} else {
CLIGetHexWithReturn(6, adata, &applen);
if (applen && applen != 32) {
PrintAndLog("ERROR: application parameter length must be 32 bytes only.");
return 1;
}
}
if (applen)
memmove(&data[32], adata, 32);
CLIParserFree();
SetAPDULogging(APDULogging);
// challenge parameter [32 bytes] - The challenge parameter is the SHA-256 hash of the Client Data, a stringified JSON data structure that the FIDO Client prepares
// application parameter [32 bytes] - The application parameter is the SHA-256 hash of the UTF-8 encoding of the application identity
uint8_t buf[2048] = {0};
size_t len = 0;
uint16_t sw = 0;
DropField();
int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
if (res) {
PrintAndLog("Can't select authenticator. res=%x. Exit...", res);
DropField();
return res;
}
if (sw != 0x9000) {
PrintAndLog("Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
DropField();
return 2;
}
res = FIDORegister(data, buf, sizeof(buf), &len, &sw);
DropField();
if (res) {
PrintAndLog("Can't execute register command. res=%x. Exit...", res);
return res;
}
if (sw != 0x9000) {
PrintAndLog("ERROR execute register command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
return 3;
}
PrintAndLog("");
if (APDULogging)
PrintAndLog("---------------------------------------------------------------");
PrintAndLog("data len: %d", len);
if (verbose2) {
PrintAndLog("--------------data----------------------");
dump_buffer((const unsigned char *)buf, len, NULL, 0);
PrintAndLog("--------------data----------------------");
}
if (buf[0] != 0x05) {
PrintAndLog("ERROR: First byte must be 0x05, but it %2x", buf[0]);
return 5;
}
PrintAndLog("User public key: %s", sprint_hex(&buf[1], 65));
uint8_t keyHandleLen = buf[66];
PrintAndLog("Key handle[%d]: %s", keyHandleLen, sprint_hex(&buf[67], keyHandleLen));
int derp = 67 + keyHandleLen;
int derLen = (buf[derp + 2] << 8) + buf[derp + 3] + 4;
if (verbose2) {
PrintAndLog("DER certificate[%d]:\n------------------DER-------------------", derLen);
dump_buffer_simple((const unsigned char *)&buf[67 + keyHandleLen], derLen, NULL);
PrintAndLog("\n----------------DER---------------------");
} else {
if (verbose)
PrintAndLog("------------------DER-------------------");
PrintAndLog("DER certificate[%d]: %s...", derLen, sprint_hex(&buf[derp], 20));
}
// check and print DER certificate
uint8_t public_key[65] = {0};
// TODO: print DER certificate in DER view
// load CA's
mbedtls_x509_crt cacert;
mbedtls_x509_crt_init(&cacert);
res = mbedtls_x509_crt_parse(&cacert, (const unsigned char *) additional_ca_pem, additional_ca_pem_len);
if (res < 0) {
PrintAndLog("ERROR: CA parse certificate returned -0x%x - %s", -res, ecdsa_get_error(res));
}
if (verbose)
PrintAndLog("CA load OK. %d skipped", res);
// load DER certificate from authenticator's data
mbedtls_x509_crt cert;
mbedtls_x509_crt_init(&cert);
res = mbedtls_x509_crt_parse_der(&cert, &buf[67 + keyHandleLen], derLen);
if (res) {
PrintAndLog("ERROR: DER parse returned 0x%x - %s", (res<0)?-res:res, ecdsa_get_error(res));
}
// get certificate info
char linfo[300] = {0};
if (verbose) {
mbedtls_x509_crt_info(linfo, sizeof(linfo), " ", &cert);
PrintAndLog("DER certificate info:\n%s", linfo);
}
// verify certificate
uint32_t verifyflags = 0;
res = mbedtls_x509_crt_verify(&cert, &cacert, NULL, NULL, &verifyflags, NULL, NULL);
if (res) {
PrintAndLog("ERROR: DER verify returned 0x%x - %s", (res<0)?-res:res, ecdsa_get_error(res));
} else {
PrintAndLog("Certificate OK.");
}
if (verbose) {
memset(linfo, 0x00, sizeof(linfo));
mbedtls_x509_crt_verify_info(linfo, sizeof(linfo), " ", verifyflags);
PrintAndLog("Verification info:\n%s", linfo);
}
// get public key
res = ecdsa_public_key_from_pk(&cert.pk, public_key, sizeof(public_key));
if (res) {
PrintAndLog("ERROR: getting public key from certificate 0x%x - %s", (res<0)?-res:res, ecdsa_get_error(res));
} else {
if (verbose)
PrintAndLog("Got a public key from certificate:\n%s", sprint_hex_inrow(public_key, 65));
}
if (verbose)
PrintAndLog("------------------DER-------------------");
mbedtls_x509_crt_free(&cert);
mbedtls_x509_crt_free(&cacert);
// get hash
int hashp = 1 + 65 + 1 + keyHandleLen + derLen;
PrintAndLog("Hash[%d]: %s", len - hashp, sprint_hex(&buf[hashp], len - hashp));
// check ANSI X9.62 format ECDSA signature (on P-256)
uint8_t rval[300] = {0};
uint8_t sval[300] = {0};
res = ecdsa_asn1_get_signature(&buf[hashp], len - hashp, rval, sval);
if (!res) {
if (verbose) {
PrintAndLog(" r: %s", sprint_hex(rval, 32));
PrintAndLog(" s: %s", sprint_hex(sval, 32));
}
uint8_t xbuf[4096] = {0};
size_t xbuflen = 0;
res = FillBuffer(xbuf, sizeof(xbuf), &xbuflen,
"\x00", 1,
&data[32], 32, // application parameter
&data[0], 32, // challenge parameter
&buf[67], keyHandleLen, // keyHandle
&buf[1], 65, // user public key
NULL, 0);
//PrintAndLog("--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
res = ecdsa_signature_verify(public_key, xbuf, xbuflen, &buf[hashp], len - hashp);
if (res) {
if (res == -0x4e00) {
PrintAndLog("Signature is NOT VALID.");
} else {
PrintAndLog("Other signature check error: %x %s", (res<0)?-res:res, ecdsa_get_error(res));
}
} else {
PrintAndLog("Signature is OK.");
}
} else {
PrintAndLog("Invalid signature. res=%d.", res);
}
PrintAndLog("\nauth command: ");
printf("hf fido auth %s%s", paramsPlain?"-p ":"", sprint_hex_inrow(&buf[67], keyHandleLen));
if(chlen || applen)
printf(" %s", paramsPlain?(char *)cdata:sprint_hex_inrow(cdata, 32));
if(applen)
printf(" %s", paramsPlain?(char *)adata:sprint_hex_inrow(adata, 32));
printf("\n");
if (root) {
JsonSaveBufAsHex(root, "ChallengeParam", data, 32);
JsonSaveBufAsHex(root, "ApplicationParam", &data[32], 32);
JsonSaveBufAsHexCompact(root, "PublicKey", &buf[1], 65);
JsonSaveInt(root, "KeyHandleLen", keyHandleLen);
JsonSaveBufAsHexCompact(root, "KeyHandle", &buf[67], keyHandleLen);
JsonSaveBufAsHexCompact(root, "DER", &buf[67 + keyHandleLen], derLen);
res = json_dump_file(root, fname, JSON_INDENT(2));
if (res) {
PrintAndLog("ERROR: can't save the file: %s", fname);
return 200;
}
PrintAndLog("File `%s` saved.", fname);
// free json object
json_decref(root);
}
return 0;
};
int CmdHFFidoAuthenticate(const char *cmd) {
uint8_t data[512] = {0};
uint8_t hdata[250] = {0};
bool public_key_loaded = false;
uint8_t public_key[65] = {0};
int hdatalen = 0;
uint8_t keyHandleLen = 0;
json_t *root = NULL;
CLIParserInit("hf fido auth",
"Initiate a U2F token authentication. Needs key handle and two 32-byte hash number. \nkey handle(var 0..255), challenge parameter (32b) and application parameter (32b).",
"Usage:\n\thf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with 2 parameters, filled 0x00 and key handle\n"
"\thf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f "
"000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters");
void* argtable[] = {
arg_param_begin,
arg_lit0("aA", "apdu", "show APDU reqests and responses"),
arg_lit0("vV", "verbose", "show technical data"),
arg_lit0("pP", "plain", "send plain ASCII to challenge and application parameters instead of HEX"),
arg_rem("default mode:", "dont-enforce-user-presence-and-sign"),
arg_lit0("uU", "user", "mode: enforce-user-presence-and-sign"),
arg_lit0("cC", "check", "mode: check-only"),
arg_str0("jJ", "json", "fido.json", "JSON input / output file name for parameters."),
arg_str0("kK", "key", "public key to verify signature", NULL),
arg_str0(NULL, NULL, "<HEX key handle (var 0..255b)>", NULL),
arg_str0(NULL, NULL, "<HEX/ASCII challenge parameter (32b HEX/1..16 chars)>", NULL),
arg_str0(NULL, NULL, "<HEX/ASCII application parameter (32b HEX/1..16 chars)>", NULL),
arg_param_end
};
CLIExecWithReturn(cmd, argtable, true);
bool APDULogging = arg_get_lit(1);
bool verbose = arg_get_lit(2);
bool paramsPlain = arg_get_lit(3);
uint8_t controlByte = 0x08;
if (arg_get_lit(5))
controlByte = 0x03;
if (arg_get_lit(6))
controlByte = 0x07;
char fname[250] = {0};
bool err;
root = OpenJson(7, fname, argtable, &err);
if(err)
return 1;
if (root) {
size_t jlen;
JsonLoadBufAsHex(root, "$.ChallengeParam", data, 32, &jlen);
JsonLoadBufAsHex(root, "$.ApplicationParam", &data[32], 32, &jlen);
JsonLoadBufAsHex(root, "$.KeyHandle", &data[65], 512 - 67, &jlen);
keyHandleLen = jlen & 0xff;
data[64] = keyHandleLen;
JsonLoadBufAsHex(root, "$.PublicKey", public_key, 65, &jlen);
public_key_loaded = (jlen > 0);
}
// public key
CLIGetHexWithReturn(8, hdata, &hdatalen);
if (hdatalen && hdatalen != 130) {
PrintAndLog("ERROR: public key length must be 65 bytes only.");
return 1;
}
if (hdatalen) {
memmove(public_key, hdata, hdatalen);
public_key_loaded = true;
}
CLIGetHexWithReturn(9, hdata, &hdatalen);
if (hdatalen > 255) {
PrintAndLog("ERROR: application parameter length must be less than 255.");
return 1;
}
if (hdatalen) {
keyHandleLen = hdatalen;
data[64] = keyHandleLen;
memmove(&data[65], hdata, keyHandleLen);
}
if (paramsPlain) {
memset(hdata, 0x00, 32);
CLIGetStrWithReturn(9, hdata, &hdatalen);
if (hdatalen && hdatalen > 16) {
PrintAndLog("ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen);
return 1;
}
} else {
CLIGetHexWithReturn(10, hdata, &hdatalen);
if (hdatalen && hdatalen != 32) {
PrintAndLog("ERROR: challenge parameter length must be 32 bytes only.");
return 1;
}
}
if (hdatalen)
memmove(data, hdata, 32);
if (paramsPlain) {
memset(hdata, 0x00, 32);
CLIGetStrWithReturn(11, hdata, &hdatalen);
if (hdatalen && hdatalen > 16) {
PrintAndLog("ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen);
return 1;
}
} else {
CLIGetHexWithReturn(10, hdata, &hdatalen);
if (hdatalen && hdatalen != 32) {
PrintAndLog("ERROR: application parameter length must be 32 bytes only.");
return 1;
}
}
if (hdatalen)
memmove(&data[32], hdata, 32);
CLIParserFree();
SetAPDULogging(APDULogging);
// (in parameter) conrtol byte 0x07 - check only, 0x03 - user presense + cign. 0x08 - sign only
// challenge parameter [32 bytes]
// application parameter [32 bytes]
// key handle length [1b] = N
// key handle [N]
uint8_t datalen = 32 + 32 + 1 + keyHandleLen;
uint8_t buf[2048] = {0};
size_t len = 0;
uint16_t sw = 0;
DropField();
int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw);
if (res) {
PrintAndLog("Can't select authenticator. res=%x. Exit...", res);
DropField();
return res;
}
if (sw != 0x9000) {
PrintAndLog("Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
DropField();
return 2;
}
res = FIDOAuthentication(data, datalen, controlByte, buf, sizeof(buf), &len, &sw);
DropField();
if (res) {
PrintAndLog("Can't execute authentication command. res=%x. Exit...", res);
return res;
}
if (sw != 0x9000) {
PrintAndLog("ERROR execute authentication command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
return 3;
}
PrintAndLog("---------------------------------------------------------------");
PrintAndLog("User presence: %s", (buf[0]?"verified":"not verified"));
uint32_t cntr = (uint32_t)bytes_to_num(&buf[1], 4);
PrintAndLog("Counter: %d", cntr);
PrintAndLog("Hash[%d]: %s", len - 5, sprint_hex(&buf[5], len - 5));
// check ANSI X9.62 format ECDSA signature (on P-256)
uint8_t rval[300] = {0};
uint8_t sval[300] = {0};
res = ecdsa_asn1_get_signature(&buf[5], len - 5, rval, sval);
if (!res) {
if (verbose) {
PrintAndLog(" r: %s", sprint_hex(rval, 32));
PrintAndLog(" s: %s", sprint_hex(sval, 32));
}
if (public_key_loaded) {
uint8_t xbuf[4096] = {0};
size_t xbuflen = 0;
res = FillBuffer(xbuf, sizeof(xbuf), &xbuflen,
&data[32], 32, // application parameter
&buf[0], 1, // user presence
&buf[1], 4, // counter
data, 32, // challenge parameter
NULL, 0);
//PrintAndLog("--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen));
res = ecdsa_signature_verify(public_key, xbuf, xbuflen, &buf[5], len - 5);
if (res) {
if (res == -0x4e00) {
PrintAndLog("Signature is NOT VALID.");
} else {
PrintAndLog("Other signature check error: %x %s", (res<0)?-res:res, ecdsa_get_error(res));
}
} else {
PrintAndLog("Signature is OK.");
}
} else {
PrintAndLog("No public key provided. can't check signature.");
}
} else {
PrintAndLog("Invalid signature. res=%d.", res);
}
if (root) {
JsonSaveBufAsHex(root, "ChallengeParam", data, 32);
JsonSaveBufAsHex(root, "ApplicationParam", &data[32], 32);
JsonSaveInt(root, "KeyHandleLen", keyHandleLen);
JsonSaveBufAsHexCompact(root, "KeyHandle", &data[65], keyHandleLen);
JsonSaveInt(root, "Counter", cntr);
res = json_dump_file(root, fname, JSON_INDENT(2));
if (res) {
PrintAndLog("ERROR: can't save the file: %s", fname);
return 200;
}
PrintAndLog("File `%s` saved.", fname);
// free json object
json_decref(root);
}
return 0;
};
static command_t CommandTable[] =
{
{"help", CmdHelp, 1, "This help."},
{"info", CmdHFFidoInfo, 0, "Info about FIDO tag."},
{"reg", CmdHFFidoRegister, 0, "FIDO U2F Registration Message."},
{"auth", CmdHFFidoAuthenticate, 0, "FIDO U2F Authentication Message."},
{NULL, NULL, 0, NULL}
};
int CmdHFFido(const char *Cmd) {
(void)WaitForResponseTimeout(CMD_ACK,NULL,100);
CmdsParse(CommandTable, Cmd);
return 0;
}
int CmdHelp(const char *Cmd) {
CmdsHelp(CommandTable);
return 0;
}

27
client/cmdhffido.h Normal file
View file

@ -0,0 +1,27 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2018 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// High frequency FIDO U2F and FIDO2 contactless authenticators
//-----------------------------------------------------------------------------
//
// Documentation here:
//
// FIDO Alliance specifications
// https://fidoalliance.org/download/
// FIDO NFC Protocol Specification v1.0
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-nfc-protocol-v1.2-ps-20170411.html
// FIDO U2F Raw Message Formats
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html
//-----------------------------------------------------------------------------
#ifndef CMDHFFIDO_H__
#define CMDHFFIDO_H__
extern int CmdHFFido(const char *Cmd);
#endif

View file

@ -16,6 +16,7 @@
#include <mbedtls/asn1.h> #include <mbedtls/asn1.h>
#include <mbedtls/aes.h> #include <mbedtls/aes.h>
#include <mbedtls/cmac.h> #include <mbedtls/cmac.h>
#include <mbedtls/pk.h>
#include <mbedtls/ecdsa.h> #include <mbedtls/ecdsa.h>
#include <mbedtls/sha256.h> #include <mbedtls/sha256.h>
#include <mbedtls/ctr_drbg.h> #include <mbedtls/ctr_drbg.h>
@ -208,6 +209,31 @@ char *ecdsa_get_error(int ret) {
return retstr; return retstr;
} }
int ecdsa_public_key_from_pk(mbedtls_pk_context *pk, uint8_t *key, size_t keylen) {
int res = 0;
size_t realkeylen = 0;
if (keylen < 65)
return 1;
mbedtls_ecdsa_context ctx;
mbedtls_ecdsa_init(&ctx);
res = mbedtls_ecp_group_load(&ctx.grp, MBEDTLS_ECP_DP_SECP256R1); // secp256r1
if (res)
goto exit;
res = mbedtls_ecdsa_from_keypair(&ctx, mbedtls_pk_ec(*pk) );
if (res)
goto exit;
res = mbedtls_ecp_point_write_binary(&ctx.grp, &ctx.Q, MBEDTLS_ECP_PF_UNCOMPRESSED, &realkeylen, key, keylen);
if (realkeylen != 65)
res = 2;
exit:
mbedtls_ecdsa_free(&ctx);
return res;
}
int ecdsa_signature_create(uint8_t *key_d, uint8_t *key_xy, uint8_t *input, int length, uint8_t *signature, size_t *signaturelen) { int ecdsa_signature_create(uint8_t *key_d, uint8_t *key_xy, uint8_t *input, int length, uint8_t *signature, size_t *signaturelen) {
int res; int res;
*signaturelen = 0; *signaturelen = 0;

View file

@ -14,6 +14,7 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <mbedtls/pk.h>
extern int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length); extern int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length);
extern int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length); extern int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length);
@ -23,6 +24,7 @@ extern int aes_cmac8(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, in
extern int sha256hash(uint8_t *input, int length, uint8_t *hash); extern int sha256hash(uint8_t *input, int length, uint8_t *hash);
extern int ecdsa_key_create(uint8_t * key_d, uint8_t *key_xy); extern int ecdsa_key_create(uint8_t * key_d, uint8_t *key_xy);
extern int ecdsa_public_key_from_pk(mbedtls_pk_context *pk, uint8_t *key, size_t keylen);
extern int ecdsa_signature_create(uint8_t *key_d, uint8_t *key_xy, uint8_t *input, int length, uint8_t *signature, size_t *signaturelen); extern int ecdsa_signature_create(uint8_t *key_d, uint8_t *key_xy, uint8_t *input, int length, uint8_t *signature, size_t *signaturelen);
extern int ecdsa_signature_verify(uint8_t *key_xy, uint8_t *input, int length, uint8_t *signature, size_t signaturelen); extern int ecdsa_signature_verify(uint8_t *key_xy, uint8_t *input, int length, uint8_t *signature, size_t signaturelen);
extern char *ecdsa_get_error(int ret); extern char *ecdsa_get_error(int ret);

View file

@ -681,3 +681,17 @@ bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level)
return true; return true;
} }
char *emv_get_tag_name(const struct tlv *tlv)
{
static char *defstr = "";
if (!tlv)
return defstr;
const struct emv_tag *tag = emv_get_tag(tlv);
if (tag)
return tag->name;
return defstr;
}

View file

@ -18,7 +18,6 @@
#include "tlv.h" #include "tlv.h"
#include <stdio.h> #include <stdio.h>
#include <inttypes.h>
// AC // AC
# define EMVAC_AC_MASK 0xC0 # define EMVAC_AC_MASK 0xC0
@ -32,5 +31,6 @@
# define EMVCID_REASON_MASK 0x07 # define EMVCID_REASON_MASK 0x07
bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level); bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level);
char *emv_get_tag_name(const struct tlv *tlv);
#endif #endif

View file

@ -228,8 +228,10 @@ int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool Includ
if (sw) *sw = 0; if (sw) *sw = 0;
uint16_t isw = 0; uint16_t isw = 0;
if (ActivateField) if (ActivateField) {
DropField(); DropField();
msleep(50);
}
// COMPUTE APDU // COMPUTE APDU
memcpy(data, &apdu, 5); memcpy(data, &apdu, 5);

View file

@ -68,6 +68,9 @@ extern struct tlvdb *GetdCVVRawFromTrack2(const struct tlv *track2);
extern void SetAPDULogging(bool logging); extern void SetAPDULogging(bool logging);
// exchange
extern int EMVExchange(bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv);
// search application // search application
extern int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv); extern int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);
extern int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv); extern int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);

385
client/emv/emvjson.c Normal file
View file

@ -0,0 +1,385 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2018 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// EMV json logic
//-----------------------------------------------------------------------------
#include "emvjson.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include "util.h"
#include "ui.h"
#include "proxmark3.h"
#include "emv_tags.h"
static const ApplicationDataElm ApplicationData[] = {
{0x82, "AIP"},
{0x94, "AFL"},
{0x5A, "PAN"},
{0x5F34, "PANSeqNo"},
{0x5F24, "ExpirationDate"},
{0x5F25, "EffectiveDate"},
{0x5F28, "IssuerCountryCode"},
{0x50, "ApplicationLabel"},
{0x9F08, "VersionNumber"},
{0x9F42, "CurrencyCode"},
{0x5F2D, "LanguagePreference"},
{0x87, "PriorityIndicator"},
{0x9F36, "ATC"}, //Application Transaction Counter
{0x5F20, "CardholderName"},
{0x9F38, "PDOL"},
{0x8C, "CDOL1"},
{0x8D, "CDOL2"},
{0x9F07, "AUC"}, // Application Usage Control
{0x9F6C, "CTQ"},
{0x8E, "CVMList"},
{0x9F0D, "IACDefault"},
{0x9F0E, "IACDeny"},
{0x9F0F, "IACOnline"},
{0x8F, "CertificationAuthorityPublicKeyIndex"},
{0x9F32, "IssuerPublicKeyExponent"},
{0x92, "IssuerPublicKeyRemainder"},
{0x90, "IssuerPublicKeyCertificate"},
{0x9F47, "ICCPublicKeyExponent"},
{0x9F46, "ICCPublicKeyCertificate"},
{0x00, "end..."}
};
int ApplicationDataLen = sizeof(ApplicationData) / sizeof(ApplicationDataElm);
char* GetApplicationDataName(tlv_tag_t tag) {
for (int i = 0; i < ApplicationDataLen; i++)
if (ApplicationData[i].Tag == tag)
return ApplicationData[i].Name;
return NULL;
}
int JsonSaveJsonObject(json_t *root, char *path, json_t *value) {
json_error_t error;
if (strlen(path) < 1)
return 1;
if (path[0] == '$') {
if (json_path_set(root, path, value, 0, &error)) {
PrintAndLog("ERROR: can't set json path: ", error.text);
return 2;
} else {
return 0;
}
} else {
return json_object_set_new(root, path, value);
}
}
int JsonSaveInt(json_t *root, char *path, int value) {
return JsonSaveJsonObject(root, path, json_integer(value));
}
int JsonSaveStr(json_t *root, char *path, char *value) {
return JsonSaveJsonObject(root, path, json_string(value));
};
int JsonSaveBufAsHexCompact(json_t *elm, char *path, uint8_t *data, size_t datalen) {
char * msg = sprint_hex_inrow(data, datalen);
if (msg && strlen(msg) && msg[strlen(msg) - 1] == ' ')
msg[strlen(msg) - 1] = '\0';
return JsonSaveStr(elm, path, msg);
}
int JsonSaveBufAsHex(json_t *elm, char *path, uint8_t *data, size_t datalen) {
char * msg = sprint_hex(data, datalen);
if (msg && strlen(msg) && msg[strlen(msg) - 1] == ' ')
msg[strlen(msg) - 1] = '\0';
return JsonSaveStr(elm, path, msg);
}
int JsonSaveHex(json_t *elm, char *path, uint64_t data, int datalen) {
uint8_t bdata[8] = {0};
int len = 0;
if (!datalen) {
for (uint64_t u = 0xffffffffffffffff; u; u = u << 8) {
if (!(data & u)) {
break;
}
len++;
}
if (!len)
len = 1;
} else {
len = datalen;
}
num_to_bytes(data, len, bdata);
return JsonSaveBufAsHex(elm, path, bdata, len);
}
int JsonSaveTLVValue(json_t *root, char *path, struct tlvdb *tlvdbelm) {
const struct tlv *tlvelm = tlvdb_get_tlv(tlvdbelm);
if (tlvelm)
return JsonSaveBufAsHex(root, path, (uint8_t *)tlvelm->value, tlvelm->len);
else
return 1;
}
int JsonSaveTLVElm(json_t *elm, char *path, struct tlv *tlvelm, bool saveName, bool saveValue, bool saveAppDataLink) {
json_error_t error;
if (strlen(path) < 1 || !tlvelm)
return 1;
if (path[0] == '$') {
json_t *obj = json_path_get(elm, path);
if (!obj) {
obj = json_object();
if (json_is_array(elm)) {
if (json_array_append_new(elm, obj)) {
PrintAndLog("ERROR: can't append array: %s", path);
return 2;
}
} else {
if (json_path_set(elm, path, obj, 0, &error)) {
PrintAndLog("ERROR: can't set json path: ", error.text);
return 2;
}
}
}
if (saveAppDataLink) {
char * AppDataName = GetApplicationDataName(tlvelm->tag);
if (AppDataName)
JsonSaveStr(obj, "appdata", AppDataName);
} else {
char * name = emv_get_tag_name(tlvelm);
if (saveName && name && strlen(name) > 0 && strncmp(name, "Unknown", 7))
JsonSaveStr(obj, "name", emv_get_tag_name(tlvelm));
JsonSaveHex(obj, "tag", tlvelm->tag, 0);
if (saveValue) {
JsonSaveHex(obj, "length", tlvelm->len, 0);
JsonSaveBufAsHex(obj, "value", (uint8_t *)tlvelm->value, tlvelm->len);
};
}
}
return 0;
}
int JsonSaveTLVTreeElm(json_t *elm, char *path, struct tlvdb *tlvdbelm, bool saveName, bool saveValue, bool saveAppDataLink) {
return JsonSaveTLVElm(elm, path, (struct tlv *)tlvdb_get_tlv(tlvdbelm), saveName, saveValue, saveAppDataLink);
}
int JsonSaveTLVTree(json_t *root, json_t *elm, char *path, struct tlvdb *tlvdbelm) {
struct tlvdb *tlvp = tlvdbelm;
while (tlvp) {
const struct tlv * tlvpelm = tlvdb_get_tlv(tlvp);
char * AppDataName = NULL;
if (tlvpelm)
AppDataName = GetApplicationDataName(tlvpelm->tag);
if (AppDataName) {
char appdatalink[200] = {0};
sprintf(appdatalink, "$.ApplicationData.%s", AppDataName);
JsonSaveBufAsHex(root, appdatalink, (uint8_t *)tlvpelm->value, tlvpelm->len);
}
json_t *pelm = json_path_get(elm, path);
if (pelm && json_is_array(pelm)) {
json_t *appendelm = json_object();
json_array_append_new(pelm, appendelm);
JsonSaveTLVTreeElm(appendelm, "$", tlvp, !AppDataName, !tlvdb_elm_get_children(tlvp), AppDataName);
pelm = appendelm;
} else {
JsonSaveTLVTreeElm(elm, path, tlvp, !AppDataName, !tlvdb_elm_get_children(tlvp), AppDataName);
pelm = json_path_get(elm, path);
}
if (tlvdb_elm_get_children(tlvp)) {
// get path element
if(!pelm)
return 1;
// check childs element and add it if not found
json_t *chjson = json_path_get(pelm, "$.Childs");
if (!chjson) {
json_object_set_new(pelm, "Childs", json_array());
chjson = json_path_get(pelm, "$.Childs");
}
// check
if (!json_is_array(chjson)) {
PrintAndLog("E->Internal logic error. `$.Childs` is not an array.");
break;
}
// Recursion
JsonSaveTLVTree(root, chjson, "$", tlvdb_elm_get_children(tlvp));
}
tlvp = tlvdb_elm_get_next(tlvp);
}
return 0;
}
bool HexToBuffer(const char *errormsg, const char *hexvalue, uint8_t * buffer, size_t maxbufferlen, size_t *bufferlen) {
int buflen = 0;
switch(param_gethex_to_eol(hexvalue, 0, buffer, maxbufferlen, &buflen)) {
case 1:
PrintAndLog("%s Invalid HEX value.", errormsg);
return false;
case 2:
PrintAndLog("%s Hex value too large.", errormsg);
return false;
case 3:
PrintAndLog("%s Hex value must have even number of digits.", errormsg);
return false;
}
if (buflen > maxbufferlen) {
PrintAndLog("%s HEX length (%d) more than %d", errormsg, *bufferlen, maxbufferlen);
return false;
}
*bufferlen = buflen;
return true;
}
int JsonLoadBufAsHex(json_t *elm, char *path, uint8_t *data, size_t maxbufferlen, size_t *datalen) {
if (datalen)
*datalen = 0;
json_t *jelm = json_path_get((const json_t *)elm, path);
if (!jelm || !json_is_string(jelm))
return 1;
if (!HexToBuffer("ERROR load", json_string_value(jelm), data, maxbufferlen, datalen))
return 2;
return 0;
};
bool ParamLoadFromJson(struct tlvdb *tlv) {
json_t *root;
json_error_t error;
if (!tlv) {
PrintAndLog("ERROR load params: tlv tree is NULL.");
return false;
}
// current path + file name
const char *relfname = "emv/defparams.json";
char fname[strlen(get_my_executable_directory()) + strlen(relfname) + 1];
strcpy(fname, get_my_executable_directory());
strcat(fname, relfname);
root = json_load_file(fname, 0, &error);
if (!root) {
PrintAndLog("Load params: json error on line %d: %s", error.line, error.text);
return false;
}
if (!json_is_array(root)) {
PrintAndLog("Load params: Invalid json format. root must be array.");
return false;
}
PrintAndLog("Load params: json(%d) OK", json_array_size(root));
for(int i = 0; i < json_array_size(root); i++) {
json_t *data, *jtag, *jlength, *jvalue;
data = json_array_get(root, i);
if(!json_is_object(data))
{
PrintAndLog("Load params: data [%d] is not an object", i + 1);
json_decref(root);
return false;
}
jtag = json_object_get(data, "tag");
if(!json_is_string(jtag))
{
PrintAndLog("Load params: data [%d] tag is not a string", i + 1);
json_decref(root);
return false;
}
const char *tlvTag = json_string_value(jtag);
jvalue = json_object_get(data, "value");
if(!json_is_string(jvalue))
{
PrintAndLog("Load params: data [%d] value is not a string", i + 1);
json_decref(root);
return false;
}
const char *tlvValue = json_string_value(jvalue);
jlength = json_object_get(data, "length");
if(!json_is_number(jlength))
{
PrintAndLog("Load params: data [%d] length is not a number", i + 1);
json_decref(root);
return false;
}
int tlvLength = json_integer_value(jlength);
if (tlvLength > 250) {
PrintAndLog("Load params: data [%d] length more than 250", i + 1);
json_decref(root);
return false;
}
PrintAndLog("TLV param: %s[%d]=%s", tlvTag, tlvLength, tlvValue);
uint8_t buf[251] = {0};
size_t buflen = 0;
// here max length must be 4, but now tlv_tag_t is 2-byte var. so let it be 2 by now... TODO: needs refactoring tlv_tag_t...
if (!HexToBuffer("TLV Error type:", tlvTag, buf, 2, &buflen)) {
json_decref(root);
return false;
}
tlv_tag_t tag = 0;
for (int i = 0; i < buflen; i++) {
tag = (tag << 8) + buf[i];
}
if (!HexToBuffer("TLV Error value:", tlvValue, buf, sizeof(buf) - 1, &buflen)) {
json_decref(root);
return false;
}
if (buflen != tlvLength) {
PrintAndLog("Load params: data [%d] length of HEX must(%d) be identical to length in TLV param(%d)", i + 1, buflen, tlvLength);
json_decref(root);
return false;
}
tlvdb_change_or_add_node(tlv, tag, tlvLength, (const unsigned char *)buf);
}
json_decref(root);
return true;
}

40
client/emv/emvjson.h Normal file
View file

@ -0,0 +1,40 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2018 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// EMV json logic
//-----------------------------------------------------------------------------
#ifndef EMVJSON_H__
#define EMVJSON_H__
#include <jansson.h>
#include "tlv.h"
typedef struct {
tlv_tag_t Tag;
char *Name;
} ApplicationDataElm;
extern char* GetApplicationDataName(tlv_tag_t tag);
extern int JsonSaveJsonObject(json_t *root, char *path, json_t *value);
extern int JsonSaveStr(json_t *root, char *path, char *value);
extern int JsonSaveInt(json_t *root, char *path, int value);
extern int JsonSaveBufAsHexCompact(json_t *elm, char *path, uint8_t *data, size_t datalen);
extern int JsonSaveBufAsHex(json_t *elm, char *path, uint8_t *data, size_t datalen);
extern int JsonSaveHex(json_t *elm, char *path, uint64_t data, int datalen);
extern int JsonSaveTLVValue(json_t *root, char *path, struct tlvdb *tlvdbelm);
extern int JsonSaveTLVElm(json_t *elm, char *path, struct tlv *tlvelm, bool saveName, bool saveValue, bool saveAppDataLink);
extern int JsonSaveTLVTreeElm(json_t *elm, char *path, struct tlvdb *tlvdbelm, bool saveName, bool saveValue, bool saveAppDataLink);
extern int JsonSaveTLVTree(json_t *root, json_t *elm, char *path, struct tlvdb *tlvdbelm);
extern int JsonLoadBufAsHex(json_t *elm, char *path, uint8_t *data, size_t maxbufferlen, size_t *datalen);
extern bool ParamLoadFromJson(struct tlvdb *tlv);
#endif

View file

@ -318,6 +318,24 @@ struct tlvdb *tlvdb_find(struct tlvdb *tlvdb, tlv_tag_t tag) {
return NULL; return NULL;
} }
struct tlvdb *tlvdb_find_full(struct tlvdb *tlvdb, tlv_tag_t tag) {
if (!tlvdb)
return NULL;
for (; tlvdb; tlvdb = tlvdb->next) {
if (tlvdb->tag.tag == tag)
return tlvdb;
if (tlvdb->children) {
struct tlvdb * ch = tlvdb_find_full(tlvdb->children, tag);
if (ch)
return ch;
}
}
return NULL;
}
struct tlvdb *tlvdb_find_path(struct tlvdb *tlvdb, tlv_tag_t tag[]) { struct tlvdb *tlvdb_find_path(struct tlvdb *tlvdb, tlv_tag_t tag[]) {
int i = 0; int i = 0;
struct tlvdb *tnext = tlvdb; struct tlvdb *tnext = tlvdb;
@ -342,6 +360,52 @@ void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other)
tlvdb->next = other; tlvdb->next = other;
} }
void tlvdb_change_or_add_node(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, const unsigned char *value)
{
struct tlvdb *telm = tlvdb_find_full(tlvdb, tag);
if (telm == NULL) {
// new tlv element
tlvdb_add(tlvdb, tlvdb_fixed(tag, len, value));
} else {
// the same tlv structure
if (telm->tag.tag == tag && telm->tag.len == len && !memcmp(telm->tag.value, value, len))
return;
// replace tlv element
struct tlvdb *tnewelm = tlvdb_fixed(tag, len, value);
tnewelm->next = telm->next;
tnewelm->parent = telm->parent;
// if telm stayed first in children chain
if (telm->parent && telm->parent->children == telm) {
telm->parent->children = tnewelm;
}
// if telm have previous element
if (telm != tlvdb) {
// elm in root
struct tlvdb *celm = tlvdb;
// elm in child list of node
if (telm->parent && telm->parent->children)
celm = telm->parent->children;
// find previous element
for (; celm; celm = celm->next) {
if (celm->next == telm) {
celm->next = tnewelm;
break;
}
}
}
// free old element with childrens
telm->next = NULL;
tlvdb_free(telm);
}
return;
}
void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level) void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level)
{ {
struct tlvdb *next = NULL; struct tlvdb *next = NULL;
@ -394,6 +458,10 @@ const struct tlv *tlvdb_get_inchild(const struct tlvdb *tlvdb, tlv_tag_t tag, co
return tlvdb_get(tlvdb, tag, prev); return tlvdb_get(tlvdb, tag, prev);
} }
const struct tlv *tlvdb_get_tlv(const struct tlvdb *tlvdb) {
return &tlvdb->tag;
}
unsigned char *tlv_encode(const struct tlv *tlv, size_t *len) unsigned char *tlv_encode(const struct tlv *tlv, size_t *len)
{ {
size_t size = tlv->len; size_t size = tlv->len;
@ -452,3 +520,18 @@ bool tlv_equal(const struct tlv *a, const struct tlv *b)
return a->tag == b->tag && a->len == b->len && !memcmp(a->value, b->value, a->len); return a->tag == b->tag && a->len == b->len && !memcmp(a->value, b->value, a->len);
} }
struct tlvdb *tlvdb_elm_get_next(struct tlvdb *tlvdb)
{
return tlvdb->next;
}
struct tlvdb *tlvdb_elm_get_children(struct tlvdb *tlvdb)
{
return tlvdb->children;
}
struct tlvdb *tlvdb_elm_get_parent(struct tlvdb *tlvdb)
{
return tlvdb->parent;
}

View file

@ -39,15 +39,22 @@ struct tlvdb *tlvdb_parse(const unsigned char *buf, size_t len);
struct tlvdb *tlvdb_parse_multi(const unsigned char *buf, size_t len); struct tlvdb *tlvdb_parse_multi(const unsigned char *buf, size_t len);
void tlvdb_free(struct tlvdb *tlvdb); void tlvdb_free(struct tlvdb *tlvdb);
struct tlvdb *tlvdb_elm_get_next(struct tlvdb *tlvdb);
struct tlvdb *tlvdb_elm_get_children(struct tlvdb *tlvdb);
struct tlvdb *tlvdb_elm_get_parent(struct tlvdb *tlvdb);
struct tlvdb *tlvdb_find_full(struct tlvdb *tlvdb, tlv_tag_t tag); // search also in childrens
struct tlvdb *tlvdb_find(struct tlvdb *tlvdb, tlv_tag_t tag); struct tlvdb *tlvdb_find(struct tlvdb *tlvdb, tlv_tag_t tag);
struct tlvdb *tlvdb_find_next(struct tlvdb *tlvdb, tlv_tag_t tag); struct tlvdb *tlvdb_find_next(struct tlvdb *tlvdb, tlv_tag_t tag);
struct tlvdb *tlvdb_find_path(struct tlvdb *tlvdb, tlv_tag_t tag[]); struct tlvdb *tlvdb_find_path(struct tlvdb *tlvdb, tlv_tag_t tag[]);
void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other); void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other);
void tlvdb_change_or_add_node(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, const unsigned char *value);
void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level); void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level);
const struct tlv *tlvdb_get(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev); const struct tlv *tlvdb_get(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev);
const struct tlv *tlvdb_get_inchild(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev); const struct tlv *tlvdb_get_inchild(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev);
const struct tlv *tlvdb_get_tlv(const struct tlvdb *tlvdb);
bool tlv_parse_tl(const unsigned char **buf, size_t *len, struct tlv *tlv); bool tlv_parse_tl(const unsigned char **buf, size_t *len, struct tlv *tlv);
unsigned char *tlv_encode(const struct tlv *tlv, size_t *len); unsigned char *tlv_encode(const struct tlv *tlv, size_t *len);

View file

@ -0,0 +1,63 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2018 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// CA PEM certificates
//-----------------------------------------------------------------------------
//
#include "additional_ca.h"
#include "mbedtls/certs.h"
#define GLOBALSIGN_CA \
"-----BEGIN CERTIFICATE-----\r\n" \
"MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG\r\n" \
"A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv\r\n" \
"b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw\r\n" \
"MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i\r\n" \
"YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT\r\n" \
"aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ\r\n" \
"jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp\r\n" \
"xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp\r\n" \
"1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG\r\n" \
"snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ\r\n" \
"U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8\r\n" \
"9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E\r\n" \
"BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B\r\n" \
"AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz\r\n" \
"yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE\r\n" \
"38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP\r\n" \
"AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad\r\n" \
"DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME\r\n" \
"HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==\r\n" \
"-----END CERTIFICATE-----\r\n"
// Name: Yubico U2F Root CA Serial 457200631
// Issued: 2014-08-01
#define YUBICO_CA \
"-----BEGIN CERTIFICATE-----\r\n" \
"MIIDHjCCAgagAwIBAgIEG0BT9zANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZ\r\n" \
"dWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAw\r\n" \
"MDBaGA8yMDUwMDkwNDAwMDAwMFowLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290\r\n" \
"IENBIFNlcmlhbCA0NTcyMDA2MzEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\r\n" \
"AoIBAQC/jwYuhBVlqaiYWEMsrWFisgJ+PtM91eSrpI4TK7U53mwCIawSDHy8vUmk\r\n" \
"5N2KAj9abvT9NP5SMS1hQi3usxoYGonXQgfO6ZXyUA9a+KAkqdFnBnlyugSeCOep\r\n" \
"8EdZFfsaRFtMjkwz5Gcz2Py4vIYvCdMHPtwaz0bVuzneueIEz6TnQjE63Rdt2zbw\r\n" \
"nebwTG5ZybeWSwbzy+BJ34ZHcUhPAY89yJQXuE0IzMZFcEBbPNRbWECRKgjq//qT\r\n" \
"9nmDOFVlSRCt2wiqPSzluwn+v+suQEBsUjTGMEd25tKXXTkNW21wIWbxeSyUoTXw\r\n" \
"LvGS6xlwQSgNpk2qXYwf8iXg7VWZAgMBAAGjQjBAMB0GA1UdDgQWBBQgIvz0bNGJ\r\n" \
"hjgpToksyKpP9xv9oDAPBgNVHRMECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAN\r\n" \
"BgkqhkiG9w0BAQsFAAOCAQEAjvjuOMDSa+JXFCLyBKsycXtBVZsJ4Ue3LbaEsPY4\r\n" \
"MYN/hIQ5ZM5p7EjfcnMG4CtYkNsfNHc0AhBLdq45rnT87q/6O3vUEtNMafbhU6kt\r\n" \
"hX7Y+9XFN9NpmYxr+ekVY5xOxi8h9JDIgoMP4VB1uS0aunL1IGqrNooL9mmFnL2k\r\n" \
"LVVee6/VR6C5+KSTCMCWppMuJIZII2v9o4dkoZ8Y7QRjQlLfYzd3qGtKbw7xaF1U\r\n" \
"sG/5xUb/Btwb2X2g4InpiB/yt/3CpQXpiWX/K4mBvUKiGn05ZsqeY1gx4g0xLBqc\r\n" \
"U9psmyPzK+Vsgw2jeRQ5JlKDyqE0hebfC1tvFu0CCrJFcw==\r\n" \
"-----END CERTIFICATE-----\r\n"
/* Concatenation of all additional CA certificates in PEM format if available */
const char additional_ca_pem[] = GLOBALSIGN_CA YUBICO_CA;
const size_t additional_ca_pem_len = sizeof(additional_ca_pem);

View file

@ -0,0 +1,21 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2018 Merlok
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// CA PEM certificates
//-----------------------------------------------------------------------------
//
#ifndef __ADDITIONAL_CA_H__
#define __ADDITIONAL_CA_H__
#include <stddef.h>
// Concatenation of all CA certificates in PEM format if available
extern const char additional_ca_pem[];
extern const size_t additional_ca_pem_len;
#endif /* __ADDITIONAL_CA_H__ */

64
client/jansson/Makefile Normal file
View file

@ -0,0 +1,64 @@
include_HEADERS = jansson.h
nodist_include_HEADERS = jansson_config.h
LIB_A = libjansson.a
libjansson_la_SOURCES = \
dump.c \
error.c \
hashtable.c \
hashtable.h \
hashtable_seed.c \
jansson_private.h \
load.c \
lookup3.h \
memory.c \
pack_unpack.c \
strbuffer.c \
strbuffer.h \
strconv.c \
utf.c \
utf.h \
path.c \
value.c
libjansson_la_LDFLAGS = \
-no-undefined \
-export-symbols-regex '^json_' \
-version-info 15:0:11
CFILES = $(filter %.c, $(libjansson_la_SOURCES))
CMDOBJS = $(CFILES:%.c=%.o)
CLEAN = $(CMDOBJS)
CC= gcc
CFLAGS= -O2 -Wall -Wno-unused-variable -Wno-unused-function
LDFLAGS= $(SYSLDFLAGS) $(libjansson_la_LDFLAGS)
LIBS= -lm $(SYSLIBS) $(MYLIBS)
DEFAULT_INCLUDES = -I.
DEFS = -DHAVE_STDINT_H
AR= ar rcs
RANLIB= ranlib
RM= rm -f
TST= echo
SYSLDFLAGS=
SYSLIBS=
MYLIBS=
MYOBJS=
all: $(CMDOBJS)
$(AR) $(LIB_A) $(CMDOBJS)
$(RANLIB) $(LIB_A)
clean:
$(RM) $(CLEAN)
$(RM) $(LIB_A)
%.o: %.c
$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(CFLAGS) -c -o $@ $< $(LIBS)
.PHONY: all clean

504
client/jansson/dump.c Normal file
View file

@ -0,0 +1,504 @@
/*
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "jansson_private.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "jansson.h"
#include "strbuffer.h"
#include "utf.h"
#define MAX_INTEGER_STR_LENGTH 100
#define MAX_REAL_STR_LENGTH 100
#define FLAGS_TO_INDENT(f) ((f) & 0x1F)
#define FLAGS_TO_PRECISION(f) (((f) >> 11) & 0x1F)
struct buffer {
const size_t size;
size_t used;
char *data;
};
static int dump_to_strbuffer(const char *buffer, size_t size, void *data)
{
return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
}
static int dump_to_buffer(const char *buffer, size_t size, void *data)
{
struct buffer *buf = (struct buffer *)data;
if(buf->used + size <= buf->size)
memcpy(&buf->data[buf->used], buffer, size);
buf->used += size;
return 0;
}
static int dump_to_file(const char *buffer, size_t size, void *data)
{
FILE *dest = (FILE *)data;
if(fwrite(buffer, size, 1, dest) != 1)
return -1;
return 0;
}
static int dump_to_fd(const char *buffer, size_t size, void *data)
{
int *dest = (int *)data;
#ifdef HAVE_UNISTD_H
if(write(*dest, buffer, size) == (ssize_t)size)
return 0;
#endif
return -1;
}
/* 32 spaces (the maximum indentation size) */
static const char whitespace[] = " ";
static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data)
{
if(FLAGS_TO_INDENT(flags) > 0)
{
unsigned int ws_count = FLAGS_TO_INDENT(flags), n_spaces = depth * ws_count;
if(dump("\n", 1, data))
return -1;
while(n_spaces > 0)
{
int cur_n = n_spaces < sizeof whitespace - 1 ? n_spaces : sizeof whitespace - 1;
if(dump(whitespace, cur_n, data))
return -1;
n_spaces -= cur_n;
}
}
else if(space && !(flags & JSON_COMPACT))
{
return dump(" ", 1, data);
}
return 0;
}
static int dump_string(const char *str, size_t len, json_dump_callback_t dump, void *data, size_t flags)
{
const char *pos, *end, *lim;
int32_t codepoint;
if(dump("\"", 1, data))
return -1;
end = pos = str;
lim = str + len;
while(1)
{
const char *text;
char seq[13];
int length;
while(end < lim)
{
end = utf8_iterate(pos, lim - pos, &codepoint);
if(!end)
return -1;
/* mandatory escape or control char */
if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
break;
/* slash */
if((flags & JSON_ESCAPE_SLASH) && codepoint == '/')
break;
/* non-ASCII */
if((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F)
break;
pos = end;
}
if(pos != str) {
if(dump(str, pos - str, data))
return -1;
}
if(end == pos)
break;
/* handle \, /, ", and control codes */
length = 2;
switch(codepoint)
{
case '\\': text = "\\\\"; break;
case '\"': text = "\\\""; break;
case '\b': text = "\\b"; break;
case '\f': text = "\\f"; break;
case '\n': text = "\\n"; break;
case '\r': text = "\\r"; break;
case '\t': text = "\\t"; break;
case '/': text = "\\/"; break;
default:
{
/* codepoint is in BMP */
if(codepoint < 0x10000)
{
snprintf(seq, sizeof(seq), "\\u%04X", (unsigned int)codepoint);
length = 6;
}
/* not in BMP -> construct a UTF-16 surrogate pair */
else
{
int32_t first, last;
codepoint -= 0x10000;
first = 0xD800 | ((codepoint & 0xffc00) >> 10);
last = 0xDC00 | (codepoint & 0x003ff);
snprintf(seq, sizeof(seq), "\\u%04X\\u%04X", (unsigned int)first, (unsigned int)last);
length = 12;
}
text = seq;
break;
}
}
if(dump(text, length, data))
return -1;
str = pos = end;
}
return dump("\"", 1, data);
}
static int compare_keys(const void *key1, const void *key2)
{
return strcmp(*(const char **)key1, *(const char **)key2);
}
static int loop_check(hashtable_t *parents, const json_t *json, char *key, size_t key_size)
{
snprintf(key, key_size, "%p", json);
if (hashtable_get(parents, key))
return -1;
return hashtable_set(parents, key, json_null());
}
static int do_dump(const json_t *json, size_t flags, int depth,
hashtable_t *parents, json_dump_callback_t dump, void *data)
{
int embed = flags & JSON_EMBED;
flags &= ~JSON_EMBED;
if(!json)
return -1;
switch(json_typeof(json)) {
case JSON_NULL:
return dump("null", 4, data);
case JSON_TRUE:
return dump("true", 4, data);
case JSON_FALSE:
return dump("false", 5, data);
case JSON_INTEGER:
{
char buffer[MAX_INTEGER_STR_LENGTH];
int size;
size = snprintf(buffer, MAX_INTEGER_STR_LENGTH,
"%" JSON_INTEGER_FORMAT,
json_integer_value(json));
if(size < 0 || size >= MAX_INTEGER_STR_LENGTH)
return -1;
return dump(buffer, size, data);
}
case JSON_REAL:
{
char buffer[MAX_REAL_STR_LENGTH];
int size;
double value = json_real_value(json);
size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value,
FLAGS_TO_PRECISION(flags));
if(size < 0)
return -1;
return dump(buffer, size, data);
}
case JSON_STRING:
return dump_string(json_string_value(json), json_string_length(json), dump, data, flags);
case JSON_ARRAY:
{
size_t n;
size_t i;
/* Space for "0x", double the sizeof a pointer for the hex and a terminator. */
char key[2 + (sizeof(json) * 2) + 1];
/* detect circular references */
if (loop_check(parents, json, key, sizeof(key)))
return -1;
n = json_array_size(json);
if(!embed && dump("[", 1, data))
return -1;
if(n == 0) {
hashtable_del(parents, key);
return embed ? 0 : dump("]", 1, data);
}
if(dump_indent(flags, depth + 1, 0, dump, data))
return -1;
for(i = 0; i < n; ++i) {
if(do_dump(json_array_get(json, i), flags, depth + 1,
parents, dump, data))
return -1;
if(i < n - 1)
{
if(dump(",", 1, data) ||
dump_indent(flags, depth + 1, 1, dump, data))
return -1;
}
else
{
if(dump_indent(flags, depth, 0, dump, data))
return -1;
}
}
hashtable_del(parents, key);
return embed ? 0 : dump("]", 1, data);
}
case JSON_OBJECT:
{
void *iter;
const char *separator;
int separator_length;
/* Space for "0x", double the sizeof a pointer for the hex and a terminator. */
char key[2 + (sizeof(json) * 2) + 1];
if(flags & JSON_COMPACT) {
separator = ":";
separator_length = 1;
}
else {
separator = ": ";
separator_length = 2;
}
/* detect circular references */
if (loop_check(parents, json, key, sizeof(key)))
return -1;
iter = json_object_iter((json_t *)json);
if(!embed && dump("{", 1, data))
return -1;
if(!iter) {
hashtable_del(parents, key);
return embed ? 0 : dump("}", 1, data);
}
if(dump_indent(flags, depth + 1, 0, dump, data))
return -1;
if(flags & JSON_SORT_KEYS)
{
const char **keys;
size_t size, i;
size = json_object_size(json);
keys = jsonp_malloc(size * sizeof(const char *));
if(!keys)
return -1;
i = 0;
while(iter)
{
keys[i] = json_object_iter_key(iter);
iter = json_object_iter_next((json_t *)json, iter);
i++;
}
assert(i == size);
qsort(keys, size, sizeof(const char *), compare_keys);
for(i = 0; i < size; i++)
{
const char *key;
json_t *value;
key = keys[i];
value = json_object_get(json, key);
assert(value);
dump_string(key, strlen(key), dump, data, flags);
if(dump(separator, separator_length, data) ||
do_dump(value, flags, depth + 1, parents, dump, data))
{
jsonp_free(keys);
return -1;
}
if(i < size - 1)
{
if(dump(",", 1, data) ||
dump_indent(flags, depth + 1, 1, dump, data))
{
jsonp_free(keys);
return -1;
}
}
else
{
if(dump_indent(flags, depth, 0, dump, data))
{
jsonp_free(keys);
return -1;
}
}
}
jsonp_free(keys);
}
else
{
/* Don't sort keys */
while(iter)
{
void *next = json_object_iter_next((json_t *)json, iter);
const char *key = json_object_iter_key(iter);
dump_string(key, strlen(key), dump, data, flags);
if(dump(separator, separator_length, data) ||
do_dump(json_object_iter_value(iter), flags, depth + 1,
parents, dump, data))
return -1;
if(next)
{
if(dump(",", 1, data) ||
dump_indent(flags, depth + 1, 1, dump, data))
return -1;
}
else
{
if(dump_indent(flags, depth, 0, dump, data))
return -1;
}
iter = next;
}
}
hashtable_del(parents, key);
return embed ? 0 : dump("}", 1, data);
}
default:
/* not reached */
return -1;
}
}
char *json_dumps(const json_t *json, size_t flags)
{
strbuffer_t strbuff;
char *result;
if(strbuffer_init(&strbuff))
return NULL;
if(json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags))
result = NULL;
else
result = jsonp_strdup(strbuffer_value(&strbuff));
strbuffer_close(&strbuff);
return result;
}
size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags)
{
struct buffer buf = { size, 0, buffer };
if(json_dump_callback(json, dump_to_buffer, (void *)&buf, flags))
return 0;
return buf.used;
}
int json_dumpf(const json_t *json, FILE *output, size_t flags)
{
return json_dump_callback(json, dump_to_file, (void *)output, flags);
}
int json_dumpfd(const json_t *json, int output, size_t flags)
{
return json_dump_callback(json, dump_to_fd, (void *)&output, flags);
}
int json_dump_file(const json_t *json, const char *path, size_t flags)
{
int result;
FILE *output = fopen(path, "w");
if(!output)
return -1;
result = json_dumpf(json, output, flags);
if(fclose(output) != 0)
return -1;
return result;
}
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
{
int res;
hashtable_t parents_set;
if(!(flags & JSON_ENCODE_ANY)) {
if(!json_is_array(json) && !json_is_object(json))
return -1;
}
if (hashtable_init(&parents_set))
return -1;
res = do_dump(json, flags, 0, &parents_set, callback, data);
hashtable_close(&parents_set);
return res;
}

66
client/jansson/error.c Normal file
View file

@ -0,0 +1,66 @@
#include <string.h>
#include "jansson_private.h"
void jsonp_error_init(json_error_t *error, const char *source)
{
if(error)
{
error->text[0] = '\0';
error->line = -1;
error->column = -1;
error->position = 0;
if(source)
jsonp_error_set_source(error, source);
else
error->source[0] = '\0';
}
}
void jsonp_error_set_source(json_error_t *error, const char *source)
{
size_t length;
if(!error || !source)
return;
length = strlen(source);
if(length < JSON_ERROR_SOURCE_LENGTH)
strncpy(error->source, source, JSON_ERROR_SOURCE_LENGTH);
else {
size_t extra = length - JSON_ERROR_SOURCE_LENGTH + 4;
memcpy(error->source, "...", 3);
strncpy(error->source + 3, source + extra, length - extra + 1);
}
}
void jsonp_error_set(json_error_t *error, int line, int column,
size_t position, enum json_error_code code,
const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
jsonp_error_vset(error, line, column, position, code, msg, ap);
va_end(ap);
}
void jsonp_error_vset(json_error_t *error, int line, int column,
size_t position, enum json_error_code code,
const char *msg, va_list ap)
{
if(!error)
return;
if(error->text[0] != '\0') {
/* error already set */
return;
}
error->line = line;
error->column = column;
error->position = (int)position;
vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH - 1, msg, ap);
error->text[JSON_ERROR_TEXT_LENGTH - 2] = '\0';
error->text[JSON_ERROR_TEXT_LENGTH - 1] = code;
}

356
client/jansson/hashtable.c Normal file
View file

@ -0,0 +1,356 @@
/*
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#if HAVE_CONFIG_H
#include <jansson_private_config.h>
#endif
#include <stdlib.h>
#include <string.h>
#if HAVE_STDINT_H
#include <stdint.h>
#endif
#include <jansson_config.h> /* for JSON_INLINE */
#include "jansson_private.h" /* for container_of() */
#include "hashtable.h"
#ifndef INITIAL_HASHTABLE_ORDER
#define INITIAL_HASHTABLE_ORDER 3
#endif
typedef struct hashtable_list list_t;
typedef struct hashtable_pair pair_t;
typedef struct hashtable_bucket bucket_t;
extern volatile uint32_t hashtable_seed;
/* Implementation of the hash function */
#include "lookup3.h"
#define list_to_pair(list_) container_of(list_, pair_t, list)
#define ordered_list_to_pair(list_) container_of(list_, pair_t, ordered_list)
#define hash_str(key) ((size_t)hashlittle((key), strlen(key), hashtable_seed))
static JSON_INLINE void list_init(list_t *list)
{
list->next = list;
list->prev = list;
}
static JSON_INLINE void list_insert(list_t *list, list_t *node)
{
node->next = list;
node->prev = list->prev;
list->prev->next = node;
list->prev = node;
}
static JSON_INLINE void list_remove(list_t *list)
{
list->prev->next = list->next;
list->next->prev = list->prev;
}
static JSON_INLINE int bucket_is_empty(hashtable_t *hashtable, bucket_t *bucket)
{
return bucket->first == &hashtable->list && bucket->first == bucket->last;
}
static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket,
list_t *list)
{
if(bucket_is_empty(hashtable, bucket))
{
list_insert(&hashtable->list, list);
bucket->first = bucket->last = list;
}
else
{
list_insert(bucket->first, list);
bucket->first = list;
}
}
static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
const char *key, size_t hash)
{
list_t *list;
pair_t *pair;
if(bucket_is_empty(hashtable, bucket))
return NULL;
list = bucket->first;
while(1)
{
pair = list_to_pair(list);
if(pair->hash == hash && strcmp(pair->key, key) == 0)
return pair;
if(list == bucket->last)
break;
list = list->next;
}
return NULL;
}
/* returns 0 on success, -1 if key was not found */
static int hashtable_do_del(hashtable_t *hashtable,
const char *key, size_t hash)
{
pair_t *pair;
bucket_t *bucket;
size_t index;
index = hash & hashmask(hashtable->order);
bucket = &hashtable->buckets[index];
pair = hashtable_find_pair(hashtable, bucket, key, hash);
if(!pair)
return -1;
if(&pair->list == bucket->first && &pair->list == bucket->last)
bucket->first = bucket->last = &hashtable->list;
else if(&pair->list == bucket->first)
bucket->first = pair->list.next;
else if(&pair->list == bucket->last)
bucket->last = pair->list.prev;
list_remove(&pair->list);
list_remove(&pair->ordered_list);
json_decref(pair->value);
jsonp_free(pair);
hashtable->size--;
return 0;
}
static void hashtable_do_clear(hashtable_t *hashtable)
{
list_t *list, *next;
pair_t *pair;
for(list = hashtable->list.next; list != &hashtable->list; list = next)
{
next = list->next;
pair = list_to_pair(list);
json_decref(pair->value);
jsonp_free(pair);
}
}
static int hashtable_do_rehash(hashtable_t *hashtable)
{
list_t *list, *next;
pair_t *pair;
size_t i, index, new_size, new_order;
struct hashtable_bucket *new_buckets;
new_order = hashtable->order + 1;
new_size = hashsize(new_order);
new_buckets = jsonp_malloc(new_size * sizeof(bucket_t));
if(!new_buckets)
return -1;
jsonp_free(hashtable->buckets);
hashtable->buckets = new_buckets;
hashtable->order = new_order;
for(i = 0; i < hashsize(hashtable->order); i++)
{
hashtable->buckets[i].first = hashtable->buckets[i].last =
&hashtable->list;
}
list = hashtable->list.next;
list_init(&hashtable->list);
for(; list != &hashtable->list; list = next) {
next = list->next;
pair = list_to_pair(list);
index = pair->hash % new_size;
insert_to_bucket(hashtable, &hashtable->buckets[index], &pair->list);
}
return 0;
}
int hashtable_init(hashtable_t *hashtable)
{
size_t i;
hashtable->size = 0;
hashtable->order = INITIAL_HASHTABLE_ORDER;
hashtable->buckets = jsonp_malloc(hashsize(hashtable->order) * sizeof(bucket_t));
if(!hashtable->buckets)
return -1;
list_init(&hashtable->list);
list_init(&hashtable->ordered_list);
for(i = 0; i < hashsize(hashtable->order); i++)
{
hashtable->buckets[i].first = hashtable->buckets[i].last =
&hashtable->list;
}
return 0;
}
void hashtable_close(hashtable_t *hashtable)
{
hashtable_do_clear(hashtable);
jsonp_free(hashtable->buckets);
}
int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value)
{
pair_t *pair;
bucket_t *bucket;
size_t hash, index;
/* rehash if the load ratio exceeds 1 */
if(hashtable->size >= hashsize(hashtable->order))
if(hashtable_do_rehash(hashtable))
return -1;
hash = hash_str(key);
index = hash & hashmask(hashtable->order);
bucket = &hashtable->buckets[index];
pair = hashtable_find_pair(hashtable, bucket, key, hash);
if(pair)
{
json_decref(pair->value);
pair->value = value;
}
else
{
/* offsetof(...) returns the size of pair_t without the last,
flexible member. This way, the correct amount is
allocated. */
size_t len = strlen(key);
if(len >= (size_t)-1 - offsetof(pair_t, key)) {
/* Avoid an overflow if the key is very long */
return -1;
}
pair = jsonp_malloc(offsetof(pair_t, key) + len + 1);
if(!pair)
return -1;
pair->hash = hash;
strncpy(pair->key, key, len + 1);
pair->value = value;
list_init(&pair->list);
list_init(&pair->ordered_list);
insert_to_bucket(hashtable, bucket, &pair->list);
list_insert(&hashtable->ordered_list, &pair->ordered_list);
hashtable->size++;
}
return 0;
}
void *hashtable_get(hashtable_t *hashtable, const char *key)
{
pair_t *pair;
size_t hash;
bucket_t *bucket;
hash = hash_str(key);
bucket = &hashtable->buckets[hash & hashmask(hashtable->order)];
pair = hashtable_find_pair(hashtable, bucket, key, hash);
if(!pair)
return NULL;
return pair->value;
}
int hashtable_del(hashtable_t *hashtable, const char *key)
{
size_t hash = hash_str(key);
return hashtable_do_del(hashtable, key, hash);
}
void hashtable_clear(hashtable_t *hashtable)
{
size_t i;
hashtable_do_clear(hashtable);
for(i = 0; i < hashsize(hashtable->order); i++)
{
hashtable->buckets[i].first = hashtable->buckets[i].last =
&hashtable->list;
}
list_init(&hashtable->list);
list_init(&hashtable->ordered_list);
hashtable->size = 0;
}
void *hashtable_iter(hashtable_t *hashtable)
{
return hashtable_iter_next(hashtable, &hashtable->ordered_list);
}
void *hashtable_iter_at(hashtable_t *hashtable, const char *key)
{
pair_t *pair;
size_t hash;
bucket_t *bucket;
hash = hash_str(key);
bucket = &hashtable->buckets[hash & hashmask(hashtable->order)];
pair = hashtable_find_pair(hashtable, bucket, key, hash);
if(!pair)
return NULL;
return &pair->ordered_list;
}
void *hashtable_iter_next(hashtable_t *hashtable, void *iter)
{
list_t *list = (list_t *)iter;
if(list->next == &hashtable->ordered_list)
return NULL;
return list->next;
}
void *hashtable_iter_key(void *iter)
{
pair_t *pair = ordered_list_to_pair((list_t *)iter);
return pair->key;
}
void *hashtable_iter_value(void *iter)
{
pair_t *pair = ordered_list_to_pair((list_t *)iter);
return pair->value;
}
void hashtable_iter_set(void *iter, json_t *value)
{
pair_t *pair = ordered_list_to_pair((list_t *)iter);
json_decref(pair->value);
pair->value = value;
}

176
client/jansson/hashtable.h Normal file
View file

@ -0,0 +1,176 @@
/*
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#ifndef HASHTABLE_H
#define HASHTABLE_H
#include <stdlib.h>
#include "jansson.h"
struct hashtable_list {
struct hashtable_list *prev;
struct hashtable_list *next;
};
/* "pair" may be a bit confusing a name, but think of it as a
key-value pair. In this case, it just encodes some extra data,
too */
struct hashtable_pair {
struct hashtable_list list;
struct hashtable_list ordered_list;
size_t hash;
json_t *value;
char key[1];
};
struct hashtable_bucket {
struct hashtable_list *first;
struct hashtable_list *last;
};
typedef struct hashtable {
size_t size;
struct hashtable_bucket *buckets;
size_t order; /* hashtable has pow(2, order) buckets */
struct hashtable_list list;
struct hashtable_list ordered_list;
} hashtable_t;
#define hashtable_key_to_iter(key_) \
(&(container_of(key_, struct hashtable_pair, key)->ordered_list))
/**
* hashtable_init - Initialize a hashtable object
*
* @hashtable: The (statically allocated) hashtable object
*
* Initializes a statically allocated hashtable object. The object
* should be cleared with hashtable_close when it's no longer used.
*
* Returns 0 on success, -1 on error (out of memory).
*/
int hashtable_init(hashtable_t *hashtable);
/**
* hashtable_close - Release all resources used by a hashtable object
*
* @hashtable: The hashtable
*
* Destroys a statically allocated hashtable object.
*/
void hashtable_close(hashtable_t *hashtable);
/**
* hashtable_set - Add/modify value in hashtable
*
* @hashtable: The hashtable object
* @key: The key
* @serial: For addition order of keys
* @value: The value
*
* If a value with the given key already exists, its value is replaced
* with the new value. Value is "stealed" in the sense that hashtable
* doesn't increment its refcount but decreases the refcount when the
* value is no longer needed.
*
* Returns 0 on success, -1 on failure (out of memory).
*/
int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value);
/**
* hashtable_get - Get a value associated with a key
*
* @hashtable: The hashtable object
* @key: The key
*
* Returns value if it is found, or NULL otherwise.
*/
void *hashtable_get(hashtable_t *hashtable, const char *key);
/**
* hashtable_del - Remove a value from the hashtable
*
* @hashtable: The hashtable object
* @key: The key
*
* Returns 0 on success, or -1 if the key was not found.
*/
int hashtable_del(hashtable_t *hashtable, const char *key);
/**
* hashtable_clear - Clear hashtable
*
* @hashtable: The hashtable object
*
* Removes all items from the hashtable.
*/
void hashtable_clear(hashtable_t *hashtable);
/**
* hashtable_iter - Iterate over hashtable
*
* @hashtable: The hashtable object
*
* Returns an opaque iterator to the first element in the hashtable.
* The iterator should be passed to hashtable_iter_* functions.
* The hashtable items are not iterated over in any particular order.
*
* There's no need to free the iterator in any way. The iterator is
* valid as long as the item that is referenced by the iterator is not
* deleted. Other values may be added or deleted. In particular,
* hashtable_iter_next() may be called on an iterator, and after that
* the key/value pair pointed by the old iterator may be deleted.
*/
void *hashtable_iter(hashtable_t *hashtable);
/**
* hashtable_iter_at - Return an iterator at a specific key
*
* @hashtable: The hashtable object
* @key: The key that the iterator should point to
*
* Like hashtable_iter() but returns an iterator pointing to a
* specific key.
*/
void *hashtable_iter_at(hashtable_t *hashtable, const char *key);
/**
* hashtable_iter_next - Advance an iterator
*
* @hashtable: The hashtable object
* @iter: The iterator
*
* Returns a new iterator pointing to the next element in the
* hashtable or NULL if the whole hastable has been iterated over.
*/
void *hashtable_iter_next(hashtable_t *hashtable, void *iter);
/**
* hashtable_iter_key - Retrieve the key pointed by an iterator
*
* @iter: The iterator
*/
void *hashtable_iter_key(void *iter);
/**
* hashtable_iter_value - Retrieve the value pointed by an iterator
*
* @iter: The iterator
*/
void *hashtable_iter_value(void *iter);
/**
* hashtable_iter_set - Set the value pointed by an iterator
*
* @iter: The iterator
* @value: The value to set
*/
void hashtable_iter_set(void *iter, json_t *value);
#endif

View file

@ -0,0 +1,277 @@
/* Generate sizeof(uint32_t) bytes of as random data as possible to seed
the hash function.
*/
#ifdef HAVE_CONFIG_H
#include <jansson_private_config.h>
#endif
#include <stdio.h>
#include <time.h>
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_SCHED_H
#include <sched.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if defined(_WIN32)
/* For GetModuleHandle(), GetProcAddress() and GetCurrentProcessId() */
#include <windows.h>
#endif
#include "jansson.h"
static uint32_t buf_to_uint32(char *data) {
size_t i;
uint32_t result = 0;
for (i = 0; i < sizeof(uint32_t); i++)
result = (result << 8) | (unsigned char)data[i];
return result;
}
/* /dev/urandom */
#if !defined(_WIN32) && defined(USE_URANDOM)
static int seed_from_urandom(uint32_t *seed) {
/* Use unbuffered I/O if we have open(), close() and read(). Otherwise
fall back to fopen() */
char data[sizeof(uint32_t)];
int ok;
#if defined(HAVE_OPEN) && defined(HAVE_CLOSE) && defined(HAVE_READ)
int urandom;
urandom = open("/dev/urandom", O_RDONLY);
if (urandom == -1)
return 1;
ok = read(urandom, data, sizeof(uint32_t)) == sizeof(uint32_t);
close(urandom);
#else
FILE *urandom;
urandom = fopen("/dev/urandom", "rb");
if (!urandom)
return 1;
ok = fread(data, 1, sizeof(uint32_t), urandom) == sizeof(uint32_t);
fclose(urandom);
#endif
if (!ok)
return 1;
*seed = buf_to_uint32(data);
return 0;
}
#endif
/* Windows Crypto API */
#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI)
#include <wincrypt.h>
typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv, LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType, DWORD dwFlags);
typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen, BYTE *pbBuffer);
typedef BOOL (WINAPI *CRYPTRELEASECONTEXT)(HCRYPTPROV hProv, DWORD dwFlags);
static int seed_from_windows_cryptoapi(uint32_t *seed)
{
HINSTANCE hAdvAPI32 = NULL;
CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL;
CRYPTGENRANDOM pCryptGenRandom = NULL;
CRYPTRELEASECONTEXT pCryptReleaseContext = NULL;
HCRYPTPROV hCryptProv = 0;
BYTE data[sizeof(uint32_t)];
int ok;
hAdvAPI32 = GetModuleHandle(TEXT("advapi32.dll"));
if(hAdvAPI32 == NULL)
return 1;
pCryptAcquireContext = (CRYPTACQUIRECONTEXTA)GetProcAddress(hAdvAPI32, "CryptAcquireContextA");
if (!pCryptAcquireContext)
return 1;
pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32, "CryptGenRandom");
if (!pCryptGenRandom)
return 1;
pCryptReleaseContext = (CRYPTRELEASECONTEXT)GetProcAddress(hAdvAPI32, "CryptReleaseContext");
if (!pCryptReleaseContext)
return 1;
if (!pCryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
return 1;
ok = pCryptGenRandom(hCryptProv, sizeof(uint32_t), data);
pCryptReleaseContext(hCryptProv, 0);
if (!ok)
return 1;
*seed = buf_to_uint32((char *)data);
return 0;
}
#endif
/* gettimeofday() and getpid() */
static int seed_from_timestamp_and_pid(uint32_t *seed) {
#ifdef HAVE_GETTIMEOFDAY
/* XOR of seconds and microseconds */
struct timeval tv;
gettimeofday(&tv, NULL);
*seed = (uint32_t)tv.tv_sec ^ (uint32_t)tv.tv_usec;
#else
/* Seconds only */
*seed = (uint32_t)time(NULL);
#endif
/* XOR with PID for more randomness */
#if defined(_WIN32)
*seed ^= (uint32_t)GetCurrentProcessId();
#elif defined(HAVE_GETPID)
*seed ^= (uint32_t)getpid();
#endif
return 0;
}
static uint32_t generate_seed() {
uint32_t seed;
int done = 0;
#if !defined(_WIN32) && defined(USE_URANDOM)
if (seed_from_urandom(&seed) == 0)
done = 1;
#endif
#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI)
if (seed_from_windows_cryptoapi(&seed) == 0)
done = 1;
#endif
if (!done) {
/* Fall back to timestamp and PID if no better randomness is
available */
seed_from_timestamp_and_pid(&seed);
}
/* Make sure the seed is never zero */
if (seed == 0)
seed = 1;
return seed;
}
volatile uint32_t hashtable_seed = 0;
#if defined(HAVE_ATOMIC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32))
static volatile char seed_initialized = 0;
void json_object_seed(size_t seed) {
uint32_t new_seed = (uint32_t)seed;
if (hashtable_seed == 0) {
if (__atomic_test_and_set(&seed_initialized, __ATOMIC_RELAXED) == 0) {
/* Do the seeding ourselves */
if (new_seed == 0)
new_seed = generate_seed();
__atomic_store_n(&hashtable_seed, new_seed, __ATOMIC_RELEASE);
} else {
/* Wait for another thread to do the seeding */
do {
#ifdef HAVE_SCHED_YIELD
sched_yield();
#endif
} while(__atomic_load_n(&hashtable_seed, __ATOMIC_ACQUIRE) == 0);
}
}
}
#elif defined(HAVE_SYNC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32))
void json_object_seed(size_t seed) {
uint32_t new_seed = (uint32_t)seed;
if (hashtable_seed == 0) {
if (new_seed == 0) {
/* Explicit synchronization fences are not supported by the
__sync builtins, so every thread getting here has to
generate the seed value.
*/
new_seed = generate_seed();
}
do {
if (__sync_bool_compare_and_swap(&hashtable_seed, 0, new_seed)) {
/* We were the first to seed */
break;
} else {
/* Wait for another thread to do the seeding */
#ifdef HAVE_SCHED_YIELD
sched_yield();
#endif
}
} while(hashtable_seed == 0);
}
}
#elif defined(_WIN32)
static long seed_initialized = 0;
void json_object_seed(size_t seed) {
uint32_t new_seed = (uint32_t)seed;
if (hashtable_seed == 0) {
if (InterlockedIncrement(&seed_initialized) == 1) {
/* Do the seeding ourselves */
if (new_seed == 0)
new_seed = generate_seed();
hashtable_seed = new_seed;
} else {
/* Wait for another thread to do the seeding */
do {
SwitchToThread();
} while (hashtable_seed == 0);
}
}
}
#else
/* Fall back to a thread-unsafe version */
void json_object_seed(size_t seed) {
uint32_t new_seed = (uint32_t)seed;
if (hashtable_seed == 0) {
if (new_seed == 0)
new_seed = generate_seed();
hashtable_seed = new_seed;
}
}
#endif

View file

@ -0,0 +1,75 @@
EXPORTS
json_delete
json_true
json_false
json_null
json_sprintf
json_vsprintf
json_string
json_stringn
json_string_nocheck
json_stringn_nocheck
json_string_value
json_string_length
json_string_set
json_string_setn
json_string_set_nocheck
json_string_setn_nocheck
json_integer
json_integer_value
json_integer_set
json_real
json_real_value
json_real_set
json_number_value
json_array
json_array_size
json_array_get
json_array_set_new
json_array_append_new
json_array_insert_new
json_array_remove
json_array_clear
json_array_extend
json_object
json_object_size
json_object_get
json_object_set_new
json_object_set_new_nocheck
json_object_del
json_object_clear
json_object_update
json_object_update_existing
json_object_update_missing
json_object_iter
json_object_iter_at
json_object_iter_next
json_object_iter_key
json_object_iter_value
json_object_iter_set_new
json_object_key_to_iter
json_object_seed
json_dumps
json_dumpb
json_dumpf
json_dumpfd
json_dump_file
json_dump_callback
json_loads
json_loadb
json_loadf
json_loadfd
json_load_file
json_load_callback
json_equal
json_copy
json_deep_copy
json_pack
json_pack_ex
json_vpack_ex
json_unpack
json_unpack_ex
json_vunpack_ex
json_set_alloc_funcs
json_get_alloc_funcs

372
client/jansson/jansson.h Normal file
View file

@ -0,0 +1,372 @@
/*
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#ifndef JANSSON_H
#define JANSSON_H
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h> /* for size_t */
#include <stdarg.h>
#include "jansson_config.h"
#ifdef __cplusplus
extern "C" {
#endif
/* version */
#define JANSSON_MAJOR_VERSION 2
#define JANSSON_MINOR_VERSION 11
#define JANSSON_MICRO_VERSION 0
/* Micro version is omitted if it's 0 */
#define JANSSON_VERSION "2.11"
/* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this
for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */
#define JANSSON_VERSION_HEX ((JANSSON_MAJOR_VERSION << 16) | \
(JANSSON_MINOR_VERSION << 8) | \
(JANSSON_MICRO_VERSION << 0))
/* If __atomic or __sync builtins are available the library is thread
* safe for all read-only functions plus reference counting. */
#if JSON_HAVE_ATOMIC_BUILTINS || JSON_HAVE_SYNC_BUILTINS
#define JANSSON_THREAD_SAFE_REFCOUNT 1
#endif
/* types */
typedef enum {
JSON_OBJECT,
JSON_ARRAY,
JSON_STRING,
JSON_INTEGER,
JSON_REAL,
JSON_TRUE,
JSON_FALSE,
JSON_NULL
} json_type;
typedef struct json_t {
json_type type;
volatile size_t refcount;
} json_t;
#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */
#if JSON_INTEGER_IS_LONG_LONG
#ifdef _WIN32
#define JSON_INTEGER_FORMAT "I64d"
#else
#define JSON_INTEGER_FORMAT "lld"
#endif
typedef long long json_int_t;
#else
#define JSON_INTEGER_FORMAT "ld"
typedef long json_int_t;
#endif /* JSON_INTEGER_IS_LONG_LONG */
#endif
#define json_typeof(json) ((json)->type)
#define json_is_object(json) ((json) && json_typeof(json) == JSON_OBJECT)
#define json_is_array(json) ((json) && json_typeof(json) == JSON_ARRAY)
#define json_is_string(json) ((json) && json_typeof(json) == JSON_STRING)
#define json_is_integer(json) ((json) && json_typeof(json) == JSON_INTEGER)
#define json_is_real(json) ((json) && json_typeof(json) == JSON_REAL)
#define json_is_number(json) (json_is_integer(json) || json_is_real(json))
#define json_is_true(json) ((json) && json_typeof(json) == JSON_TRUE)
#define json_is_false(json) ((json) && json_typeof(json) == JSON_FALSE)
#define json_boolean_value json_is_true
#define json_is_boolean(json) (json_is_true(json) || json_is_false(json))
#define json_is_null(json) ((json) && json_typeof(json) == JSON_NULL)
/* construction, destruction, reference counting */
json_t *json_object(void);
json_t *json_array(void);
json_t *json_string(const char *value);
json_t *json_stringn(const char *value, size_t len);
json_t *json_string_nocheck(const char *value);
json_t *json_stringn_nocheck(const char *value, size_t len);
json_t *json_integer(json_int_t value);
json_t *json_real(double value);
json_t *json_true(void);
json_t *json_false(void);
#define json_boolean(val) ((val) ? json_true() : json_false())
json_t *json_null(void);
/* do not call JSON_INTERNAL_INCREF or JSON_INTERNAL_DECREF directly */
#if JSON_HAVE_ATOMIC_BUILTINS
#define JSON_INTERNAL_INCREF(json) __atomic_add_fetch(&json->refcount, 1, __ATOMIC_ACQUIRE)
#define JSON_INTERNAL_DECREF(json) __atomic_sub_fetch(&json->refcount, 1, __ATOMIC_RELEASE)
#elif JSON_HAVE_SYNC_BUILTINS
#define JSON_INTERNAL_INCREF(json) __sync_add_and_fetch(&json->refcount, 1)
#define JSON_INTERNAL_DECREF(json) __sync_sub_and_fetch(&json->refcount, 1)
#else
#define JSON_INTERNAL_INCREF(json) (++json->refcount)
#define JSON_INTERNAL_DECREF(json) (--json->refcount)
#endif
static JSON_INLINE
json_t *json_incref(json_t *json)
{
if(json && json->refcount != (size_t)-1)
JSON_INTERNAL_INCREF(json);
return json;
}
/* do not call json_delete directly */
void json_delete(json_t *json);
static JSON_INLINE
void json_decref(json_t *json)
{
if(json && json->refcount != (size_t)-1 && JSON_INTERNAL_DECREF(json) == 0)
json_delete(json);
}
#if defined(__GNUC__) || defined(__clang__)
static JSON_INLINE
void json_decrefp(json_t **json)
{
if(json) {
json_decref(*json);
*json = NULL;
}
}
#define json_auto_t json_t __attribute__((cleanup(json_decrefp)))
#endif
/* error reporting */
#define JSON_ERROR_TEXT_LENGTH 160
#define JSON_ERROR_SOURCE_LENGTH 80
typedef struct json_error_t {
int line;
int column;
int position;
char source[JSON_ERROR_SOURCE_LENGTH];
char text[JSON_ERROR_TEXT_LENGTH];
} json_error_t;
enum json_error_code {
json_error_unknown,
json_error_out_of_memory,
json_error_stack_overflow,
json_error_cannot_open_file,
json_error_invalid_argument,
json_error_invalid_utf8,
json_error_premature_end_of_input,
json_error_end_of_input_expected,
json_error_invalid_syntax,
json_error_invalid_format,
json_error_wrong_type,
json_error_null_character,
json_error_null_value,
json_error_null_byte_in_key,
json_error_duplicate_key,
json_error_numeric_overflow,
json_error_item_not_found,
json_error_index_out_of_range
};
static JSON_INLINE enum json_error_code json_error_code(const json_error_t *e) {
return (enum json_error_code)e->text[JSON_ERROR_TEXT_LENGTH - 1];
}
/* getters, setters, manipulation */
void json_object_seed(size_t seed);
size_t json_object_size(const json_t *object);
json_t *json_object_get(const json_t *object, const char *key);
int json_object_set_new(json_t *object, const char *key, json_t *value);
int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value);
int json_object_del(json_t *object, const char *key);
int json_object_clear(json_t *object);
int json_object_update(json_t *object, json_t *other);
int json_object_update_existing(json_t *object, json_t *other);
int json_object_update_missing(json_t *object, json_t *other);
void *json_object_iter(json_t *object);
void *json_object_iter_at(json_t *object, const char *key);
void *json_object_key_to_iter(const char *key);
void *json_object_iter_next(json_t *object, void *iter);
const char *json_object_iter_key(void *iter);
json_t *json_object_iter_value(void *iter);
int json_object_iter_set_new(json_t *object, void *iter, json_t *value);
#define json_object_foreach(object, key, value) \
for(key = json_object_iter_key(json_object_iter(object)); \
key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
key = json_object_iter_key(json_object_iter_next(object, json_object_key_to_iter(key))))
#define json_object_foreach_safe(object, n, key, value) \
for(key = json_object_iter_key(json_object_iter(object)), \
n = json_object_iter_next(object, json_object_key_to_iter(key)); \
key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
key = json_object_iter_key(n), \
n = json_object_iter_next(object, json_object_key_to_iter(key)))
#define json_array_foreach(array, index, value) \
for(index = 0; \
index < json_array_size(array) && (value = json_array_get(array, index)); \
index++)
static JSON_INLINE
int json_object_set(json_t *object, const char *key, json_t *value)
{
return json_object_set_new(object, key, json_incref(value));
}
static JSON_INLINE
int json_object_set_nocheck(json_t *object, const char *key, json_t *value)
{
return json_object_set_new_nocheck(object, key, json_incref(value));
}
static JSON_INLINE
int json_object_iter_set(json_t *object, void *iter, json_t *value)
{
return json_object_iter_set_new(object, iter, json_incref(value));
}
size_t json_array_size(const json_t *array);
json_t *json_array_get(const json_t *array, size_t index);
int json_array_set_new(json_t *array, size_t index, json_t *value);
int json_array_append_new(json_t *array, json_t *value);
int json_array_insert_new(json_t *array, size_t index, json_t *value);
int json_array_remove(json_t *array, size_t index);
int json_array_clear(json_t *array);
int json_array_extend(json_t *array, json_t *other);
static JSON_INLINE
int json_array_set(json_t *array, size_t ind, json_t *value)
{
return json_array_set_new(array, ind, json_incref(value));
}
static JSON_INLINE
int json_array_append(json_t *array, json_t *value)
{
return json_array_append_new(array, json_incref(value));
}
static JSON_INLINE
int json_array_insert(json_t *array, size_t ind, json_t *value)
{
return json_array_insert_new(array, ind, json_incref(value));
}
const char *json_string_value(const json_t *string);
size_t json_string_length(const json_t *string);
json_int_t json_integer_value(const json_t *integer);
double json_real_value(const json_t *real);
double json_number_value(const json_t *json);
int json_string_set(json_t *string, const char *value);
int json_string_setn(json_t *string, const char *value, size_t len);
int json_string_set_nocheck(json_t *string, const char *value);
int json_string_setn_nocheck(json_t *string, const char *value, size_t len);
int json_integer_set(json_t *integer, json_int_t value);
int json_real_set(json_t *real, double value);
/* pack, unpack */
json_t *json_pack(const char *fmt, ...);
json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...);
json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap);
#define JSON_VALIDATE_ONLY 0x1
#define JSON_STRICT 0x2
int json_unpack(json_t *root, const char *fmt, ...);
int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...);
int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, va_list ap);
/* sprintf */
json_t *json_sprintf(const char *fmt, ...);
json_t *json_vsprintf(const char *fmt, va_list ap);
/* equality */
int json_equal(const json_t *value1, const json_t *value2);
/* copying */
json_t *json_copy(json_t *value);
json_t *json_deep_copy(const json_t *value);
/* path */
json_t *json_path_get(const json_t *json, const char *path);
int json_path_set_new(json_t *json, const char *path, json_t *value, size_t flags, json_error_t *error);
static JSON_INLINE
int json_path_set(json_t *json, const char *path, json_t *value, size_t flags, json_error_t *error)
{
return json_path_set_new(json, path, json_incref(value), flags, error);
}
/* decoding */
#define JSON_REJECT_DUPLICATES 0x1
#define JSON_DISABLE_EOF_CHECK 0x2
#define JSON_DECODE_ANY 0x4
#define JSON_DECODE_INT_AS_REAL 0x8
#define JSON_ALLOW_NUL 0x10
typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data);
json_t *json_loads(const char *input, size_t flags, json_error_t *error);
json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error);
json_t *json_loadf(FILE *input, size_t flags, json_error_t *error);
json_t *json_loadfd(int input, size_t flags, json_error_t *error);
json_t *json_load_file(const char *path, size_t flags, json_error_t *error);
json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error);
/* encoding */
#define JSON_MAX_INDENT 0x1F
#define JSON_INDENT(n) ((n) & JSON_MAX_INDENT)
#define JSON_COMPACT 0x20
#define JSON_ENSURE_ASCII 0x40
#define JSON_SORT_KEYS 0x80
#define JSON_PRESERVE_ORDER 0x100
#define JSON_ENCODE_ANY 0x200
#define JSON_ESCAPE_SLASH 0x400
#define JSON_REAL_PRECISION(n) (((n) & 0x1F) << 11)
#define JSON_EMBED 0x10000
typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data);
char *json_dumps(const json_t *json, size_t flags);
size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags);
int json_dumpf(const json_t *json, FILE *output, size_t flags);
int json_dumpfd(const json_t *json, int output, size_t flags);
int json_dump_file(const json_t *json, const char *path, size_t flags);
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags);
/* custom memory allocation */
typedef void *(*json_malloc_t)(size_t);
typedef void (*json_free_t)(void *);
void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn);
void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2010-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*
*
* This file specifies a part of the site-specific configuration for
* Jansson, namely those things that affect the public API in
* jansson.h.
*
* The configure script copies this file to jansson_config.h and
* replaces @var@ substitutions by values that fit your system. If you
* cannot run the configure script, you can do the value substitution
* by hand.
*/
#ifndef JANSSON_CONFIG_H
#define JANSSON_CONFIG_H
/* If your compiler supports the inline keyword in C, JSON_INLINE is
defined to `inline', otherwise empty. In C++, the inline is always
supported. */
#ifdef __cplusplus
#define JSON_INLINE inline
#else
#define JSON_INLINE inline
#endif
/* If your compiler supports the `long long` type and the strtoll()
library function, JSON_INTEGER_IS_LONG_LONG is defined to 1,
otherwise to 0. */
#define JSON_INTEGER_IS_LONG_LONG 1
/* If locale.h and localeconv() are available, define to 1,
otherwise to 0. */
#define JSON_HAVE_LOCALECONV 1
/* If __atomic builtins are available they will be used to manage
reference counts of json_t. */
#define JSON_HAVE_ATOMIC_BUILTINS 1
/* If __atomic builtins are not available we try using __sync builtins
to manage reference counts of json_t. */
#define JSON_HAVE_SYNC_BUILTINS 1
/* Maximum recursion depth for parsing JSON input.
This limits the depth of e.g. array-within-array constructions. */
#define JSON_PARSER_MAX_DEPTH 2048
#endif

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2010-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*
*
* This file specifies a part of the site-specific configuration for
* Jansson, namely those things that affect the public API in
* jansson.h.
*
* The configure script copies this file to jansson_config.h and
* replaces @var@ substitutions by values that fit your system. If you
* cannot run the configure script, you can do the value substitution
* by hand.
*/
#ifndef JANSSON_CONFIG_H
#define JANSSON_CONFIG_H
/* If your compiler supports the inline keyword in C, JSON_INLINE is
defined to `inline', otherwise empty. In C++, the inline is always
supported. */
#ifdef __cplusplus
#define JSON_INLINE inline
#else
#define JSON_INLINE @json_inline@
#endif
/* If your compiler supports the `long long` type and the strtoll()
library function, JSON_INTEGER_IS_LONG_LONG is defined to 1,
otherwise to 0. */
#define JSON_INTEGER_IS_LONG_LONG @json_have_long_long@
/* If locale.h and localeconv() are available, define to 1,
otherwise to 0. */
#define JSON_HAVE_LOCALECONV @json_have_localeconv@
/* If __atomic builtins are available they will be used to manage
reference counts of json_t. */
#define JSON_HAVE_ATOMIC_BUILTINS @json_have_atomic_builtins@
/* If __atomic builtins are not available we try using __sync builtins
to manage reference counts of json_t. */
#define JSON_HAVE_SYNC_BUILTINS @json_have_sync_builtins@
/* Maximum recursion depth for parsing JSON input.
This limits the depth of e.g. array-within-array constructions. */
#define JSON_PARSER_MAX_DEPTH 2048
#endif

View file

@ -0,0 +1,112 @@
/*
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#ifndef JANSSON_PRIVATE_H
#define JANSSON_PRIVATE_H
#ifdef HAVE_CONFIG_H
#include <jansson_private_config.h>
#endif
#include <stddef.h>
#include "jansson.h"
#include "hashtable.h"
#include "strbuffer.h"
#define container_of(ptr_, type_, member_) \
((type_ *)((char *)ptr_ - offsetof(type_, member_)))
/* On some platforms, max() may already be defined */
#ifndef max
#define max(a, b) ((a) > (b) ? (a) : (b))
#endif
/* va_copy is a C99 feature. In C89 implementations, it's sometimes
available as __va_copy. If not, memcpy() should do the trick. */
#ifndef va_copy
#ifdef __va_copy
#define va_copy __va_copy
#else
#define va_copy(a, b) memcpy(&(a), &(b), sizeof(va_list))
#endif
#endif
typedef struct {
json_t json;
hashtable_t hashtable;
} json_object_t;
typedef struct {
json_t json;
size_t size;
size_t entries;
json_t **table;
} json_array_t;
typedef struct {
json_t json;
char *value;
size_t length;
} json_string_t;
typedef struct {
json_t json;
double value;
} json_real_t;
typedef struct {
json_t json;
json_int_t value;
} json_integer_t;
#define json_to_object(json_) container_of(json_, json_object_t, json)
#define json_to_array(json_) container_of(json_, json_array_t, json)
#define json_to_string(json_) container_of(json_, json_string_t, json)
#define json_to_real(json_) container_of(json_, json_real_t, json)
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
/* Create a string by taking ownership of an existing buffer */
json_t *jsonp_stringn_nocheck_own(const char *value, size_t len);
/* Error message formatting */
void jsonp_error_init(json_error_t *error, const char *source);
void jsonp_error_set_source(json_error_t *error, const char *source);
void jsonp_error_set(json_error_t *error, int line, int column,
size_t position, enum json_error_code code,
const char *msg, ...);
void jsonp_error_vset(json_error_t *error, int line, int column,
size_t position, enum json_error_code code,
const char *msg, va_list ap);
/* Locale independent string<->double conversions */
int jsonp_strtod(strbuffer_t *strbuffer, double *out);
int jsonp_dtostr(char *buffer, size_t size, double value, int prec);
/* Wrappers for custom memory functions */
void* jsonp_malloc(size_t size);
void jsonp_free(void *ptr);
char *jsonp_strndup(const char *str, size_t length);
char *jsonp_strdup(const char *str);
char *jsonp_strndup(const char *str, size_t len);
/* Windows compatibility */
#if defined(_WIN32) || defined(WIN32)
# if defined(_MSC_VER) /* MS compiller */
# if (_MSC_VER < 1900) && !defined(snprintf) /* snprintf not defined yet & not introduced */
# define snprintf _snprintf
# endif
# if (_MSC_VER < 1500) && !defined(vsnprintf) /* vsnprintf not defined yet & not introduced */
# define vsnprintf(b,c,f,a) _vsnprintf(b,c,f,a)
# endif
# else /* Other Windows compiller, old definition */
# define snprintf _snprintf
# define vsnprintf _vsnprintf
# endif
#endif
#endif

1155
client/jansson/load.c Normal file

File diff suppressed because it is too large Load diff

381
client/jansson/lookup3.h Normal file
View file

@ -0,0 +1,381 @@
/*
-------------------------------------------------------------------------------
lookup3.c, by Bob Jenkins, May 2006, Public Domain.
These are functions for producing 32-bit hashes for hash table lookup.
hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
are externally useful functions. Routines to test the hash are included
if SELF_TEST is defined. You can use this free for any purpose. It's in
the public domain. It has no warranty.
You probably want to use hashlittle(). hashlittle() and hashbig()
hash byte arrays. hashlittle() is is faster than hashbig() on
little-endian machines. Intel and AMD are little-endian machines.
On second thought, you probably want hashlittle2(), which is identical to
hashlittle() except it returns two 32-bit hashes for the price of one.
You could implement hashbig2() if you wanted but I haven't bothered here.
If you want to find a hash of, say, exactly 7 integers, do
a = i1; b = i2; c = i3;
mix(a,b,c);
a += i4; b += i5; c += i6;
mix(a,b,c);
a += i7;
final(a,b,c);
then use c as the hash value. If you have a variable length array of
4-byte integers to hash, use hashword(). If you have a byte array (like
a character string), use hashlittle(). If you have several byte arrays, or
a mix of things, see the comments above hashlittle().
Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
then mix those integers. This is fast (you can do a lot more thorough
mixing with 12*3 instructions on 3 integers than you can with 3 instructions
on 1 byte), but shoehorning those bytes into integers efficiently is messy.
-------------------------------------------------------------------------------
*/
#include <stdlib.h>
#ifdef HAVE_CONFIG_H
#include <jansson_private_config.h>
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h> /* defines uint32_t etc */
#endif
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h> /* attempt to define endianness */
#endif
#ifdef HAVE_ENDIAN_H
# include <endian.h> /* attempt to define endianness */
#endif
/*
* My best guess at if you are big-endian or little-endian. This may
* need adjustment.
*/
#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
__BYTE_ORDER == __LITTLE_ENDIAN) || \
(defined(i386) || defined(__i386__) || defined(__i486__) || \
defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL))
# define HASH_LITTLE_ENDIAN 1
# define HASH_BIG_ENDIAN 0
#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \
__BYTE_ORDER == __BIG_ENDIAN) || \
(defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel))
# define HASH_LITTLE_ENDIAN 0
# define HASH_BIG_ENDIAN 1
#else
# define HASH_LITTLE_ENDIAN 0
# define HASH_BIG_ENDIAN 0
#endif
#define hashsize(n) ((uint32_t)1<<(n))
#define hashmask(n) (hashsize(n)-1)
#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
/*
-------------------------------------------------------------------------------
mix -- mix 3 32-bit values reversibly.
This is reversible, so any information in (a,b,c) before mix() is
still in (a,b,c) after mix().
If four pairs of (a,b,c) inputs are run through mix(), or through
mix() in reverse, there are at least 32 bits of the output that
are sometimes the same for one pair and different for another pair.
This was tested for:
* pairs that differed by one bit, by two bits, in any combination
of top bits of (a,b,c), or in any combination of bottom bits of
(a,b,c).
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
is commonly produced by subtraction) look like a single 1-bit
difference.
* the base values were pseudorandom, all zero but one bit set, or
all zero plus a counter that starts at zero.
Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
satisfy this are
4 6 8 16 19 4
9 15 3 18 27 15
14 9 3 7 17 3
Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
for "differ" defined as + with a one-bit base and a two-bit delta. I
used http://burtleburtle.net/bob/hash/avalanche.html to choose
the operations, constants, and arrangements of the variables.
This does not achieve avalanche. There are input bits of (a,b,c)
that fail to affect some output bits of (a,b,c), especially of a. The
most thoroughly mixed value is c, but it doesn't really even achieve
avalanche in c.
This allows some parallelism. Read-after-writes are good at doubling
the number of bits affected, so the goal of mixing pulls in the opposite
direction as the goal of parallelism. I did what I could. Rotates
seem to cost as much as shifts on every machine I could lay my hands
on, and rotates are much kinder to the top and bottom bits, so I used
rotates.
-------------------------------------------------------------------------------
*/
#define mix(a,b,c) \
{ \
a -= c; a ^= rot(c, 4); c += b; \
b -= a; b ^= rot(a, 6); a += c; \
c -= b; c ^= rot(b, 8); b += a; \
a -= c; a ^= rot(c,16); c += b; \
b -= a; b ^= rot(a,19); a += c; \
c -= b; c ^= rot(b, 4); b += a; \
}
/*
-------------------------------------------------------------------------------
final -- final mixing of 3 32-bit values (a,b,c) into c
Pairs of (a,b,c) values differing in only a few bits will usually
produce values of c that look totally different. This was tested for
* pairs that differed by one bit, by two bits, in any combination
of top bits of (a,b,c), or in any combination of bottom bits of
(a,b,c).
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
is commonly produced by subtraction) look like a single 1-bit
difference.
* the base values were pseudorandom, all zero but one bit set, or
all zero plus a counter that starts at zero.
These constants passed:
14 11 25 16 4 14 24
12 14 25 16 4 14 24
and these came close:
4 8 15 26 3 22 24
10 8 15 26 3 22 24
11 8 15 26 3 22 24
-------------------------------------------------------------------------------
*/
#define final(a,b,c) \
{ \
c ^= b; c -= rot(b,14); \
a ^= c; a -= rot(c,11); \
b ^= a; b -= rot(a,25); \
c ^= b; c -= rot(b,16); \
a ^= c; a -= rot(c,4); \
b ^= a; b -= rot(a,14); \
c ^= b; c -= rot(b,24); \
}
/*
-------------------------------------------------------------------------------
hashlittle() -- hash a variable-length key into a 32-bit value
k : the key (the unaligned variable-length array of bytes)
length : the length of the key, counting by bytes
initval : can be any 4-byte value
Returns a 32-bit value. Every bit of the key affects every bit of
the return value. Two keys differing by one or two bits will have
totally different hash values.
The best hash table sizes are powers of 2. There is no need to do
mod a prime (mod is sooo slow!). If you need less than 32 bits,
use a bitmask. For example, if you need only 10 bits, do
h = (h & hashmask(10));
In which case, the hash table should have hashsize(10) elements.
If you are hashing n strings (uint8_t **)k, do it like this:
for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h);
By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this
code any way you wish, private, educational, or commercial. It's free.
Use for hash table lookup, or anything where one collision in 2^^32 is
acceptable. Do NOT use for cryptographic purposes.
-------------------------------------------------------------------------------
*/
static uint32_t hashlittle(const void *key, size_t length, uint32_t initval)
{
uint32_t a,b,c; /* internal state */
union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
/* Set up the internal state */
a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;
u.ptr = key;
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
/* Detect Valgrind or AddressSanitizer */
#ifdef VALGRIND
# define NO_MASKING_TRICK 1
#else
# if defined(__has_feature) /* Clang */
# if __has_feature(address_sanitizer) /* is ASAN enabled? */
# define NO_MASKING_TRICK 1
# endif
# else
# if defined(__SANITIZE_ADDRESS__) /* GCC 4.8.x, is ASAN enabled? */
# define NO_MASKING_TRICK 1
# endif
# endif
#endif
#ifdef NO_MASKING_TRICK
const uint8_t *k8;
#endif
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
while (length > 12)
{
a += k[0];
b += k[1];
c += k[2];
mix(a,b,c);
length -= 12;
k += 3;
}
/*----------------------------- handle the last (probably partial) block */
/*
* "k[2]&0xffffff" actually reads beyond the end of the string, but
* then masks off the part it's not allowed to read. Because the
* string is aligned, the masked-off tail is in the same word as the
* rest of the string. Every machine with memory protection I've seen
* does it on word boundaries, so is OK with this. But VALGRIND will
* still catch it and complain. The masking trick does make the hash
* noticably faster for short strings (like English words).
*/
#ifndef NO_MASKING_TRICK
switch(length)
{
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
case 8 : b+=k[1]; a+=k[0]; break;
case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
case 6 : b+=k[1]&0xffff; a+=k[0]; break;
case 5 : b+=k[1]&0xff; a+=k[0]; break;
case 4 : a+=k[0]; break;
case 3 : a+=k[0]&0xffffff; break;
case 2 : a+=k[0]&0xffff; break;
case 1 : a+=k[0]&0xff; break;
case 0 : return c; /* zero length strings require no mixing */
}
#else /* make valgrind happy */
k8 = (const uint8_t *)k;
switch(length)
{
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
case 9 : c+=k8[8]; /* fall through */
case 8 : b+=k[1]; a+=k[0]; break;
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
case 5 : b+=k8[4]; /* fall through */
case 4 : a+=k[0]; break;
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
case 1 : a+=k8[0]; break;
case 0 : return c;
}
#endif /* !valgrind */
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
const uint8_t *k8;
/*--------------- all but last block: aligned reads and different mixing */
while (length > 12)
{
a += k[0] + (((uint32_t)k[1])<<16);
b += k[2] + (((uint32_t)k[3])<<16);
c += k[4] + (((uint32_t)k[5])<<16);
mix(a,b,c);
length -= 12;
k += 6;
}
/*----------------------------- handle the last (probably partial) block */
k8 = (const uint8_t *)k;
switch(length)
{
case 12: c+=k[4]+(((uint32_t)k[5])<<16);
b+=k[2]+(((uint32_t)k[3])<<16);
a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
case 10: c+=k[4];
b+=k[2]+(((uint32_t)k[3])<<16);
a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 9 : c+=k8[8]; /* fall through */
case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
case 6 : b+=k[2];
a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 5 : b+=k8[4]; /* fall through */
case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
case 2 : a+=k[0];
break;
case 1 : a+=k8[0];
break;
case 0 : return c; /* zero length requires no mixing */
}
} else { /* need to read the key one byte at a time */
const uint8_t *k = (const uint8_t *)key;
/*--------------- all but the last block: affect some 32 bits of (a,b,c) */
while (length > 12)
{
a += k[0];
a += ((uint32_t)k[1])<<8;
a += ((uint32_t)k[2])<<16;
a += ((uint32_t)k[3])<<24;
b += k[4];
b += ((uint32_t)k[5])<<8;
b += ((uint32_t)k[6])<<16;
b += ((uint32_t)k[7])<<24;
c += k[8];
c += ((uint32_t)k[9])<<8;
c += ((uint32_t)k[10])<<16;
c += ((uint32_t)k[11])<<24;
mix(a,b,c);
length -= 12;
k += 12;
}
/*-------------------------------- last block: affect all 32 bits of (c) */
switch(length) /* all the case statements fall through */
{
case 12: c+=((uint32_t)k[11])<<24; /* fall through */
case 11: c+=((uint32_t)k[10])<<16; /* fall through */
case 10: c+=((uint32_t)k[9])<<8; /* fall through */
case 9 : c+=k[8]; /* fall through */
case 8 : b+=((uint32_t)k[7])<<24; /* fall through */
case 7 : b+=((uint32_t)k[6])<<16; /* fall through */
case 6 : b+=((uint32_t)k[5])<<8; /* fall through */
case 5 : b+=k[4]; /* fall through */
case 4 : a+=((uint32_t)k[3])<<24; /* fall through */
case 3 : a+=((uint32_t)k[2])<<16; /* fall through */
case 2 : a+=((uint32_t)k[1])<<8; /* fall through */
case 1 : a+=k[0];
break;
case 0 : return c;
}
}
final(a,b,c);
return c;
}

69
client/jansson/memory.c Normal file
View file

@ -0,0 +1,69 @@
/*
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2011-2012 Basile Starynkevitch <basile@starynkevitch.net>
*
* Jansson is free software; you can redistribute it and/or modify it
* under the terms of the MIT license. See LICENSE for details.
*/
#include <stdlib.h>
#include <string.h>
#include "jansson.h"
#include "jansson_private.h"
/* C89 allows these to be macros */
#undef malloc
#undef free
/* memory function pointers */
static json_malloc_t do_malloc = malloc;
static json_free_t do_free = free;
void *jsonp_malloc(size_t size)
{
if(!size)
return NULL;
return (*do_malloc)(size);
}
void jsonp_free(void *ptr)
{
if(!ptr)
return;
(*do_free)(ptr);
}
char *jsonp_strdup(const char *str)
{
return jsonp_strndup(str, strlen(str));
}
char *jsonp_strndup(const char *str, size_t len)
{
char *new_str;
new_str = jsonp_malloc(len + 1);
if(!new_str)
return NULL;
memcpy(new_str, str, len);
new_str[len] = '\0';
return new_str;
}
void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn)
{
do_malloc = malloc_fn;
do_free = free_fn;
}
void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn)
{
if (malloc_fn)
*malloc_fn = do_malloc;
if (free_fn)
*free_fn = do_free;
}

View file

@ -0,0 +1,909 @@
/*
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
* Copyright (c) 2011-2012 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <string.h>
#include "jansson.h"
#include "jansson_private.h"
#include "utf.h"
typedef struct {
int line;
int column;
size_t pos;
char token;
} token_t;
typedef struct {
const char *start;
const char *fmt;
token_t prev_token;
token_t token;
token_t next_token;
json_error_t *error;
size_t flags;
int line;
int column;
size_t pos;
int has_error;
} scanner_t;
#define token(scanner) ((scanner)->token.token)
static const char * const type_names[] = {
"object",
"array",
"string",
"integer",
"real",
"true",
"false",
"null"
};
#define type_name(x) type_names[json_typeof(x)]
static const char unpack_value_starters[] = "{[siIbfFOon";
static void scanner_init(scanner_t *s, json_error_t *error,
size_t flags, const char *fmt)
{
s->error = error;
s->flags = flags;
s->fmt = s->start = fmt;
memset(&s->prev_token, 0, sizeof(token_t));
memset(&s->token, 0, sizeof(token_t));
memset(&s->next_token, 0, sizeof(token_t));
s->line = 1;
s->column = 0;
s->pos = 0;
s->has_error = 0;
}
static void next_token(scanner_t *s)
{
const char *t;
s->prev_token = s->token;
if(s->next_token.line) {
s->token = s->next_token;
s->next_token.line = 0;
return;
}
t = s->fmt;
s->column++;
s->pos++;
/* skip space and ignored chars */
while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') {
if(*t == '\n') {
s->line++;
s->column = 1;
}
else
s->column++;
s->pos++;
t++;
}
s->token.token = *t;
s->token.line = s->line;
s->token.column = s->column;
s->token.pos = s->pos;
t++;
s->fmt = t;
}
static void prev_token(scanner_t *s)
{
s->next_token = s->token;
s->token = s->prev_token;
}
static void set_error(scanner_t *s, const char *source, enum json_error_code code,
const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
jsonp_error_vset(s->error, s->token.line, s->token.column, s->token.pos,
code, fmt, ap);
jsonp_error_set_source(s->error, source);
va_end(ap);
}
static json_t *pack(scanner_t *s, va_list *ap);
/* ours will be set to 1 if jsonp_free() must be called for the result
afterwards */
static char *read_string(scanner_t *s, va_list *ap,
const char *purpose, size_t *out_len, int *ours)
{
char t;
strbuffer_t strbuff;
const char *str;
size_t length;
next_token(s);
t = token(s);
prev_token(s);
*ours = 0;
if(t != '#' && t != '%' && t != '+') {
/* Optimize the simple case */
str = va_arg(*ap, const char *);
if(!str) {
set_error(s, "<args>", json_error_null_value, "NULL string argument");
return NULL;
}
length = strlen(str);
if(!utf8_check_string(str, length)) {
set_error(s, "<args>", json_error_invalid_utf8, "Invalid UTF-8 %s", purpose);
return NULL;
}
*out_len = length;
return (char *)str;
}
strbuffer_init(&strbuff);
while(1) {
str = va_arg(*ap, const char *);
if(!str) {
set_error(s, "<args>", json_error_null_value, "NULL string argument");
s->has_error = 1;
}
next_token(s);
if(token(s) == '#') {
length = va_arg(*ap, int);
}
else if(token(s) == '%') {
length = va_arg(*ap, size_t);
}
else {
prev_token(s);
length = s->has_error ? 0 : strlen(str);
}
if(!s->has_error && strbuffer_append_bytes(&strbuff, str, length) == -1) {
set_error(s, "<internal>", json_error_out_of_memory, "Out of memory");
s->has_error = 1;
}
next_token(s);
if(token(s) != '+') {
prev_token(s);
break;
}
}
if(s->has_error) {
strbuffer_close(&strbuff);
return NULL;
}
if(!utf8_check_string(strbuff.value, strbuff.length)) {
set_error(s, "<args>", json_error_invalid_utf8, "Invalid UTF-8 %s", purpose);
strbuffer_close(&strbuff);
s->has_error = 1;
return NULL;
}
*out_len = strbuff.length;
*ours = 1;
return strbuffer_steal_value(&strbuff);
}
static json_t *pack_object(scanner_t *s, va_list *ap)
{
json_t *object = json_object();
next_token(s);
while(token(s) != '}') {
char *key;
size_t len;
int ours;
json_t *value;
if(!token(s)) {
set_error(s, "<format>", json_error_invalid_format, "Unexpected end of format string");
goto error;
}
if(token(s) != 's') {
set_error(s, "<format>", json_error_invalid_format, "Expected format 's', got '%c'", token(s));
goto error;
}
key = read_string(s, ap, "object key", &len, &ours);
if (!key)
s->has_error = 1;
next_token(s);
value = pack(s, ap);
if(!value) {
if(ours)
jsonp_free(key);
if(strchr("soO", token(s)) && s->next_token.token == '*') {
next_token(s);
} else {
s->has_error = 1;
}
next_token(s);
continue;
}
if(s->has_error)
json_decref(value);
if(!s->has_error && json_object_set_new_nocheck(object, key, value)) {
set_error(s, "<internal>", json_error_out_of_memory, "Unable to add key \"%s\"", key);
s->has_error = 1;
}
if(ours)
jsonp_free(key);
if(strchr("soO", token(s)) && s->next_token.token == '*')
next_token(s);
next_token(s);
}
if(!s->has_error)
return object;
error:
json_decref(object);
return NULL;
}
static json_t *pack_array(scanner_t *s, va_list *ap)
{
json_t *array = json_array();
next_token(s);
while(token(s) != ']') {
json_t *value;
if(!token(s)) {
set_error(s, "<format>", json_error_invalid_format, "Unexpected end of format string");
/* Format string errors are unrecoverable. */
goto error;
}
value = pack(s, ap);
if(!value) {
if(strchr("soO", token(s)) && s->next_token.token == '*') {
next_token(s);
} else {
s->has_error = 1;
}
next_token(s);
continue;
}
if(s->has_error)
json_decref(value);
if(!s->has_error && json_array_append_new(array, value)) {
set_error(s, "<internal>", json_error_out_of_memory, "Unable to append to array");
s->has_error = 1;
}
if(strchr("soO", token(s)) && s->next_token.token == '*')
next_token(s);
next_token(s);
}
if(!s->has_error)
return array;
error:
json_decref(array);
return NULL;
}
static json_t *pack_string(scanner_t *s, va_list *ap)
{
char *str;
size_t len;
int ours;
int nullable;
next_token(s);
nullable = token(s) == '?';
if (!nullable)
prev_token(s);
str = read_string(s, ap, "string", &len, &ours);
if (!str) {
return nullable ? json_null() : NULL;
} else if (ours) {
return jsonp_stringn_nocheck_own(str, len);
} else {
return json_stringn_nocheck(str, len);
}
}
static json_t *pack(scanner_t *s, va_list *ap)
{
switch(token(s)) {
case '{':
return pack_object(s, ap);
case '[':
return pack_array(s, ap);
case 's': /* string */
return pack_string(s, ap);
case 'n': /* null */
return json_null();
case 'b': /* boolean */
return va_arg(*ap, int) ? json_true() : json_false();
case 'i': /* integer from int */
return json_integer(va_arg(*ap, int));
case 'I': /* integer from json_int_t */
return json_integer(va_arg(*ap, json_int_t));
case 'f': /* real */
return json_real(va_arg(*ap, double));
case 'O': /* a json_t object; increments refcount */
{
int nullable;
json_t *json;
next_token(s);
nullable = token(s) == '?';
if (!nullable)
prev_token(s);
json = va_arg(*ap, json_t *);
if (!json && nullable) {
return json_null();
} else {
return json_incref(json);
}
}
case 'o': /* a json_t object; doesn't increment refcount */
{
int nullable;
json_t *json;
next_token(s);
nullable = token(s) == '?';
if (!nullable)
prev_token(s);
json = va_arg(*ap, json_t *);
if (!json && nullable) {
return json_null();
} else {
return json;
}
}
default:
set_error(s, "<format>", json_error_invalid_format, "Unexpected format character '%c'",
token(s));
s->has_error = 1;
return NULL;
}
}
static int unpack(scanner_t *s, json_t *root, va_list *ap);
static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
{
int ret = -1;
int strict = 0;
int gotopt = 0;
/* Use a set (emulated by a hashtable) to check that all object
keys are accessed. Checking that the correct number of keys
were accessed is not enough, as the same key can be unpacked
multiple times.
*/
hashtable_t key_set;
if(hashtable_init(&key_set)) {
set_error(s, "<internal>", json_error_out_of_memory, "Out of memory");
return -1;
}
if(root && !json_is_object(root)) {
set_error(s, "<validation>", json_error_wrong_type, "Expected object, got %s",
type_name(root));
goto out;
}
next_token(s);
while(token(s) != '}') {
const char *key;
json_t *value;
int opt = 0;
if(strict != 0) {
set_error(s, "<format>", json_error_invalid_format, "Expected '}' after '%c', got '%c'",
(strict == 1 ? '!' : '*'), token(s));
goto out;
}
if(!token(s)) {
set_error(s, "<format>", json_error_invalid_format, "Unexpected end of format string");
goto out;
}
if(token(s) == '!' || token(s) == '*') {
strict = (token(s) == '!' ? 1 : -1);
next_token(s);
continue;
}
if(token(s) != 's') {
set_error(s, "<format>", json_error_invalid_format, "Expected format 's', got '%c'", token(s));
goto out;
}
key = va_arg(*ap, const char *);
if(!key) {
set_error(s, "<args>", json_error_null_value, "NULL object key");
goto out;
}
next_token(s);
if(token(s) == '?') {
opt = gotopt = 1;
next_token(s);
}
if(!root) {
/* skipping */
value = NULL;
}
else {
value = json_object_get(root, key);
if(!value && !opt) {
set_error(s, "<validation>", json_error_item_not_found, "Object item not found: %s", key);
goto out;
}
}
if(unpack(s, value, ap))
goto out;
hashtable_set(&key_set, key, json_null());
next_token(s);
}
if(strict == 0 && (s->flags & JSON_STRICT))
strict = 1;
if(root && strict == 1) {
/* We need to check that all non optional items have been parsed */
const char *key;
int have_unrecognized_keys = 0;
strbuffer_t unrecognized_keys;
json_t *value;
long unpacked = 0;
if (gotopt) {
/* We have optional keys, we need to iter on each key */
json_object_foreach(root, key, value) {
if(!hashtable_get(&key_set, key)) {
unpacked++;
/* Save unrecognized keys for the error message */
if (!have_unrecognized_keys) {
strbuffer_init(&unrecognized_keys);
have_unrecognized_keys = 1;
} else {
strbuffer_append_bytes(&unrecognized_keys, ", ", 2);
}
strbuffer_append_bytes(&unrecognized_keys, key, strlen(key));
}
}
} else {
/* No optional keys, we can just compare the number of items */
unpacked = (long)json_object_size(root) - (long)key_set.size;
}
if (unpacked) {
if (!gotopt) {
/* Save unrecognized keys for the error message */
json_object_foreach(root, key, value) {
if(!hashtable_get(&key_set, key)) {
if (!have_unrecognized_keys) {
strbuffer_init(&unrecognized_keys);
have_unrecognized_keys = 1;
} else {
strbuffer_append_bytes(&unrecognized_keys, ", ", 2);
}
strbuffer_append_bytes(&unrecognized_keys, key, strlen(key));
}
}
}
set_error(s, "<validation>", json_error_end_of_input_expected,
"%li object item(s) left unpacked: %s",
unpacked, strbuffer_value(&unrecognized_keys));
strbuffer_close(&unrecognized_keys);
goto out;
}
}
ret = 0;
out:
hashtable_close(&key_set);
return ret;
}
static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
{
size_t i = 0;
int strict = 0;
if(root && !json_is_array(root)) {
set_error(s, "<validation>", json_error_wrong_type, "Expected array, got %s", type_name(root));
return -1;
}
next_token(s);
while(token(s) != ']') {
json_t *value;
if(strict != 0) {
set_error(s, "<format>", json_error_invalid_format, "Expected ']' after '%c', got '%c'",
(strict == 1 ? '!' : '*'),
token(s));
return -1;
}
if(!token(s)) {
set_error(s, "<format>", json_error_invalid_format, "Unexpected end of format string");
return -1;
}
if(token(s) == '!' || token(s) == '*') {
strict = (token(s) == '!' ? 1 : -1);
next_token(s);
continue;
}
if(!strchr(unpack_value_starters, token(s))) {
set_error(s, "<format>", json_error_invalid_format, "Unexpected format character '%c'",
token(s));
return -1;
}
if(!root) {
/* skipping */
value = NULL;
}
else {
value = json_array_get(root, i);
if(!value) {
set_error(s, "<validation>", json_error_index_out_of_range, "Array index %lu out of range",
(unsigned long)i);
return -1;
}
}
if(unpack(s, value, ap))
return -1;
next_token(s);
i++;
}
if(strict == 0 && (s->flags & JSON_STRICT))
strict = 1;
if(root && strict == 1 && i != json_array_size(root)) {
long diff = (long)json_array_size(root) - (long)i;
set_error(s, "<validation>", json_error_end_of_input_expected, "%li array item(s) left unpacked", diff);
return -1;
}
return 0;
}
static int unpack(scanner_t *s, json_t *root, va_list *ap)
{
switch(token(s))
{
case '{':
return unpack_object(s, root, ap);
case '[':
return unpack_array(s, root, ap);
case 's':
if(root && !json_is_string(root)) {
set_error(s, "<validation>", json_error_wrong_type, "Expected string, got %s",
type_name(root));
return -1;
}
if(!(s->flags & JSON_VALIDATE_ONLY)) {
const char **str_target;
size_t *len_target = NULL;
str_target = va_arg(*ap, const char **);
if(!str_target) {
set_error(s, "<args>", json_error_null_value, "NULL string argument");
return -1;
}
next_token(s);
if(token(s) == '%') {
len_target = va_arg(*ap, size_t *);
if(!len_target) {
set_error(s, "<args>", json_error_null_value, "NULL string length argument");
return -1;
}
}
else
prev_token(s);
if(root) {
*str_target = json_string_value(root);
if(len_target)
*len_target = json_string_length(root);
}
}
return 0;
case 'i':
if(root && !json_is_integer(root)) {
set_error(s, "<validation>", json_error_wrong_type, "Expected integer, got %s",
type_name(root));
return -1;
}
if(!(s->flags & JSON_VALIDATE_ONLY)) {
int *target = va_arg(*ap, int*);
if(root)
*target = (int)json_integer_value(root);
}
return 0;
case 'I':
if(root && !json_is_integer(root)) {
set_error(s, "<validation>", json_error_wrong_type, "Expected integer, got %s",
type_name(root));
return -1;
}
if(!(s->flags & JSON_VALIDATE_ONLY)) {
json_int_t *target = va_arg(*ap, json_int_t*);
if(root)
*target = json_integer_value(root);
}
return 0;
case 'b':
if(root && !json_is_boolean(root)) {
set_error(s, "<validation>", json_error_wrong_type, "Expected true or false, got %s",
type_name(root));
return -1;
}
if(!(s->flags & JSON_VALIDATE_ONLY)) {
int *target = va_arg(*ap, int*);
if(root)
*target = json_is_true(root);
}
return 0;
case 'f':
if(root && !json_is_real(root)) {
set_error(s, "<validation>", json_error_wrong_type, "Expected real, got %s",
type_name(root));
return -1;
}
if(!(s->flags & JSON_VALIDATE_ONLY)) {
double *target = va_arg(*ap, double*);
if(root)
*target = json_real_value(root);
}
return 0;
case 'F':
if(root && !json_is_number(root)) {
set_error(s, "<validation>", json_error_wrong_type, "Expected real or integer, got %s",
type_name(root));
return -1;
}
if(!(s->flags & JSON_VALIDATE_ONLY)) {
double *target = va_arg(*ap, double*);
if(root)
*target = json_number_value(root);
}
return 0;
case 'O':
if(root && !(s->flags & JSON_VALIDATE_ONLY))
json_incref(root);
/* Fall through */
case 'o':
if(!(s->flags & JSON_VALIDATE_ONLY)) {
json_t **target = va_arg(*ap, json_t**);
if(root)
*target = root;
}
return 0;
case 'n':
/* Never assign, just validate */
if(root && !json_is_null(root)) {
set_error(s, "<validation>", json_error_wrong_type, "Expected null, got %s",
type_name(root));
return -1;
}
return 0;
default:
set_error(s, "<format>", json_error_invalid_format, "Unexpected format character '%c'",
token(s));
return -1;
}
}
json_t *json_vpack_ex(json_error_t *error, size_t flags,
const char *fmt, va_list ap)
{
scanner_t s;
va_list ap_copy;
json_t *value;
if(!fmt || !*fmt) {
jsonp_error_init(error, "<format>");
jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, "NULL or empty format string");
return NULL;
}
jsonp_error_init(error, NULL);
scanner_init(&s, error, flags, fmt);
next_token(&s);
va_copy(ap_copy, ap);
value = pack(&s, &ap_copy);
va_end(ap_copy);
if(!value)
return NULL;
next_token(&s);
if(token(&s)) {
json_decref(value);
set_error(&s, "<format>", json_error_invalid_format, "Garbage after format string");
return NULL;
}
if(s.has_error) {
json_decref(value);
return NULL;
}
return value;
}
json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...)
{
json_t *value;
va_list ap;
va_start(ap, fmt);
value = json_vpack_ex(error, flags, fmt, ap);
va_end(ap);
return value;
}
json_t *json_pack(const char *fmt, ...)
{
json_t *value;
va_list ap;
va_start(ap, fmt);
value = json_vpack_ex(NULL, 0, fmt, ap);
va_end(ap);
return value;
}
int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags,
const char *fmt, va_list ap)
{
scanner_t s;
va_list ap_copy;
if(!root) {
jsonp_error_init(error, "<root>");
jsonp_error_set(error, -1, -1, 0, json_error_null_value, "NULL root value");
return -1;
}
if(!fmt || !*fmt) {
jsonp_error_init(error, "<format>");
jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, "NULL or empty format string");
return -1;
}
jsonp_error_init(error, NULL);
scanner_init(&s, error, flags, fmt);
next_token(&s);
va_copy(ap_copy, ap);
if(unpack(&s, root, &ap_copy)) {
va_end(ap_copy);
return -1;
}
va_end(ap_copy);
next_token(&s);
if(token(&s)) {
set_error(&s, "<format>", json_error_invalid_format, "Garbage after format string");
return -1;
}
return 0;
}
int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...)
{
int ret;
va_list ap;
va_start(ap, fmt);
ret = json_vunpack_ex(root, error, flags, fmt, ap);
va_end(ap);
return ret;
}
int json_unpack(json_t *root, const char *fmt, ...)
{
int ret;
va_list ap;
va_start(ap, fmt);
ret = json_vunpack_ex(root, NULL, 0, fmt, ap);
va_end(ap);
return ret;
}

201
client/jansson/path.c Normal file
View file

@ -0,0 +1,201 @@
/*
* Copyright (c) 2012 Rogerz Zhang <rogerz.zhang@gmail.com>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*
* source here https://github.com/rogerz/jansson/blob/json_path/src/path.c
*/
#include <string.h>
#include <assert.h>
#include <jansson.h>
#include "jansson_private.h"
json_t *json_path_get(const json_t *json, const char *path)
{
static const char root_chr = '$', array_open = '[';
static const char *path_delims = ".[", *array_close = "]";
const json_t *cursor;
char *token, *buf, *peek, *endptr, delim = '\0';
const char *expect;
if (!json || !path || path[0] != root_chr)
return NULL;
else
buf = jsonp_strdup(path);
peek = buf + 1;
cursor = json;
token = NULL;
expect = path_delims;
while (peek && *peek && cursor)
{
char *last_peek = peek;
peek = strpbrk(peek, expect);
if (peek) {
if (!token && peek != last_peek)
goto fail;
delim = *peek;
*peek++ = '\0';
} else if (expect != path_delims || !token) {
goto fail;
}
if (expect == path_delims) {
if (token) {
cursor = json_object_get(cursor, token);
}
expect = (delim == array_open ? array_close : path_delims);
token = peek;
} else if (expect == array_close) {
size_t index = strtol(token, &endptr, 0);
if (*endptr)
goto fail;
cursor = json_array_get(cursor, index);
token = NULL;
expect = path_delims;
} else {
goto fail;
}
}
jsonp_free(buf);
return (json_t *)cursor;
fail:
jsonp_free(buf);
return NULL;
}
int json_path_set_new(json_t *json, const char *path, json_t *value, size_t flags, json_error_t *error)
{
static const char root_chr = '$', array_open = '[', object_delim = '.';
static const char * const path_delims = ".[", *array_close = "]";
json_t *cursor, *parent = NULL;
char *token, *buf = NULL, *peek, delim = '\0';
const char *expect;
int index_saved = -1;
jsonp_error_init(error, "<path>");
if (!json || !path || flags || !value) {
jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, "invalid argument");
goto fail;
} else {
buf = jsonp_strdup(path);
}
if (buf[0] != root_chr) {
jsonp_error_set(error, -1, -1, 0, json_error_invalid_format, "path should start with $");
goto fail;
}
peek = buf + 1;
cursor = json;
token = NULL;
expect = path_delims;
while (peek && *peek && cursor)
{
char *last_peek = peek;
peek = strpbrk(last_peek, expect);
if (peek) {
if (!token && peek != last_peek) {
jsonp_error_set(error, -1, -1, last_peek - buf, json_error_invalid_format, "unexpected trailing chars");
goto fail;
}
delim = *peek;
*peek++ = '\0';
} else { // end of path
if (expect == path_delims) {
break;
} else {
jsonp_error_set(error, -1, -1, peek - buf, json_error_invalid_format, "missing ']'?");
goto fail;
}
}
if (expect == path_delims) {
if (token) {
if (token[0] == '\0') {
jsonp_error_set(error, -1, -1, peek - buf, json_error_invalid_format, "empty token");
goto fail;
}
parent = cursor;
cursor = json_object_get(parent, token);
if (!cursor) {
if (!json_is_object(parent)) {
jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "object expected");
goto fail;
}
if (delim == object_delim) {
cursor = json_object();
json_object_set_new(parent, token, cursor);
} else {
jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "new array is not allowed");
goto fail;
}
}
}
expect = (delim == array_open ? array_close : path_delims);
token = peek;
} else if (expect == array_close) {
char *endptr;
size_t index;
parent = cursor;
if (!json_is_array(parent)) {
jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "array expected");
goto fail;
}
index = strtol(token, &endptr, 0);
if (*endptr) {
jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "invalid array index");
goto fail;
}
cursor = json_array_get(parent, index);
if (!cursor) {
jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "array index out of bound");
goto fail;
}
index_saved = index;
token = NULL;
expect = path_delims;
} else {
assert(1);
jsonp_error_set(error, -1, -1, peek - buf, json_error_unknown, "unexpected error in path move");
goto fail;
}
}
if (token) {
if (json_is_object(cursor)) {
json_object_set(cursor, token, value);
} else {
jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "object expected");
goto fail;
}
cursor = json_object_get(cursor, token);
} else if (index_saved != -1 && json_is_array(parent)) {
json_array_set(parent, index_saved, value);
cursor = json_array_get(parent, index_saved);
} else {
jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "invalid path");
goto fail;
}
json_decref(value);
jsonp_free(buf);
return 0;
fail:
json_decref(value);
jsonp_free(buf);
return -1;
}

111
client/jansson/strbuffer.c Normal file
View file

@ -0,0 +1,111 @@
/*
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdlib.h>
#include <string.h>
#include "jansson_private.h"
#include "strbuffer.h"
#define STRBUFFER_MIN_SIZE 16
#define STRBUFFER_FACTOR 2
#define STRBUFFER_SIZE_MAX ((size_t)-1)
int strbuffer_init(strbuffer_t *strbuff)
{
strbuff->size = STRBUFFER_MIN_SIZE;
strbuff->length = 0;
strbuff->value = jsonp_malloc(strbuff->size);
if(!strbuff->value)
return -1;
/* initialize to empty */
strbuff->value[0] = '\0';
return 0;
}
void strbuffer_close(strbuffer_t *strbuff)
{
if(strbuff->value)
jsonp_free(strbuff->value);
strbuff->size = 0;
strbuff->length = 0;
strbuff->value = NULL;
}
void strbuffer_clear(strbuffer_t *strbuff)
{
strbuff->length = 0;
strbuff->value[0] = '\0';
}
const char *strbuffer_value(const strbuffer_t *strbuff)
{
return strbuff->value;
}
char *strbuffer_steal_value(strbuffer_t *strbuff)
{
char *result = strbuff->value;
strbuff->value = NULL;
return result;
}
int strbuffer_append_byte(strbuffer_t *strbuff, char byte)
{
return strbuffer_append_bytes(strbuff, &byte, 1);
}
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size)
{
if(size >= strbuff->size - strbuff->length)
{
size_t new_size;
char *new_value;
/* avoid integer overflow */
if (strbuff->size > STRBUFFER_SIZE_MAX / STRBUFFER_FACTOR
|| size > STRBUFFER_SIZE_MAX - 1
|| strbuff->length > STRBUFFER_SIZE_MAX - 1 - size)
return -1;
new_size = max(strbuff->size * STRBUFFER_FACTOR,
strbuff->length + size + 1);
new_value = jsonp_malloc(new_size);
if(!new_value)
return -1;
memcpy(new_value, strbuff->value, strbuff->length);
jsonp_free(strbuff->value);
strbuff->value = new_value;
strbuff->size = new_size;
}
memcpy(strbuff->value + strbuff->length, data, size);
strbuff->length += size;
strbuff->value[strbuff->length] = '\0';
return 0;
}
char strbuffer_pop(strbuffer_t *strbuff)
{
if(strbuff->length > 0) {
char c = strbuff->value[--strbuff->length];
strbuff->value[strbuff->length] = '\0';
return c;
}
else
return '\0';
}

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#ifndef STRBUFFER_H
#define STRBUFFER_H
#include <stdlib.h>
typedef struct {
char *value;
size_t length; /* bytes used */
size_t size; /* bytes allocated */
} strbuffer_t;
int strbuffer_init(strbuffer_t *strbuff);
void strbuffer_close(strbuffer_t *strbuff);
void strbuffer_clear(strbuffer_t *strbuff);
const char *strbuffer_value(const strbuffer_t *strbuff);
/* Steal the value and close the strbuffer */
char *strbuffer_steal_value(strbuffer_t *strbuff);
int strbuffer_append_byte(strbuffer_t *strbuff, char byte);
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size);
char strbuffer_pop(strbuffer_t *strbuff);
#endif

145
client/jansson/strconv.c Normal file
View file

@ -0,0 +1,145 @@
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#ifdef __MINGW32__
#undef __NO_ISOCEXT /* ensure stdlib.h will declare prototypes for mingw own 'strtod' replacement, called '__strtod' */
#endif
#include "jansson_private.h"
#include "strbuffer.h"
/* need jansson_private_config.h to get the correct snprintf */
#ifdef HAVE_CONFIG_H
#include <jansson_private_config.h>
#endif
#ifdef __MINGW32__
#define strtod __strtod
#endif
#if JSON_HAVE_LOCALECONV
#include <locale.h>
/*
- This code assumes that the decimal separator is exactly one
character.
- If setlocale() is called by another thread between the call to
localeconv() and the call to sprintf() or strtod(), the result may
be wrong. setlocale() is not thread-safe and should not be used
this way. Multi-threaded programs should use uselocale() instead.
*/
static void to_locale(strbuffer_t *strbuffer)
{
const char *point;
char *pos;
point = localeconv()->decimal_point;
if(*point == '.') {
/* No conversion needed */
return;
}
pos = strchr(strbuffer->value, '.');
if(pos)
*pos = *point;
}
static void from_locale(char *buffer)
{
const char *point;
char *pos;
point = localeconv()->decimal_point;
if(*point == '.') {
/* No conversion needed */
return;
}
pos = strchr(buffer, *point);
if(pos)
*pos = '.';
}
#endif
int jsonp_strtod(strbuffer_t *strbuffer, double *out)
{
double value;
char *end;
#if JSON_HAVE_LOCALECONV
to_locale(strbuffer);
#endif
errno = 0;
value = strtod(strbuffer->value, &end);
assert(end == strbuffer->value + strbuffer->length);
if((value == HUGE_VAL || value == -HUGE_VAL) && errno == ERANGE) {
/* Overflow */
return -1;
}
*out = value;
return 0;
}
int jsonp_dtostr(char *buffer, size_t size, double value, int precision)
{
int ret;
char *start, *end;
size_t length;
if (precision == 0)
precision = 17;
ret = snprintf(buffer, size, "%.*g", precision, value);
if(ret < 0)
return -1;
length = (size_t)ret;
if(length >= size)
return -1;
#if JSON_HAVE_LOCALECONV
from_locale(buffer);
#endif
/* Make sure there's a dot or 'e' in the output. Otherwise
a real is converted to an integer when decoding */
if(strchr(buffer, '.') == NULL &&
strchr(buffer, 'e') == NULL)
{
if(length + 3 >= size) {
/* No space to append ".0" */
return -1;
}
buffer[length] = '.';
buffer[length + 1] = '0';
buffer[length + 2] = '\0';
length += 2;
}
/* Remove leading '+' from positive exponent. Also remove leading
zeros from exponents (added by some printf() implementations) */
start = strchr(buffer, 'e');
if(start) {
start++;
end = start + 1;
if(*start == '-')
start++;
while(*end == '0')
end++;
if(end != start) {
memmove(start, end, length - (size_t)(end - buffer));
length -= (size_t)(end - start);
}
}
return (int)length;
}

187
client/jansson/utf.c Normal file
View file

@ -0,0 +1,187 @@
/*
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <string.h>
#include "utf.h"
int utf8_encode(int32_t codepoint, char *buffer, size_t *size)
{
if(codepoint < 0)
return -1;
else if(codepoint < 0x80)
{
buffer[0] = (char)codepoint;
*size = 1;
}
else if(codepoint < 0x800)
{
buffer[0] = 0xC0 + ((codepoint & 0x7C0) >> 6);
buffer[1] = 0x80 + ((codepoint & 0x03F));
*size = 2;
}
else if(codepoint < 0x10000)
{
buffer[0] = 0xE0 + ((codepoint & 0xF000) >> 12);
buffer[1] = 0x80 + ((codepoint & 0x0FC0) >> 6);
buffer[2] = 0x80 + ((codepoint & 0x003F));
*size = 3;
}
else if(codepoint <= 0x10FFFF)
{
buffer[0] = 0xF0 + ((codepoint & 0x1C0000) >> 18);
buffer[1] = 0x80 + ((codepoint & 0x03F000) >> 12);
buffer[2] = 0x80 + ((codepoint & 0x000FC0) >> 6);
buffer[3] = 0x80 + ((codepoint & 0x00003F));
*size = 4;
}
else
return -1;
return 0;
}
size_t utf8_check_first(char byte)
{
unsigned char u = (unsigned char)byte;
if(u < 0x80)
return 1;
if(0x80 <= u && u <= 0xBF) {
/* second, third or fourth byte of a multi-byte
sequence, i.e. a "continuation byte" */
return 0;
}
else if(u == 0xC0 || u == 0xC1) {
/* overlong encoding of an ASCII byte */
return 0;
}
else if(0xC2 <= u && u <= 0xDF) {
/* 2-byte sequence */
return 2;
}
else if(0xE0 <= u && u <= 0xEF) {
/* 3-byte sequence */
return 3;
}
else if(0xF0 <= u && u <= 0xF4) {
/* 4-byte sequence */
return 4;
}
else { /* u >= 0xF5 */
/* Restricted (start of 4-, 5- or 6-byte sequence) or invalid
UTF-8 */
return 0;
}
}
size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint)
{
size_t i;
int32_t value = 0;
unsigned char u = (unsigned char)buffer[0];
if(size == 2)
{
value = u & 0x1F;
}
else if(size == 3)
{
value = u & 0xF;
}
else if(size == 4)
{
value = u & 0x7;
}
else
return 0;
for(i = 1; i < size; i++)
{
u = (unsigned char)buffer[i];
if(u < 0x80 || u > 0xBF) {
/* not a continuation byte */
return 0;
}
value = (value << 6) + (u & 0x3F);
}
if(value > 0x10FFFF) {
/* not in Unicode range */
return 0;
}
else if(0xD800 <= value && value <= 0xDFFF) {
/* invalid code point (UTF-16 surrogate halves) */
return 0;
}
else if((size == 2 && value < 0x80) ||
(size == 3 && value < 0x800) ||
(size == 4 && value < 0x10000)) {
/* overlong encoding */
return 0;
}
if(codepoint)
*codepoint = value;
return 1;
}
const char *utf8_iterate(const char *buffer, size_t bufsize, int32_t *codepoint)
{
size_t count;
int32_t value;
if(!bufsize)
return buffer;
count = utf8_check_first(buffer[0]);
if(count <= 0)
return NULL;
if(count == 1)
value = (unsigned char)buffer[0];
else
{
if(count > bufsize || !utf8_check_full(buffer, count, &value))
return NULL;
}
if(codepoint)
*codepoint = value;
return buffer + count;
}
int utf8_check_string(const char *string, size_t length)
{
size_t i;
for(i = 0; i < length; i++)
{
size_t count = utf8_check_first(string[i]);
if(count == 0)
return 0;
else if(count > 1)
{
if(count > length - i)
return 0;
if(!utf8_check_full(&string[i], count, NULL))
return 0;
i += count - 1;
}
}
return 1;
}

27
client/jansson/utf.h Normal file
View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
*
* Jansson is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#ifndef UTF_H
#define UTF_H
#ifdef HAVE_CONFIG_H
#include <jansson_private_config.h>
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
int utf8_encode(int32_t codepoint, char *buffer, size_t *size);
size_t utf8_check_first(char byte);
size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint);
const char *utf8_iterate(const char *buffer, size_t size, int32_t *codepoint);
int utf8_check_string(const char *string, size_t length);
#endif

1078
client/jansson/value.c Normal file

File diff suppressed because it is too large Load diff

0
client/obj/fido/.dummy Normal file
View file

View file

View file

@ -9,6 +9,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#include "util.h" #include "util.h"
#define UTIL_BUFFER_SIZE_SPRINT 4097
// global client debug variable // global client debug variable
uint8_t g_debugMode = 0; uint8_t g_debugMode = 0;
@ -22,6 +23,7 @@ uint8_t g_debugMode = 0;
#include <termios.h> #include <termios.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <unistd.h> #include <unistd.h>
#include <stdarg.h>
int ukbhit(void) { int ukbhit(void) {
int cnt = 0; int cnt = 0;
@ -119,6 +121,35 @@ void FillFileNameByUID(char *filenamePrefix, uint8_t *uid, const char *ext, int
strcat(filenamePrefix, ext); strcat(filenamePrefix, ext);
} }
// fill buffer from structure [{uint8_t data, size_t length},...]
int FillBuffer(uint8_t *data, size_t maxDataLength, size_t *dataLength, ...) {
*dataLength = 0;
va_list valist;
va_start(valist, dataLength);
uint8_t *vdata = NULL;
size_t vlength = 0;
do{
vdata = va_arg(valist, uint8_t *);
if (!vdata)
break;
vlength = va_arg(valist, size_t);
if (*dataLength + vlength > maxDataLength) {
va_end(valist);
return 1;
}
memcpy(&data[*dataLength], vdata, vlength);
*dataLength += vlength;
} while (vdata);
va_end(valist);
return 0;
}
void hex_to_buffer(const uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, const size_t hex_max_len, void hex_to_buffer(const uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, const size_t hex_max_len,
const size_t min_str_len, const size_t spaces_between, bool uppercase) { const size_t min_str_len, const size_t spaces_between, bool uppercase) {
@ -170,13 +201,13 @@ void print_hex_break(const uint8_t *data, const size_t len, uint8_t breaks) {
} }
char *sprint_hex(const uint8_t *data, const size_t len) { char *sprint_hex(const uint8_t *data, const size_t len) {
static char buf[1025] = {0}; static char buf[UTIL_BUFFER_SIZE_SPRINT] = {0};
hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, 0, 1, true); hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, 0, 1, true);
return buf; return buf;
} }
char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const size_t min_str_len) { char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const size_t min_str_len) {
static char buf[1025] = {0}; static char buf[UTIL_BUFFER_SIZE_SPRINT] = {0};
hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, min_str_len, 0, true); hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, min_str_len, 0, true);
return buf; return buf;
} }
@ -185,13 +216,11 @@ char *sprint_hex_inrow(const uint8_t *data, const size_t len) {
return sprint_hex_inrow_ex(data, len, 0); return sprint_hex_inrow_ex(data, len, 0);
} }
char *sprint_hex_inrow_spaces(const uint8_t *data, const size_t len, size_t spaces_between) { char *sprint_hex_inrow_spaces(const uint8_t *data, const size_t len, size_t spaces_between) {
static char buf[1025] = {0}; static char buf[UTIL_BUFFER_SIZE_SPRINT] = {0};
hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, 0, spaces_between, true); hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, 0, spaces_between, true);
return buf; return buf;
} }
char *sprint_bin_break(const uint8_t *data, const size_t len, const uint8_t breaks) { char *sprint_bin_break(const uint8_t *data, const size_t len, const uint8_t breaks) {
// make sure we don't go beyond our char array memory // make sure we don't go beyond our char array memory
@ -268,9 +297,9 @@ char *sprint_bin(const uint8_t *data, const size_t len) {
} }
char *sprint_hex_ascii(const uint8_t *data, const size_t len) { char *sprint_hex_ascii(const uint8_t *data, const size_t len) {
static char buf[1024]; static char buf[UTIL_BUFFER_SIZE_SPRINT];
char *tmp = buf; char *tmp = buf;
memset(buf, 0x00, 1024); memset(buf, 0x00, UTIL_BUFFER_SIZE_SPRINT);
size_t max_len = (len > 1010) ? 1010 : len; size_t max_len = (len > 1010) ? 1010 : len;
sprintf(tmp, "%s| ", sprint_hex(data, max_len) ); sprintf(tmp, "%s| ", sprint_hex(data, max_len) );
@ -288,9 +317,9 @@ char *sprint_hex_ascii(const uint8_t *data, const size_t len) {
} }
char *sprint_ascii_ex(const uint8_t *data, const size_t len, const size_t min_str_len) { char *sprint_ascii_ex(const uint8_t *data, const size_t len, const size_t min_str_len) {
static char buf[1024]; static char buf[UTIL_BUFFER_SIZE_SPRINT];
char *tmp = buf; char *tmp = buf;
memset(buf, 0x00, 1024); memset(buf, 0x00, UTIL_BUFFER_SIZE_SPRINT);
size_t max_len = (len > 1010) ? 1010 : len; size_t max_len = (len > 1010) ? 1010 : len;
size_t i = 0; size_t i = 0;
while(i < max_len){ while(i < max_len){

View file

@ -187,6 +187,9 @@ extern void AddLogUint64(char *fileName, char *extData, const uint64_t data);
extern void AddLogCurrentDT(char *fileName); extern void AddLogCurrentDT(char *fileName);
extern void FillFileNameByUID(char *filenamePrefix, uint8_t * uid, const char *ext, int uidlen); extern void FillFileNameByUID(char *filenamePrefix, uint8_t * uid, const char *ext, int uidlen);
// fill buffer from structure [{uint8_t data, size_t length},...]
extern int FillBuffer(uint8_t *data, size_t maxDataLength, size_t *dataLength, ...);
extern void hex_to_buffer(const uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, extern void hex_to_buffer(const uint8_t *buf, const uint8_t *hex_data, const size_t hex_len,
const size_t hex_max_len, const size_t min_str_len, const size_t spaces_between, const size_t hex_max_len, const size_t min_str_len, const size_t spaces_between,
bool uppercase); bool uppercase);

View file

@ -29,6 +29,7 @@ mbedtls_SOURCES = \
arc4.c \ arc4.c \
pk.c \ pk.c \
pk_wrap.c \ pk_wrap.c \
pkwrite.c \
pkcs5.c \ pkcs5.c \
pkcs12.c \ pkcs12.c \
pkparse.c \ pkparse.c \

517
common/mbedtls/pkwrite.c Normal file
View file

@ -0,0 +1,517 @@
/*
* Public Key layer for writing key files and structures
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: GPL-2.0
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This file is part of mbed TLS (https://tls.mbed.org)
*/
#if !defined(MBEDTLS_CONFIG_FILE)
#include "mbedtls/config.h"
#else
#include MBEDTLS_CONFIG_FILE
#endif
#if defined(MBEDTLS_PK_WRITE_C)
#include "mbedtls/pk.h"
#include "mbedtls/asn1write.h"
#include "mbedtls/oid.h"
#include <string.h>
#if defined(MBEDTLS_RSA_C)
#include "mbedtls/rsa.h"
#endif
#if defined(MBEDTLS_ECP_C)
#include "mbedtls/ecp.h"
#endif
#if defined(MBEDTLS_ECDSA_C)
#include "mbedtls/ecdsa.h"
#endif
#if defined(MBEDTLS_PEM_WRITE_C)
#include "mbedtls/pem.h"
#endif
#if defined(MBEDTLS_PLATFORM_C)
#include "mbedtls/platform.h"
#else
#include <stdlib.h>
#define mbedtls_calloc calloc
#define mbedtls_free free
#endif
#if defined(MBEDTLS_RSA_C)
/*
* RSAPublicKey ::= SEQUENCE {
* modulus INTEGER, -- n
* publicExponent INTEGER -- e
* }
*/
static int pk_write_rsa_pubkey( unsigned char **p, unsigned char *start,
mbedtls_rsa_context *rsa )
{
int ret;
size_t len = 0;
mbedtls_mpi T;
mbedtls_mpi_init( &T );
/* Export E */
if ( ( ret = mbedtls_rsa_export( rsa, NULL, NULL, NULL, NULL, &T ) ) != 0 ||
( ret = mbedtls_asn1_write_mpi( p, start, &T ) ) < 0 )
goto end_of_export;
len += ret;
/* Export N */
if ( ( ret = mbedtls_rsa_export( rsa, &T, NULL, NULL, NULL, NULL ) ) != 0 ||
( ret = mbedtls_asn1_write_mpi( p, start, &T ) ) < 0 )
goto end_of_export;
len += ret;
end_of_export:
mbedtls_mpi_free( &T );
if( ret < 0 )
return( ret );
MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( p, start, len ) );
MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( p, start, MBEDTLS_ASN1_CONSTRUCTED |
MBEDTLS_ASN1_SEQUENCE ) );
return( (int) len );
}
#endif /* MBEDTLS_RSA_C */
#if defined(MBEDTLS_ECP_C)
/*
* EC public key is an EC point
*/
static int pk_write_ec_pubkey( unsigned char **p, unsigned char *start,
mbedtls_ecp_keypair *ec )
{
int ret;
size_t len = 0;
unsigned char buf[MBEDTLS_ECP_MAX_PT_LEN];
if( ( ret = mbedtls_ecp_point_write_binary( &ec->grp, &ec->Q,
MBEDTLS_ECP_PF_UNCOMPRESSED,
&len, buf, sizeof( buf ) ) ) != 0 )
{
return( ret );
}
if( *p < start || (size_t)( *p - start ) < len )
return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL );
*p -= len;
memcpy( *p, buf, len );
return( (int) len );
}
/*
* ECParameters ::= CHOICE {
* namedCurve OBJECT IDENTIFIER
* }
*/
static int pk_write_ec_param( unsigned char **p, unsigned char *start,
mbedtls_ecp_keypair *ec )
{
int ret;
size_t len = 0;
const char *oid;
size_t oid_len;
if( ( ret = mbedtls_oid_get_oid_by_ec_grp( ec->grp.id, &oid, &oid_len ) ) != 0 )
return( ret );
MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_oid( p, start, oid, oid_len ) );
return( (int) len );
}
#endif /* MBEDTLS_ECP_C */
int mbedtls_pk_write_pubkey( unsigned char **p, unsigned char *start,
const mbedtls_pk_context *key )
{
int ret;
size_t len = 0;
#if defined(MBEDTLS_RSA_C)
if( mbedtls_pk_get_type( key ) == MBEDTLS_PK_RSA )
MBEDTLS_ASN1_CHK_ADD( len, pk_write_rsa_pubkey( p, start, mbedtls_pk_rsa( *key ) ) );
else
#endif
#if defined(MBEDTLS_ECP_C)
if( mbedtls_pk_get_type( key ) == MBEDTLS_PK_ECKEY )
MBEDTLS_ASN1_CHK_ADD( len, pk_write_ec_pubkey( p, start, mbedtls_pk_ec( *key ) ) );
else
#endif
return( MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE );
return( (int) len );
}
int mbedtls_pk_write_pubkey_der( mbedtls_pk_context *key, unsigned char *buf, size_t size )
{
int ret;
unsigned char *c;
size_t len = 0, par_len = 0, oid_len;
const char *oid;
c = buf + size;
MBEDTLS_ASN1_CHK_ADD( len, mbedtls_pk_write_pubkey( &c, buf, key ) );
if( c - buf < 1 )
return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL );
/*
* SubjectPublicKeyInfo ::= SEQUENCE {
* algorithm AlgorithmIdentifier,
* subjectPublicKey BIT STRING }
*/
*--c = 0;
len += 1;
MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_BIT_STRING ) );
if( ( ret = mbedtls_oid_get_oid_by_pk_alg( mbedtls_pk_get_type( key ),
&oid, &oid_len ) ) != 0 )
{
return( ret );
}
#if defined(MBEDTLS_ECP_C)
if( mbedtls_pk_get_type( key ) == MBEDTLS_PK_ECKEY )
{
MBEDTLS_ASN1_CHK_ADD( par_len, pk_write_ec_param( &c, buf, mbedtls_pk_ec( *key ) ) );
}
#endif
MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_algorithm_identifier( &c, buf, oid, oid_len,
par_len ) );
MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_CONSTRUCTED |
MBEDTLS_ASN1_SEQUENCE ) );
return( (int) len );
}
int mbedtls_pk_write_key_der( mbedtls_pk_context *key, unsigned char *buf, size_t size )
{
int ret;
unsigned char *c = buf + size;
size_t len = 0;
#if defined(MBEDTLS_RSA_C)
if( mbedtls_pk_get_type( key ) == MBEDTLS_PK_RSA )
{
mbedtls_mpi T; /* Temporary holding the exported parameters */
mbedtls_rsa_context *rsa = mbedtls_pk_rsa( *key );
/*
* Export the parameters one after another to avoid simultaneous copies.
*/
mbedtls_mpi_init( &T );
/* Export QP */
if( ( ret = mbedtls_rsa_export_crt( rsa, NULL, NULL, &T ) ) != 0 ||
( ret = mbedtls_asn1_write_mpi( &c, buf, &T ) ) < 0 )
goto end_of_export;
len += ret;
/* Export DQ */
if( ( ret = mbedtls_rsa_export_crt( rsa, NULL, &T, NULL ) ) != 0 ||
( ret = mbedtls_asn1_write_mpi( &c, buf, &T ) ) < 0 )
goto end_of_export;
len += ret;
/* Export DP */
if( ( ret = mbedtls_rsa_export_crt( rsa, &T, NULL, NULL ) ) != 0 ||
( ret = mbedtls_asn1_write_mpi( &c, buf, &T ) ) < 0 )
goto end_of_export;
len += ret;
/* Export Q */
if ( ( ret = mbedtls_rsa_export( rsa, NULL, NULL,
&T, NULL, NULL ) ) != 0 ||
( ret = mbedtls_asn1_write_mpi( &c, buf, &T ) ) < 0 )
goto end_of_export;
len += ret;
/* Export P */
if ( ( ret = mbedtls_rsa_export( rsa, NULL, &T,
NULL, NULL, NULL ) ) != 0 ||
( ret = mbedtls_asn1_write_mpi( &c, buf, &T ) ) < 0 )
goto end_of_export;
len += ret;
/* Export D */
if ( ( ret = mbedtls_rsa_export( rsa, NULL, NULL,
NULL, &T, NULL ) ) != 0 ||
( ret = mbedtls_asn1_write_mpi( &c, buf, &T ) ) < 0 )
goto end_of_export;
len += ret;
/* Export E */
if ( ( ret = mbedtls_rsa_export( rsa, NULL, NULL,
NULL, NULL, &T ) ) != 0 ||
( ret = mbedtls_asn1_write_mpi( &c, buf, &T ) ) < 0 )
goto end_of_export;
len += ret;
/* Export N */
if ( ( ret = mbedtls_rsa_export( rsa, &T, NULL,
NULL, NULL, NULL ) ) != 0 ||
( ret = mbedtls_asn1_write_mpi( &c, buf, &T ) ) < 0 )
goto end_of_export;
len += ret;
end_of_export:
mbedtls_mpi_free( &T );
if( ret < 0 )
return( ret );
MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_int( &c, buf, 0 ) );
MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c,
buf, MBEDTLS_ASN1_CONSTRUCTED |
MBEDTLS_ASN1_SEQUENCE ) );
}
else
#endif /* MBEDTLS_RSA_C */
#if defined(MBEDTLS_ECP_C)
if( mbedtls_pk_get_type( key ) == MBEDTLS_PK_ECKEY )
{
mbedtls_ecp_keypair *ec = mbedtls_pk_ec( *key );
size_t pub_len = 0, par_len = 0;
/*
* RFC 5915, or SEC1 Appendix C.4
*
* ECPrivateKey ::= SEQUENCE {
* version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
* privateKey OCTET STRING,
* parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
* publicKey [1] BIT STRING OPTIONAL
* }
*/
/* publicKey */
MBEDTLS_ASN1_CHK_ADD( pub_len, pk_write_ec_pubkey( &c, buf, ec ) );
if( c - buf < 1 )
return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL );
*--c = 0;
pub_len += 1;
MBEDTLS_ASN1_CHK_ADD( pub_len, mbedtls_asn1_write_len( &c, buf, pub_len ) );
MBEDTLS_ASN1_CHK_ADD( pub_len, mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_BIT_STRING ) );
MBEDTLS_ASN1_CHK_ADD( pub_len, mbedtls_asn1_write_len( &c, buf, pub_len ) );
MBEDTLS_ASN1_CHK_ADD( pub_len, mbedtls_asn1_write_tag( &c, buf,
MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 1 ) );
len += pub_len;
/* parameters */
MBEDTLS_ASN1_CHK_ADD( par_len, pk_write_ec_param( &c, buf, ec ) );
MBEDTLS_ASN1_CHK_ADD( par_len, mbedtls_asn1_write_len( &c, buf, par_len ) );
MBEDTLS_ASN1_CHK_ADD( par_len, mbedtls_asn1_write_tag( &c, buf,
MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 0 ) );
len += par_len;
/* privateKey: write as MPI then fix tag */
MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_mpi( &c, buf, &ec->d ) );
*c = MBEDTLS_ASN1_OCTET_STRING;
/* version */
MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_int( &c, buf, 1 ) );
MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_CONSTRUCTED |
MBEDTLS_ASN1_SEQUENCE ) );
}
else
#endif /* MBEDTLS_ECP_C */
return( MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE );
return( (int) len );
}
#if defined(MBEDTLS_PEM_WRITE_C)
#define PEM_BEGIN_PUBLIC_KEY "-----BEGIN PUBLIC KEY-----\n"
#define PEM_END_PUBLIC_KEY "-----END PUBLIC KEY-----\n"
#define PEM_BEGIN_PRIVATE_KEY_RSA "-----BEGIN RSA PRIVATE KEY-----\n"
#define PEM_END_PRIVATE_KEY_RSA "-----END RSA PRIVATE KEY-----\n"
#define PEM_BEGIN_PRIVATE_KEY_EC "-----BEGIN EC PRIVATE KEY-----\n"
#define PEM_END_PRIVATE_KEY_EC "-----END EC PRIVATE KEY-----\n"
/*
* Max sizes of key per types. Shown as tag + len (+ content).
*/
#if defined(MBEDTLS_RSA_C)
/*
* RSA public keys:
* SubjectPublicKeyInfo ::= SEQUENCE { 1 + 3
* algorithm AlgorithmIdentifier, 1 + 1 (sequence)
* + 1 + 1 + 9 (rsa oid)
* + 1 + 1 (params null)
* subjectPublicKey BIT STRING } 1 + 3 + (1 + below)
* RSAPublicKey ::= SEQUENCE { 1 + 3
* modulus INTEGER, -- n 1 + 3 + MPI_MAX + 1
* publicExponent INTEGER -- e 1 + 3 + MPI_MAX + 1
* }
*/
#define RSA_PUB_DER_MAX_BYTES 38 + 2 * MBEDTLS_MPI_MAX_SIZE
/*
* RSA private keys:
* RSAPrivateKey ::= SEQUENCE { 1 + 3
* version Version, 1 + 1 + 1
* modulus INTEGER, 1 + 3 + MPI_MAX + 1
* publicExponent INTEGER, 1 + 3 + MPI_MAX + 1
* privateExponent INTEGER, 1 + 3 + MPI_MAX + 1
* prime1 INTEGER, 1 + 3 + MPI_MAX / 2 + 1
* prime2 INTEGER, 1 + 3 + MPI_MAX / 2 + 1
* exponent1 INTEGER, 1 + 3 + MPI_MAX / 2 + 1
* exponent2 INTEGER, 1 + 3 + MPI_MAX / 2 + 1
* coefficient INTEGER, 1 + 3 + MPI_MAX / 2 + 1
* otherPrimeInfos OtherPrimeInfos OPTIONAL 0 (not supported)
* }
*/
#define MPI_MAX_SIZE_2 MBEDTLS_MPI_MAX_SIZE / 2 + \
MBEDTLS_MPI_MAX_SIZE % 2
#define RSA_PRV_DER_MAX_BYTES 47 + 3 * MBEDTLS_MPI_MAX_SIZE \
+ 5 * MPI_MAX_SIZE_2
#else /* MBEDTLS_RSA_C */
#define RSA_PUB_DER_MAX_BYTES 0
#define RSA_PRV_DER_MAX_BYTES 0
#endif /* MBEDTLS_RSA_C */
#if defined(MBEDTLS_ECP_C)
/*
* EC public keys:
* SubjectPublicKeyInfo ::= SEQUENCE { 1 + 2
* algorithm AlgorithmIdentifier, 1 + 1 (sequence)
* + 1 + 1 + 7 (ec oid)
* + 1 + 1 + 9 (namedCurve oid)
* subjectPublicKey BIT STRING 1 + 2 + 1 [1]
* + 1 (point format) [1]
* + 2 * ECP_MAX (coords) [1]
* }
*/
#define ECP_PUB_DER_MAX_BYTES 30 + 2 * MBEDTLS_ECP_MAX_BYTES
/*
* EC private keys:
* ECPrivateKey ::= SEQUENCE { 1 + 2
* version INTEGER , 1 + 1 + 1
* privateKey OCTET STRING, 1 + 1 + ECP_MAX
* parameters [0] ECParameters OPTIONAL, 1 + 1 + (1 + 1 + 9)
* publicKey [1] BIT STRING OPTIONAL 1 + 2 + [1] above
* }
*/
#define ECP_PRV_DER_MAX_BYTES 29 + 3 * MBEDTLS_ECP_MAX_BYTES
#else /* MBEDTLS_ECP_C */
#define ECP_PUB_DER_MAX_BYTES 0
#define ECP_PRV_DER_MAX_BYTES 0
#endif /* MBEDTLS_ECP_C */
#define PUB_DER_MAX_BYTES RSA_PUB_DER_MAX_BYTES > ECP_PUB_DER_MAX_BYTES ? \
RSA_PUB_DER_MAX_BYTES : ECP_PUB_DER_MAX_BYTES
#define PRV_DER_MAX_BYTES RSA_PRV_DER_MAX_BYTES > ECP_PRV_DER_MAX_BYTES ? \
RSA_PRV_DER_MAX_BYTES : ECP_PRV_DER_MAX_BYTES
int mbedtls_pk_write_pubkey_pem( mbedtls_pk_context *key, unsigned char *buf, size_t size )
{
int ret;
unsigned char output_buf[PUB_DER_MAX_BYTES];
size_t olen = 0;
if( ( ret = mbedtls_pk_write_pubkey_der( key, output_buf,
sizeof(output_buf) ) ) < 0 )
{
return( ret );
}
if( ( ret = mbedtls_pem_write_buffer( PEM_BEGIN_PUBLIC_KEY, PEM_END_PUBLIC_KEY,
output_buf + sizeof(output_buf) - ret,
ret, buf, size, &olen ) ) != 0 )
{
return( ret );
}
return( 0 );
}
int mbedtls_pk_write_key_pem( mbedtls_pk_context *key, unsigned char *buf, size_t size )
{
int ret;
unsigned char output_buf[PRV_DER_MAX_BYTES];
const char *begin, *end;
size_t olen = 0;
if( ( ret = mbedtls_pk_write_key_der( key, output_buf, sizeof(output_buf) ) ) < 0 )
return( ret );
#if defined(MBEDTLS_RSA_C)
if( mbedtls_pk_get_type( key ) == MBEDTLS_PK_RSA )
{
begin = PEM_BEGIN_PRIVATE_KEY_RSA;
end = PEM_END_PRIVATE_KEY_RSA;
}
else
#endif
#if defined(MBEDTLS_ECP_C)
if( mbedtls_pk_get_type( key ) == MBEDTLS_PK_ECKEY )
{
begin = PEM_BEGIN_PRIVATE_KEY_EC;
end = PEM_END_PRIVATE_KEY_EC;
}
else
#endif
return( MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE );
if( ( ret = mbedtls_pem_write_buffer( begin, end,
output_buf + sizeof(output_buf) - ret,
ret, buf, size, &olen ) ) != 0 )
{
return( ret );
}
return( 0 );
}
#endif /* MBEDTLS_PEM_WRITE_C */
#endif /* MBEDTLS_PK_WRITE_C */