mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-21 05:43:48 -07:00
commit
6faec7e981
50 changed files with 8448 additions and 63 deletions
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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]"},
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
704
client/cmdhffido.c
Normal 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
27
client/cmdhffido.h
Normal 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
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
385
client/emv/emvjson.c
Normal 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
40
client/emv/emvjson.h
Normal 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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
63
client/fido/additional_ca.c
Normal file
63
client/fido/additional_ca.c
Normal 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);
|
21
client/fido/additional_ca.h
Normal file
21
client/fido/additional_ca.h
Normal 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
64
client/jansson/Makefile
Normal 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
504
client/jansson/dump.c
Normal 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
66
client/jansson/error.c
Normal 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
356
client/jansson/hashtable.c
Normal 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
176
client/jansson/hashtable.h
Normal 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
|
277
client/jansson/hashtable_seed.c
Normal file
277
client/jansson/hashtable_seed.c
Normal 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
|
75
client/jansson/jansson.def
Normal file
75
client/jansson/jansson.def
Normal 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
372
client/jansson/jansson.h
Normal 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
|
51
client/jansson/jansson_config.h
Normal file
51
client/jansson/jansson_config.h
Normal 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
|
51
client/jansson/jansson_config.h.in
Normal file
51
client/jansson/jansson_config.h.in
Normal 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
|
112
client/jansson/jansson_private.h
Normal file
112
client/jansson/jansson_private.h
Normal 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
1155
client/jansson/load.c
Normal file
File diff suppressed because it is too large
Load diff
381
client/jansson/lookup3.h
Normal file
381
client/jansson/lookup3.h
Normal 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
69
client/jansson/memory.c
Normal 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;
|
||||||
|
}
|
909
client/jansson/pack_unpack.c
Normal file
909
client/jansson/pack_unpack.c
Normal 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
201
client/jansson/path.c
Normal 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
111
client/jansson/strbuffer.c
Normal 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';
|
||||||
|
}
|
34
client/jansson/strbuffer.h
Normal file
34
client/jansson/strbuffer.h
Normal 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
145
client/jansson/strconv.c
Normal 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
187
client/jansson/utf.c
Normal 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
27
client/jansson/utf.h
Normal 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
1078
client/jansson/value.c
Normal file
File diff suppressed because it is too large
Load diff
0
client/obj/fido/.dummy
Normal file
0
client/obj/fido/.dummy
Normal file
0
client/obj/jansson/.dummy
Normal file
0
client/obj/jansson/.dummy
Normal 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){
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
517
common/mbedtls/pkwrite.c
Normal 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 */
|
Loading…
Add table
Add a link
Reference in a new issue