mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-05-12 11:18:11 -07:00
@@ -85,6 +85,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
|
||||
- Added jansson (@merlokk)
|
||||
- Added `hf emv scan` - save card's data to json file (@merlokk)
|
||||
- Added `hf emv` `gpo`, `readrec`, `genac`, `challenge`, `intauth` - separate commands from `hf emc exec` (@merlokk)
|
||||
- Added `hf fido` `assert` and `make` commands from fido2 protocol (authenticatorMakeCredential and authenticatorGetAssertion) (@merlokk)
|
||||
|
||||
### Fixed
|
||||
- Changed driver file proxmark3.inf to support both old and new Product/Vendor IDs (piwi)
|
||||
|
||||
+14
-3
@@ -29,8 +29,11 @@ JANSSONLIBPATH = ./jansson
|
||||
JANSSONLIB = $(JANSSONLIBPATH)/libjansson.a
|
||||
MBEDTLSLIBPATH = ../common/mbedtls
|
||||
MBEDTLSLIB = $(MBEDTLSLIBPATH)/libmbedtls.a
|
||||
CBORLIBPATH = ./tinycbor
|
||||
CBORLIB = $(CBORLIBPATH)/tinycbor.a
|
||||
LIBS = -I../zlib -I../uart -I../liblua -I$(MBEDTLSLIBPATH) -I$(JANSSONLIBPATH) -I$(CBORLIBPATH)
|
||||
INCLUDES_CLIENT = -I. -I../include -I../common -I/opt/local/include $(LIBS)
|
||||
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) -I$(JANSSONLIBPATH)
|
||||
CFLAGS = $(ENV_CFLAGS) -std=c99 -D_ISOC99_SOURCE -DPRESETS $(INCLUDES_CLIENT) -Wall -g -O3
|
||||
CXXFLAGS = -I../include -Wall -O3
|
||||
|
||||
@@ -108,6 +111,9 @@ CMDSRCS = crapto1/crapto1.c \
|
||||
mfkey.c \
|
||||
tea.c \
|
||||
fido/additional_ca.c \
|
||||
fido/cose.c \
|
||||
fido/cbortools.c \
|
||||
fido/fidocore.c \
|
||||
crypto/asn1dump.c \
|
||||
crypto/libpcrypto.c\
|
||||
crypto/asn1utils.c\
|
||||
@@ -261,12 +267,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
|
||||
|
||||
# need to assign dependancies to build these first...
|
||||
all: lua_build jansson_build mbedtls_build $(BINS)
|
||||
all: lua_build jansson_build mbedtls_build cbor_build $(BINS)
|
||||
|
||||
all-static: LDLIBS:=-static $(LDLIBS)
|
||||
all-static: proxmark3 flasher fpga_compress
|
||||
|
||||
proxmark3: LDLIBS+=$(LUALIB) $(JANSSONLIB) $(MBEDTLSLIB) $(QTLDLIBS)
|
||||
proxmark3: LDLIBS+=$(LUALIB) $(JANSSONLIB) $(MBEDTLSLIB) $(CBORLIB) $(QTLDLIBS)
|
||||
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 $@
|
||||
|
||||
@@ -295,6 +301,7 @@ clean:
|
||||
cd ../liblua && make clean
|
||||
cd $(JANSSONLIBPATH) && make clean
|
||||
cd $(MBEDTLSLIBPATH) && make clean
|
||||
cd $(CBORLIBPATH) && make clean
|
||||
|
||||
tarbin: $(BINS)
|
||||
$(TAR) $(TARFLAGS) ../proxmark3-$(platform)-bin.tar $(BINS:%=client/%) $(WINBINS:%=client/%)
|
||||
@@ -311,6 +318,10 @@ mbedtls_build:
|
||||
@echo Compiling mbedtls
|
||||
cd $(MBEDTLSLIBPATH) && make all
|
||||
|
||||
cbor_build:
|
||||
@echo Compiling tinycbor
|
||||
cd $(CBORLIBPATH) && make all
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
# easy printing of MAKE VARIABLES
|
||||
|
||||
+318
-104
@@ -28,12 +28,14 @@
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#include <jansson.h>
|
||||
#include <mbedtls/x509_crt.h>
|
||||
#include <mbedtls/x509.h>
|
||||
#include <mbedtls/pk.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"
|
||||
@@ -41,50 +43,12 @@
|
||||
#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"
|
||||
#include "fido/cbortools.h"
|
||||
#include "fido/fidocore.h"
|
||||
#include "fido/cose.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)
|
||||
@@ -139,10 +103,24 @@ int CmdHFFidoInfo(const char *cmd) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PrintAndLog("FIDO2 version: (%d)", len);
|
||||
dump_buffer((const unsigned char *)buf, len, NULL, 0);
|
||||
|
||||
if(buf[0]) {
|
||||
PrintAndLog("FIDO2 ger version error: %d - %s", buf[0], fido2GetCmdErrorDescription(buf[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (len > 1) {
|
||||
// if (false) {
|
||||
// PrintAndLog("FIDO2 version: (len=%d)", len);
|
||||
// dump_buffer((const unsigned char *)buf, len, NULL, 0);
|
||||
// }
|
||||
|
||||
PrintAndLog("FIDO2 version CBOR decoded:");
|
||||
TinyCborPrintFIDOPackage(fido2CmdGetInfo, true, &buf[1], len - 1);
|
||||
} else {
|
||||
PrintAndLog("FIDO2 version length error");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -348,60 +326,7 @@ int CmdHFFidoRegister(const char *cmd) {
|
||||
PrintAndLog("----------------DER TLV-----------------");
|
||||
}
|
||||
|
||||
// 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[derp], 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);
|
||||
FIDOCheckDERAndGetKey(&buf[derp], derLen, verbose, public_key, sizeof(public_key));
|
||||
|
||||
// get hash
|
||||
int hashp = 1 + 65 + 1 + keyHandleLen + derLen;
|
||||
@@ -690,13 +615,302 @@ int CmdHFFidoAuthenticate(const char *cmd) {
|
||||
return 0;
|
||||
};
|
||||
|
||||
void CheckSlash(char *fileName) {
|
||||
if ((fileName[strlen(fileName) - 1] != '/') &&
|
||||
(fileName[strlen(fileName) - 1] != '\\'))
|
||||
strcat(fileName, "/");
|
||||
}
|
||||
|
||||
int GetExistsFileNameJson(char *prefixDir, char *reqestedFileName, char *fileName) {
|
||||
fileName[0] = 0x00;
|
||||
strcpy(fileName, get_my_executable_directory());
|
||||
CheckSlash(fileName);
|
||||
|
||||
strcat(fileName, prefixDir);
|
||||
CheckSlash(fileName);
|
||||
|
||||
strcat(fileName, reqestedFileName);
|
||||
if (!strstr(fileName, ".json"))
|
||||
strcat(fileName, ".json");
|
||||
|
||||
if (access(fileName, F_OK) < 0) {
|
||||
strcpy(fileName, get_my_executable_directory());
|
||||
CheckSlash(fileName);
|
||||
|
||||
strcat(fileName, reqestedFileName);
|
||||
if (!strstr(fileName, ".json"))
|
||||
strcat(fileName, ".json");
|
||||
|
||||
if (access(fileName, F_OK) < 0) {
|
||||
return 1; // file not found
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CmdHFFido2MakeCredential(const char *cmd) {
|
||||
json_error_t error;
|
||||
json_t *root = NULL;
|
||||
char fname[300] = {0};
|
||||
|
||||
CLIParserInit("hf fido make",
|
||||
"Execute a FIDO2 Make Credentional command. Needs json file with parameters. Sample file `fido2.json`. File can be placed in proxmark directory or in `proxmark/fido` directory.",
|
||||
"Usage:\n\thf fido make -> execute command default parameters file `fido2.json`\n"
|
||||
"\thf fido make test.json -> execute command with parameters file `text.json`");
|
||||
|
||||
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("tT", "tlv", "Show DER certificate contents in TLV representation"),
|
||||
arg_lit0("cC", "cbor", "show CBOR decoded data"),
|
||||
arg_str0(NULL, NULL, "<json file name>", "JSON input / output file name for parameters. Default `fido2.json`"),
|
||||
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 showDERTLV = arg_get_lit(3);
|
||||
bool showCBOR = arg_get_lit(4);
|
||||
|
||||
uint8_t jsonname[250] ={0};
|
||||
char *cjsonname = (char *)jsonname;
|
||||
int jsonnamelen = 0;
|
||||
CLIGetStrWithReturn(5, jsonname, &jsonnamelen);
|
||||
|
||||
if (!jsonnamelen) {
|
||||
strcat(cjsonname, "fido2");
|
||||
jsonnamelen = strlen(cjsonname);
|
||||
}
|
||||
|
||||
CLIParserFree();
|
||||
|
||||
SetAPDULogging(APDULogging);
|
||||
|
||||
int res = GetExistsFileNameJson("fido", cjsonname, fname);
|
||||
if(res) {
|
||||
PrintAndLog("ERROR: Can't found the json file.");
|
||||
return res;
|
||||
}
|
||||
PrintAndLog("fname: %s\n", fname);
|
||||
root = json_load_file(fname, 0, &error);
|
||||
if (!root) {
|
||||
PrintAndLog("ERROR: json error on line %d: %s", error.line, error.text);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t data[2048] = {0};
|
||||
size_t datalen = 0;
|
||||
uint8_t buf[2048] = {0};
|
||||
size_t len = 0;
|
||||
uint16_t sw = 0;
|
||||
|
||||
DropField();
|
||||
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 = FIDO2CreateMakeCredentionalReq(root, data, sizeof(data), &datalen);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
if (showCBOR) {
|
||||
PrintAndLog("CBOR make credentional request:");
|
||||
PrintAndLog("---------------- CBOR ------------------");
|
||||
TinyCborPrintFIDOPackage(fido2CmdMakeCredential, false, data, datalen);
|
||||
PrintAndLog("---------------- CBOR ------------------");
|
||||
}
|
||||
|
||||
res = FIDO2MakeCredential(data, datalen, buf, sizeof(buf), &len, &sw);
|
||||
DropField();
|
||||
if (res) {
|
||||
PrintAndLog("Can't execute make credential command. res=%x. Exit...", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (sw != 0x9000) {
|
||||
PrintAndLog("ERROR execute make credential command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
|
||||
return 3;
|
||||
}
|
||||
|
||||
if(buf[0]) {
|
||||
PrintAndLog("FIDO2 make credential error: %d - %s", buf[0], fido2GetCmdErrorDescription(buf[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
PrintAndLog("MakeCredential result (%d b) OK.", len);
|
||||
if (showCBOR) {
|
||||
PrintAndLog("CBOR make credentional response:");
|
||||
PrintAndLog("---------------- CBOR ------------------");
|
||||
TinyCborPrintFIDOPackage(fido2CmdMakeCredential, true, &buf[1], len - 1);
|
||||
PrintAndLog("---------------- CBOR ------------------");
|
||||
}
|
||||
|
||||
// parse returned cbor
|
||||
FIDO2MakeCredentionalParseRes(root, &buf[1], len - 1, verbose, verbose2, showCBOR, showDERTLV);
|
||||
|
||||
if (root) {
|
||||
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);
|
||||
}
|
||||
|
||||
json_decref(root);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
int CmdHFFido2GetAssertion(const char *cmd) {
|
||||
json_error_t error;
|
||||
json_t *root = NULL;
|
||||
char fname[300] = {0};
|
||||
|
||||
CLIParserInit("hf fido assert",
|
||||
"Execute a FIDO2 Get Assertion command. Needs json file with parameters. Sample file `fido2.json`. File can be placed in proxmark directory or in `proxmark/fido` directory.",
|
||||
"Usage:\n\thf fido assert -> execute command default parameters file `fido2.json`\n"
|
||||
"\thf fido assert test.json -l -> execute command with parameters file `text.json` and add to request CredentialId");
|
||||
|
||||
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("cC", "cbor", "show CBOR decoded data"),
|
||||
arg_lit0("lL", "list", "add CredentialId from json to allowList. Needs if `rk` option is `false` (authenticator don't store credential to its memory)"),
|
||||
arg_str0(NULL, NULL, "<json file name>", "JSON input / output file name for parameters. Default `fido2.json`"),
|
||||
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 showCBOR = arg_get_lit(3);
|
||||
bool createAllowList = arg_get_lit(4);
|
||||
|
||||
uint8_t jsonname[250] ={0};
|
||||
char *cjsonname = (char *)jsonname;
|
||||
int jsonnamelen = 0;
|
||||
CLIGetStrWithReturn(5, jsonname, &jsonnamelen);
|
||||
|
||||
if (!jsonnamelen) {
|
||||
strcat(cjsonname, "fido2");
|
||||
jsonnamelen = strlen(cjsonname);
|
||||
}
|
||||
|
||||
CLIParserFree();
|
||||
|
||||
SetAPDULogging(APDULogging);
|
||||
|
||||
int res = GetExistsFileNameJson("fido", "fido2", fname);
|
||||
if(res) {
|
||||
PrintAndLog("ERROR: Can't found the json file.");
|
||||
return res;
|
||||
}
|
||||
PrintAndLog("fname: %s\n", fname);
|
||||
root = json_load_file(fname, 0, &error);
|
||||
if (!root) {
|
||||
PrintAndLog("ERROR: json error on line %d: %s", error.line, error.text);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t data[2048] = {0};
|
||||
size_t datalen = 0;
|
||||
uint8_t buf[2048] = {0};
|
||||
size_t len = 0;
|
||||
uint16_t sw = 0;
|
||||
|
||||
DropField();
|
||||
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 = FIDO2CreateGetAssertionReq(root, data, sizeof(data), &datalen, createAllowList);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
if (showCBOR) {
|
||||
PrintAndLog("CBOR get assertion request:");
|
||||
PrintAndLog("---------------- CBOR ------------------");
|
||||
TinyCborPrintFIDOPackage(fido2CmdGetAssertion, false, data, datalen);
|
||||
PrintAndLog("---------------- CBOR ------------------");
|
||||
}
|
||||
|
||||
res = FIDO2GetAssertion(data, datalen, buf, sizeof(buf), &len, &sw);
|
||||
DropField();
|
||||
if (res) {
|
||||
PrintAndLog("Can't execute get assertion command. res=%x. Exit...", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (sw != 0x9000) {
|
||||
PrintAndLog("ERROR execute get assertion command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
|
||||
return 3;
|
||||
}
|
||||
|
||||
if(buf[0]) {
|
||||
PrintAndLog("FIDO2 get assertion error: %d - %s", buf[0], fido2GetCmdErrorDescription(buf[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
PrintAndLog("GetAssertion result (%d b) OK.", len);
|
||||
if (showCBOR) {
|
||||
PrintAndLog("CBOR get assertion response:");
|
||||
PrintAndLog("---------------- CBOR ------------------");
|
||||
TinyCborPrintFIDOPackage(fido2CmdGetAssertion, true, &buf[1], len - 1);
|
||||
PrintAndLog("---------------- CBOR ------------------");
|
||||
}
|
||||
|
||||
// parse returned cbor
|
||||
FIDO2GetAssertionParseRes(root, &buf[1], len - 1, verbose, verbose2, showCBOR);
|
||||
|
||||
if (root) {
|
||||
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);
|
||||
}
|
||||
|
||||
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}
|
||||
{"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."},
|
||||
{"make", CmdHFFido2MakeCredential, 0, "FIDO2 MakeCredential command."},
|
||||
{"assert", CmdHFFido2GetAssertion, 0, "FIDO2 GetAssertion command."},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
int CmdHFFido(const char *Cmd) {
|
||||
|
||||
@@ -0,0 +1,486 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
// Tools for work with CBOR format http://cbor.io/spec.html
|
||||
// via Intel tinycbor (https://github.com/intel/tinycbor) library
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "cbortools.h"
|
||||
#include <stdlib.h>
|
||||
#include "emv/emvjson.h"
|
||||
#include "util.h"
|
||||
#include "fidocore.h"
|
||||
|
||||
static void indent(int nestingLevel) {
|
||||
while (nestingLevel--)
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
static CborError dumpelm(CborValue *it, bool *got_next, int nestingLevel) {
|
||||
CborError err;
|
||||
*got_next = false;
|
||||
|
||||
CborType type = cbor_value_get_type(it);
|
||||
indent(nestingLevel);
|
||||
switch (type) {
|
||||
case CborMapType:
|
||||
case CborArrayType: {
|
||||
printf(type == CborArrayType ? "Array[" : "Map[");
|
||||
break;
|
||||
}
|
||||
|
||||
case CborIntegerType: {
|
||||
int64_t val;
|
||||
cbor_value_get_int64(it, &val); // can't fail
|
||||
printf("%lld", (long long)val);
|
||||
break;
|
||||
}
|
||||
|
||||
case CborByteStringType: {
|
||||
uint8_t *buf;
|
||||
size_t n;
|
||||
err = cbor_value_dup_byte_string(it, &buf, &n, it);
|
||||
*got_next = true;
|
||||
if (err)
|
||||
return err; // parse error
|
||||
printf("%s", sprint_hex(buf, n));
|
||||
free(buf);
|
||||
break;
|
||||
}
|
||||
|
||||
case CborTextStringType: {
|
||||
char *buf;
|
||||
size_t n;
|
||||
err = cbor_value_dup_text_string(it, &buf, &n, it);
|
||||
*got_next = true;
|
||||
if (err)
|
||||
return err; // parse error
|
||||
printf("%s", buf);
|
||||
free(buf);
|
||||
break;
|
||||
}
|
||||
|
||||
case CborTagType: {
|
||||
CborTag tag;
|
||||
cbor_value_get_tag(it, &tag);
|
||||
printf("Tag(%lld)", (long long)tag);
|
||||
break;
|
||||
}
|
||||
|
||||
case CborSimpleType: {
|
||||
uint8_t type;
|
||||
cbor_value_get_simple_type(it, &type);
|
||||
printf("simple(%u)", type);
|
||||
break;
|
||||
}
|
||||
|
||||
case CborNullType:
|
||||
printf("null");
|
||||
break;
|
||||
|
||||
case CborUndefinedType:
|
||||
printf("undefined");
|
||||
break;
|
||||
|
||||
case CborBooleanType: {
|
||||
bool val;
|
||||
cbor_value_get_boolean(it, &val); // can't fail
|
||||
printf("%s", val ? "true" : "false");
|
||||
break;
|
||||
}
|
||||
|
||||
case CborDoubleType: {
|
||||
double val;
|
||||
if (false) {
|
||||
float f;
|
||||
case CborFloatType:
|
||||
cbor_value_get_float(it, &f);
|
||||
val = f;
|
||||
} else {
|
||||
cbor_value_get_double(it, &val);
|
||||
}
|
||||
printf("%g", val);
|
||||
break;
|
||||
}
|
||||
case CborHalfFloatType: {
|
||||
uint16_t val;
|
||||
cbor_value_get_half_float(it, &val);
|
||||
printf("__f16(%04x)", val);
|
||||
break;
|
||||
}
|
||||
|
||||
case CborInvalidType:
|
||||
printf("CborInvalidType!!!");
|
||||
break;
|
||||
}
|
||||
|
||||
return CborNoError;
|
||||
}
|
||||
|
||||
static CborError dumprecursive(uint8_t cmdCode, bool isResponse, CborValue *it, bool isMapType, int nestingLevel) {
|
||||
int elmCount = 0;
|
||||
while (!cbor_value_at_end(it)) {
|
||||
CborError err;
|
||||
CborType type = cbor_value_get_type(it);
|
||||
//printf("^%x^", type);
|
||||
bool got_next;
|
||||
|
||||
switch (type) {
|
||||
case CborMapType:
|
||||
case CborArrayType: {
|
||||
// recursive type
|
||||
CborValue recursed;
|
||||
assert(cbor_value_is_container(it));
|
||||
if (!(isMapType && (elmCount % 2)))
|
||||
indent(nestingLevel);
|
||||
printf(type == CborArrayType ? "Array[\n" : "Map[\n");
|
||||
err = cbor_value_enter_container(it, &recursed);
|
||||
if (err)
|
||||
return err; // parse error
|
||||
err = dumprecursive(cmdCode, isResponse, &recursed, (type == CborMapType), nestingLevel + 1);
|
||||
if (err)
|
||||
return err; // parse error
|
||||
err = cbor_value_leave_container(it, &recursed);
|
||||
if (err)
|
||||
return err; // parse error
|
||||
indent(nestingLevel);
|
||||
printf("]");
|
||||
got_next = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
err = dumpelm(it, &got_next, (isMapType && (elmCount % 2)) ? 0 : nestingLevel);
|
||||
if (err)
|
||||
return err;
|
||||
if (cmdCode > 0 && nestingLevel == 1 && isMapType && !(elmCount % 2)) {
|
||||
int64_t val;
|
||||
cbor_value_get_int64(it, &val);
|
||||
char *desc = fido2GetCmdMemberDescription(cmdCode, isResponse, val);
|
||||
if (desc)
|
||||
printf(" (%s)", desc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!got_next) {
|
||||
err = cbor_value_advance_fixed(it);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
if (isMapType && !(elmCount % 2)) {
|
||||
printf(": ");
|
||||
} else {
|
||||
printf("\n");
|
||||
}
|
||||
elmCount++;
|
||||
}
|
||||
return CborNoError;
|
||||
}
|
||||
|
||||
int TinyCborInit(uint8_t *data, size_t length, CborValue *cb) {
|
||||
CborParser parser;
|
||||
CborError err = cbor_parser_init(data, length, 0, &parser, cb);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TinyCborPrintFIDOPackage(uint8_t cmdCode, bool isResponse, uint8_t *data, size_t length) {
|
||||
CborValue cb;
|
||||
int res;
|
||||
res = TinyCborInit(data, length, &cb);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
CborError err = dumprecursive(cmdCode, isResponse, &cb, false, 0);
|
||||
|
||||
if (err) {
|
||||
fprintf(stderr, "CBOR parsing failure at offset %d: %s\n",
|
||||
cb.ptr - data, cbor_error_string(err));
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int JsonObjElmCount(json_t *elm) {
|
||||
int res = 0;
|
||||
const char *key;
|
||||
json_t *value;
|
||||
|
||||
if (!json_is_object(elm))
|
||||
return 0;
|
||||
|
||||
json_object_foreach(elm, key, value) {
|
||||
if (strlen(key) > 0 && key[0] != '.')
|
||||
res++;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int JsonToCbor(json_t *elm, CborEncoder *encoder) {
|
||||
if (!elm || !encoder)
|
||||
return 1;
|
||||
|
||||
int res;
|
||||
|
||||
// CBOR map == JSON object
|
||||
if (json_is_object(elm)) {
|
||||
CborEncoder map;
|
||||
const char *key;
|
||||
json_t *value;
|
||||
|
||||
res = cbor_encoder_create_map(encoder, &map, JsonObjElmCount(elm));
|
||||
cbor_check(res);
|
||||
|
||||
json_object_foreach(elm, key, value) {
|
||||
if (strlen(key) > 0 && key[0] != '.') {
|
||||
res = cbor_encode_text_stringz(&map, key);
|
||||
cbor_check(res);
|
||||
|
||||
// RECURSION!
|
||||
JsonToCbor(value, &map);
|
||||
}
|
||||
}
|
||||
|
||||
res = cbor_encoder_close_container(encoder, &map);
|
||||
cbor_check(res);
|
||||
}
|
||||
|
||||
// CBOR array == JSON array
|
||||
if (json_is_array(elm)) {
|
||||
size_t index;
|
||||
json_t *value;
|
||||
CborEncoder array;
|
||||
|
||||
res = cbor_encoder_create_array(encoder, &array, json_array_size(elm));
|
||||
cbor_check(res);
|
||||
|
||||
json_array_foreach(elm, index, value) {
|
||||
// RECURSION!
|
||||
JsonToCbor(value, &array);
|
||||
}
|
||||
|
||||
res = cbor_encoder_close_container(encoder, &array);
|
||||
cbor_check(res);
|
||||
}
|
||||
|
||||
if (json_is_boolean(elm)) {
|
||||
res = cbor_encode_boolean(encoder, json_is_true(elm));
|
||||
cbor_check(res);
|
||||
}
|
||||
|
||||
if (json_is_integer(elm)) {
|
||||
res = cbor_encode_int(encoder, json_integer_value(elm));
|
||||
cbor_check(res);
|
||||
}
|
||||
|
||||
if (json_is_real(elm)) {
|
||||
res = cbor_encode_float(encoder, json_real_value(elm));
|
||||
cbor_check(res);
|
||||
}
|
||||
|
||||
if (json_is_string(elm)) {
|
||||
const char * val = json_string_value(elm);
|
||||
if (CheckStringIsHEXValue(val)) {
|
||||
size_t datalen = 0;
|
||||
uint8_t data[4096] = {0};
|
||||
res = JsonLoadBufAsHex(elm, "$", data, sizeof(data), &datalen);
|
||||
if (res)
|
||||
return 100;
|
||||
|
||||
res = cbor_encode_byte_string(encoder, data, datalen);
|
||||
cbor_check(res);
|
||||
} else {
|
||||
res = cbor_encode_text_stringz(encoder, val);
|
||||
cbor_check(res);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CborMapGetKeyById(CborParser *parser, CborValue *map, uint8_t *data, size_t dataLen, int key) {
|
||||
CborValue cb;
|
||||
|
||||
CborError err = cbor_parser_init(data, dataLen, 0, parser, &cb);
|
||||
cbor_check(err);
|
||||
|
||||
if (cbor_value_get_type(&cb) != CborMapType)
|
||||
return 1;
|
||||
|
||||
err = cbor_value_enter_container(&cb, map);
|
||||
cbor_check(err);
|
||||
|
||||
int64_t indx;
|
||||
while (!cbor_value_at_end(map)) {
|
||||
// check number
|
||||
if (cbor_value_get_type(map) != CborIntegerType)
|
||||
return 1;
|
||||
|
||||
cbor_value_get_int64(map, &indx);
|
||||
|
||||
err = cbor_value_advance(map);
|
||||
cbor_check(err);
|
||||
|
||||
if (indx == key)
|
||||
return 0;
|
||||
|
||||
// pass value
|
||||
err = cbor_value_advance(map);
|
||||
cbor_check(err);
|
||||
}
|
||||
|
||||
err = cbor_value_leave_container(&cb, map);
|
||||
cbor_check(err);
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
CborError CborGetArrayBinStringValue(CborValue *elm, uint8_t *data, size_t maxdatalen, size_t *datalen) {
|
||||
return CborGetArrayBinStringValueEx(elm, data, maxdatalen, datalen, NULL, 0);
|
||||
}
|
||||
|
||||
CborError CborGetArrayBinStringValueEx(CborValue *elm, uint8_t *data, size_t maxdatalen, size_t *datalen, uint8_t *delimeter, size_t delimeterlen) {
|
||||
CborValue array;
|
||||
if (datalen)
|
||||
*datalen = 0;
|
||||
|
||||
size_t slen = maxdatalen;
|
||||
size_t totallen = 0;
|
||||
|
||||
CborError res = cbor_value_enter_container(elm, &array);
|
||||
cbor_check(res);
|
||||
|
||||
while (!cbor_value_at_end(&array)) {
|
||||
res = cbor_value_copy_byte_string(&array, &data[totallen], &slen, &array);
|
||||
cbor_check(res);
|
||||
|
||||
totallen += slen;
|
||||
if (delimeter) {
|
||||
memcpy(&data[totallen], delimeter, delimeterlen);
|
||||
totallen += delimeterlen;
|
||||
}
|
||||
slen = maxdatalen - totallen;
|
||||
}
|
||||
|
||||
res = cbor_value_leave_container(elm, &array);
|
||||
cbor_check(res);
|
||||
|
||||
if (datalen)
|
||||
*datalen = totallen;
|
||||
|
||||
return CborNoError;
|
||||
};
|
||||
|
||||
CborError CborGetBinStringValue(CborValue *elm, uint8_t *data, size_t maxdatalen, size_t *datalen) {
|
||||
if (datalen)
|
||||
*datalen = 0;
|
||||
|
||||
size_t slen = maxdatalen;
|
||||
|
||||
CborError res = cbor_value_copy_byte_string(elm, data, &slen, elm);
|
||||
cbor_check(res);
|
||||
|
||||
if (datalen)
|
||||
*datalen = slen;
|
||||
|
||||
return CborNoError;
|
||||
};
|
||||
|
||||
CborError CborGetArrayStringValue(CborValue *elm, char *data, size_t maxdatalen, size_t *datalen, char *delimeter) {
|
||||
CborValue array;
|
||||
if (datalen)
|
||||
*datalen = 0;
|
||||
|
||||
size_t slen = maxdatalen;
|
||||
size_t totallen = 0;
|
||||
|
||||
CborError res = cbor_value_enter_container(elm, &array);
|
||||
cbor_check(res);
|
||||
|
||||
while (!cbor_value_at_end(&array)) {
|
||||
res = cbor_value_copy_text_string(&array, &data[totallen], &slen, &array);
|
||||
cbor_check(res);
|
||||
|
||||
totallen += slen;
|
||||
if (delimeter) {
|
||||
strcat(data, delimeter);
|
||||
totallen += strlen(delimeter);
|
||||
}
|
||||
slen = maxdatalen - totallen;
|
||||
data[totallen] = 0x00;
|
||||
}
|
||||
|
||||
res = cbor_value_leave_container(elm, &array);
|
||||
cbor_check(res);
|
||||
|
||||
if (datalen)
|
||||
*datalen = totallen;
|
||||
|
||||
return CborNoError;
|
||||
};
|
||||
|
||||
CborError CborGetStringValue(CborValue *elm, char *data, size_t maxdatalen, size_t *datalen) {
|
||||
if (datalen)
|
||||
*datalen = 0;
|
||||
|
||||
size_t slen = maxdatalen;
|
||||
|
||||
CborError res = cbor_value_copy_text_string(elm, data, &slen, elm);
|
||||
cbor_check(res);
|
||||
|
||||
if (datalen)
|
||||
*datalen = slen;
|
||||
|
||||
return CborNoError;
|
||||
};
|
||||
|
||||
CborError CborGetStringValueBuf(CborValue *elm) {
|
||||
static char stringBuf[2048];
|
||||
memset(stringBuf, 0x00, sizeof(stringBuf));
|
||||
|
||||
return CborGetStringValue(elm, stringBuf, sizeof(stringBuf), NULL);
|
||||
};
|
||||
|
||||
int CBOREncodeElm(json_t *root, char *rootElmId, CborEncoder *encoder) {
|
||||
json_t *elm = NULL;
|
||||
if (rootElmId && strlen(rootElmId) && rootElmId[0] == '$')
|
||||
elm = json_path_get(root, rootElmId);
|
||||
else
|
||||
elm = json_object_get(root, rootElmId);
|
||||
|
||||
if (!elm)
|
||||
return 1;
|
||||
|
||||
int res = JsonToCbor(elm, encoder);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
CborError CBOREncodeClientDataHash(json_t *root, CborEncoder *encoder) {
|
||||
uint8_t buf[100] = {0};
|
||||
size_t jlen;
|
||||
|
||||
JsonLoadBufAsHex(root, "$.ClientDataHash", buf, sizeof(buf), &jlen);
|
||||
|
||||
// fill with 0x00 if not found
|
||||
if (!jlen)
|
||||
jlen = 32;
|
||||
|
||||
int res = cbor_encode_byte_string(encoder, buf, jlen);
|
||||
cbor_check(res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
// Tools for work with CBOR format http://cbor.io/spec.html
|
||||
// via Intel tinycbor (https://github.com/intel/tinycbor) library
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#ifndef __CBORTOOLS_H__
|
||||
#define __CBORTOOLS_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <jansson.h>
|
||||
#include <cbor.h>
|
||||
|
||||
#define cbor_check_if(r) if ((r) != CborNoError) {return r;} else
|
||||
#define cbor_check(r) if ((r) != CborNoError) return r;
|
||||
|
||||
extern int TinyCborPrintFIDOPackage(uint8_t cmdCode, bool isResponse, uint8_t *data, size_t length);
|
||||
extern int JsonToCbor(json_t *elm, CborEncoder *encoder);
|
||||
|
||||
extern int CborMapGetKeyById(CborParser *parser, CborValue *map, uint8_t *data, size_t dataLen, int key);
|
||||
extern CborError CborGetArrayBinStringValue(CborValue *elm, uint8_t *data, size_t maxdatalen, size_t *datalen);
|
||||
extern CborError CborGetArrayBinStringValueEx(CborValue *elm, uint8_t *data, size_t maxdatalen, size_t *datalen, uint8_t *delimeter, size_t delimeterlen);
|
||||
extern CborError CborGetBinStringValue(CborValue *elm, uint8_t *data, size_t maxdatalen, size_t *datalen);
|
||||
extern CborError CborGetArrayStringValue(CborValue *elm, char *data, size_t maxdatalen, size_t *datalen, char *delimeter);
|
||||
extern CborError CborGetStringValue(CborValue *elm, char *data, size_t maxdatalen, size_t *datalen);
|
||||
extern CborError CborGetStringValueBuf(CborValue *elm);
|
||||
|
||||
extern int CBOREncodeElm(json_t *root, char *rootElmId, CborEncoder *encoder);
|
||||
extern CborError CBOREncodeClientDataHash(json_t *root, CborEncoder *encoder);
|
||||
|
||||
#endif /* __CBORTOOLS_H__ */
|
||||
@@ -0,0 +1,239 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
// Tools for work with COSE (CBOR Object Signing and Encryption) rfc8152
|
||||
// https://tools.ietf.org/html/rfc8152
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "cose.h"
|
||||
#include <cbor.h>
|
||||
#include "cbortools.h"
|
||||
#include "util.h"
|
||||
|
||||
static const char COSEEmptyStr[] = "";
|
||||
|
||||
typedef struct {
|
||||
int Value;
|
||||
char *Name;
|
||||
char *Description;
|
||||
} COSEValueNameDesc_t;
|
||||
|
||||
typedef struct {
|
||||
int Value;
|
||||
char *Type;
|
||||
char *Name;
|
||||
char *Description;
|
||||
} COSEValueTypeNameDesc_t;
|
||||
|
||||
// kty - Key Type Values
|
||||
COSEValueNameDesc_t COSEKeyTypeValueDesc[] = {
|
||||
{0, "Reserved", "Reserved"},
|
||||
{1, "OKP", "Octet Key Pair"},
|
||||
{2, "EC2", "Elliptic Curve Key w/ x- and y-coordinate pair"},
|
||||
{4, "Symmetric", "Symmetric Key"},
|
||||
};
|
||||
|
||||
COSEValueNameDesc_t *GetCOSEktyElm(int id) {
|
||||
for (int i = 0; i < ARRAYLEN(COSEKeyTypeValueDesc); i++)
|
||||
if (COSEKeyTypeValueDesc[i].Value == id)
|
||||
return &COSEKeyTypeValueDesc[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *GetCOSEktyDescription(int id) {
|
||||
COSEValueNameDesc_t *elm = GetCOSEktyElm(id);
|
||||
if (elm)
|
||||
return elm->Description;
|
||||
return COSEEmptyStr;
|
||||
}
|
||||
|
||||
// keys
|
||||
COSEValueTypeNameDesc_t COSECurvesDesc[] = {
|
||||
{1, "EC2", "P-256", "NIST P-256 also known as secp256r1"},
|
||||
{2, "EC2", "P-384", "NIST P-384 also known as secp384r1"},
|
||||
{3, "EC2", "P-521", "NIST P-521 also known as secp521r1"},
|
||||
{4, "OKP", "X25519", "X25519 for use w/ ECDH only"},
|
||||
{5, "OKP", "X448", "X448 for use w/ ECDH only"},
|
||||
{6, "OKP", "Ed25519", "Ed25519 for use w/ EdDSA only"},
|
||||
{7, "OKP", "Ed448", "Ed448 for use w/ EdDSA only"},
|
||||
};
|
||||
|
||||
COSEValueTypeNameDesc_t *GetCOSECurveElm(int id) {
|
||||
for (int i = 0; i < ARRAYLEN(COSECurvesDesc); i++)
|
||||
if (COSECurvesDesc[i].Value == id)
|
||||
return &COSECurvesDesc[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *GetCOSECurveDescription(int id) {
|
||||
COSEValueTypeNameDesc_t *elm = GetCOSECurveElm(id);
|
||||
if (elm)
|
||||
return elm->Description;
|
||||
return COSEEmptyStr;
|
||||
}
|
||||
|
||||
// RFC8152 https://www.iana.org/assignments/cose/cose.xhtml#algorithms
|
||||
COSEValueNameDesc_t COSEAlg[] = {
|
||||
{-65536, "Unassigned", "Unassigned"},
|
||||
{-65535, "RS1", "RSASSA-PKCS1-v1_5 w/ SHA-1"},
|
||||
{-259, "RS512", "RSASSA-PKCS1-v1_5 w/ SHA-512"},
|
||||
{-258, "RS384", "RSASSA-PKCS1-v1_5 w/ SHA-384"},
|
||||
{-257, "RS256", "RSASSA-PKCS1-v1_5 w/ SHA-256"},
|
||||
{-42, "RSAES-OAEP w/ SHA-512", "RSAES-OAEP w/ SHA-512"},
|
||||
{-41, "RSAES-OAEP w/ SHA-256", "RSAES-OAEP w/ SHA-256"},
|
||||
{-40, "RSAES-OAEP w/ RFC 8017 def param", "RSAES-OAEP w/ SHA-1"},
|
||||
{-39, "PS512", "RSASSA-PSS w/ SHA-512"},
|
||||
{-38, "PS384", "RSASSA-PSS w/ SHA-384"},
|
||||
{-37, "PS256", "RSASSA-PSS w/ SHA-256"},
|
||||
{-36, "ES512", "ECDSA w/ SHA-512"},
|
||||
{-35, "ES384", "ECDSA w/ SHA-384"},
|
||||
{-34, "ECDH-SS + A256KW", "ECDH SS w/ Concat KDF and AES Key Wrap w/ 256-bit key"},
|
||||
{-33, "ECDH-SS + A192KW", "ECDH SS w/ Concat KDF and AES Key Wrap w/ 192-bit key"},
|
||||
{-32, "ECDH-SS + A128KW", "ECDH SS w/ Concat KDF and AES Key Wrap w/ 128-bit key"},
|
||||
{-31, "ECDH-ES + A256KW", "ECDH ES w/ Concat KDF and AES Key Wrap w/ 256-bit key"},
|
||||
{-30, "ECDH-ES + A192KW", "ECDH ES w/ Concat KDF and AES Key Wrap w/ 192-bit key"},
|
||||
{-29, "ECDH-ES + A128KW", "ECDH ES w/ Concat KDF and AES Key Wrap w/ 128-bit key"},
|
||||
{-28, "ECDH-SS + HKDF-512", "ECDH SS w/ HKDF - generate key directly"},
|
||||
{-27, "ECDH-SS + HKDF-256", "ECDH SS w/ HKDF - generate key directly"},
|
||||
{-26, "ECDH-ES + HKDF-512", "ECDH ES w/ HKDF - generate key directly"},
|
||||
{-25, "ECDH-ES + HKDF-256", "ECDH ES w/ HKDF - generate key directly"},
|
||||
{-13, "direct+HKDF-AES-256", "Shared secret w/ AES-MAC 256-bit key"},
|
||||
{-12, "direct+HKDF-AES-128", "Shared secret w/ AES-MAC 128-bit key"},
|
||||
{-11, "direct+HKDF-SHA-512", "Shared secret w/ HKDF and SHA-512"},
|
||||
{-10, "direct+HKDF-SHA-256", "Shared secret w/ HKDF and SHA-256"},
|
||||
{-8, "EdDSA", "EdDSA"},
|
||||
{-7, "ES256", "ECDSA w/ SHA-256"},
|
||||
{-6, "direct", "Direct use of CEK"},
|
||||
{-5, "A256KW", "AES Key Wrap w/ 256-bit key"},
|
||||
{-4, "A192KW", "AES Key Wrap w/ 192-bit key"},
|
||||
{-3, "A128KW", "AES Key Wrap w/ 128-bit key"},
|
||||
{0, "Reserved", "Reserved"},
|
||||
{1, "A128GCM", "AES-GCM mode w/ 128-bit key, 128-bit tag"},
|
||||
{2, "A192GCM", "AES-GCM mode w/ 192-bit key, 128-bit tag"},
|
||||
{3, "A256GCM", "AES-GCM mode w/ 256-bit key, 128-bit tag"},
|
||||
{4, "HMAC 256/64", "HMAC w/ SHA-256 truncated to 64 bits"},
|
||||
{5, "HMAC 256/256", "HMAC w/ SHA-256"},
|
||||
{6, "HMAC 384/384", "HMAC w/ SHA-384"},
|
||||
{7, "HMAC 512/512", "HMAC w/ SHA-512"},
|
||||
{10, "AES-CCM-16-64-128", "AES-CCM mode 128-bit key, 64-bit tag, 13-byte nonce"},
|
||||
{11, "AES-CCM-16-64-256", "AES-CCM mode 256-bit key, 64-bit tag, 13-byte nonce"},
|
||||
{12, "AES-CCM-64-64-128", "AES-CCM mode 128-bit key, 64-bit tag, 7-byte nonce"},
|
||||
{13, "AES-CCM-64-64-256", "AES-CCM mode 256-bit key, 64-bit tag, 7-byte nonce"},
|
||||
{14, "AES-MAC 128/64", "AES-MAC 128-bit key, 64-bit tag"},
|
||||
{15, "AES-MAC 256/64", "AES-MAC 256-bit key, 64-bit tag"},
|
||||
{24, "ChaCha20/Poly1305", "ChaCha20/Poly1305 w/ 256-bit key, 128-bit tag"},
|
||||
{25, "AES-MAC 128/128", "AES-MAC 128-bit key, 128-bit tag"},
|
||||
{26, "AES-MAC 256/128", "AES-MAC 256-bit key, 128-bit tag"},
|
||||
{30, "AES-CCM-16-128-128", "AES-CCM mode 128-bit key, 128-bit tag, 13-byte nonce"},
|
||||
{31, "AES-CCM-16-128-256", "AES-CCM mode 256-bit key, 128-bit tag, 13-byte nonce"},
|
||||
{32, "AES-CCM-64-128-128", "AES-CCM mode 128-bit key, 128-bit tag, 7-byte nonce"},
|
||||
{33, "AES-CCM-64-128-256", "AES-CCM mode 256-bit key, 128-bit tag, 7-byte nonce"}
|
||||
};
|
||||
|
||||
COSEValueNameDesc_t *GetCOSEAlgElm(int id) {
|
||||
for (int i = 0; i < ARRAYLEN(COSEAlg); i++)
|
||||
if (COSEAlg[i].Value == id)
|
||||
return &COSEAlg[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *GetCOSEAlgName(int id) {
|
||||
COSEValueNameDesc_t *elm = GetCOSEAlgElm(id);
|
||||
if (elm)
|
||||
return elm->Name;
|
||||
return COSEEmptyStr;
|
||||
}
|
||||
|
||||
const char *GetCOSEAlgDescription(int id) {
|
||||
COSEValueNameDesc_t *elm = GetCOSEAlgElm(id);
|
||||
if (elm)
|
||||
return elm->Description;
|
||||
return COSEEmptyStr;
|
||||
}
|
||||
|
||||
int COSEGetECDSAKey(uint8_t *data, size_t datalen, bool verbose, uint8_t *public_key) {
|
||||
CborParser parser;
|
||||
CborValue map;
|
||||
int64_t i64;
|
||||
size_t len;
|
||||
|
||||
if(verbose)
|
||||
PrintAndLog("----------- CBOR decode ----------------");
|
||||
|
||||
// kty
|
||||
int res = CborMapGetKeyById(&parser, &map, data, datalen, 1);
|
||||
if(!res) {
|
||||
cbor_value_get_int64(&map, &i64);
|
||||
if(verbose)
|
||||
PrintAndLog("kty [%lld] %s", (long long)i64, GetCOSEktyDescription(i64));
|
||||
if (i64 != 2)
|
||||
PrintAndLog("ERROR: kty must be 2.");
|
||||
}
|
||||
|
||||
// algorithm
|
||||
res = CborMapGetKeyById(&parser, &map, data, datalen, 3);
|
||||
if(!res) {
|
||||
cbor_value_get_int64(&map, &i64);
|
||||
if(verbose)
|
||||
PrintAndLog("algorithm [%lld] %s", (long long)i64, GetCOSEAlgDescription(i64));
|
||||
if (i64 != -7)
|
||||
PrintAndLog("ERROR: algorithm must be -7.");
|
||||
}
|
||||
|
||||
// curve
|
||||
res = CborMapGetKeyById(&parser, &map, data, datalen, -1);
|
||||
if(!res) {
|
||||
cbor_value_get_int64(&map, &i64);
|
||||
if(verbose)
|
||||
PrintAndLog("curve [%lld] %s", (long long)i64, GetCOSECurveDescription(i64));
|
||||
if (i64 != 1)
|
||||
PrintAndLog("ERROR: curve must be 1.");
|
||||
}
|
||||
|
||||
// plain key
|
||||
public_key[0] = 0x04;
|
||||
|
||||
// x - coordinate
|
||||
res = CborMapGetKeyById(&parser, &map, data, datalen, -2);
|
||||
if(!res) {
|
||||
res = CborGetBinStringValue(&map, &public_key[1], 32, &len);
|
||||
cbor_check(res);
|
||||
if(verbose)
|
||||
PrintAndLog("x - coordinate [%d]: %s", len, sprint_hex(&public_key[1], 32));
|
||||
if (len != 32)
|
||||
PrintAndLog("ERROR: x - coordinate length must be 32.");
|
||||
}
|
||||
|
||||
// y - coordinate
|
||||
res = CborMapGetKeyById(&parser, &map, data, datalen, -3);
|
||||
if(!res) {
|
||||
res = CborGetBinStringValue(&map, &public_key[33], 32, &len);
|
||||
cbor_check(res);
|
||||
if(verbose)
|
||||
PrintAndLog("y - coordinate [%d]: %s", len, sprint_hex(&public_key[33], 32));
|
||||
if (len != 32)
|
||||
PrintAndLog("ERROR: y - coordinate length must be 32.");
|
||||
}
|
||||
|
||||
// d - private key
|
||||
uint8_t private_key[128] = {0};
|
||||
res = CborMapGetKeyById(&parser, &map, data, datalen, -4);
|
||||
if(!res) {
|
||||
res = CborGetBinStringValue(&map, private_key, sizeof(private_key), &len);
|
||||
cbor_check(res);
|
||||
if(verbose)
|
||||
PrintAndLog("d - private key [%d]: %s", len, sprint_hex(private_key, len));
|
||||
}
|
||||
|
||||
if(verbose)
|
||||
PrintAndLog("----------- CBOR decode ----------------");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
//-----------------------------------------------------------------------------
|
||||
// Tools for work with COSE (CBOR Object Signing and Encryption) rfc8152
|
||||
// https://tools.ietf.org/html/rfc8152
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#ifndef __COSE_H__
|
||||
#define __COSE_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <cbor.h>
|
||||
|
||||
extern const char *GetCOSEAlgName(int id);
|
||||
extern const char *GetCOSEAlgDescription(int id);
|
||||
extern const char *GetCOSEktyDescription(int id);
|
||||
extern const char *GetCOSECurveDescription(int id);
|
||||
|
||||
extern int COSEGetECDSAKey(uint8_t *data, size_t datalen, bool verbose, uint8_t *public_key);
|
||||
|
||||
#endif /* __COSE_H__ */
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"ClientDataHash": "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f",
|
||||
"RelyingPartyEntity": {
|
||||
"id": "acme.com",
|
||||
"name": "Acme"
|
||||
},
|
||||
"UserEntity": {
|
||||
"id": "00000000000000000000000000000001",
|
||||
"icon": "https://pics.acme.com/00/p/aBjjjpqPb.png",
|
||||
"name": "johnpsmith@acme.com",
|
||||
"displayName": "John P. Smith"
|
||||
},
|
||||
"pubKeyCredParams": [
|
||||
{
|
||||
"type": "public-key",
|
||||
"alg": -7,
|
||||
".name": "ES256"
|
||||
},
|
||||
{
|
||||
"type": "public-key",
|
||||
"alg": -257,
|
||||
".name": "RS256"
|
||||
}
|
||||
],
|
||||
"MakeCredentialOptions": {
|
||||
"uv": false,
|
||||
"rk": true
|
||||
},
|
||||
"GetAssertionOptions": {
|
||||
"up": true,
|
||||
"uv": false
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,57 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
// FIDO2 authenticators core data and commands
|
||||
// https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
#ifndef __FIDOCORE_H__
|
||||
#define __FIDOCORE_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <jansson.h>
|
||||
#include "cmdhf14a.h"
|
||||
|
||||
typedef enum {
|
||||
fido2CmdMakeCredential = 0x01,
|
||||
fido2CmdGetAssertion = 0x02,
|
||||
fido2CmdCancel = 0x03,
|
||||
fido2CmdGetInfo = 0x04,
|
||||
fido2CmdClientPIN = 0x06,
|
||||
fido2CmdReset = 0x07,
|
||||
fido2CmdGetNextAssertion = 0x08,
|
||||
|
||||
// another data
|
||||
fido2COSEKey = 0xF0
|
||||
} fido2Commands;
|
||||
|
||||
typedef enum {
|
||||
ptQuery,
|
||||
ptResponse,
|
||||
} fido2PacketType;
|
||||
|
||||
extern int FIDOSelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
|
||||
extern int FIDOExchange(sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
|
||||
extern int FIDORegister(uint8_t *params, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
|
||||
extern int FIDOAuthentication(uint8_t *params, uint8_t paramslen, uint8_t controlb, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
|
||||
extern int FIDO2GetInfo(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
|
||||
extern int FIDO2MakeCredential(uint8_t *params, uint8_t paramslen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
|
||||
extern int FIDO2GetAssertion(uint8_t *params, uint8_t paramslen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
|
||||
|
||||
extern int FIDOCheckDERAndGetKey(uint8_t *der, size_t derLen, bool verbose, uint8_t *publicKey, size_t publicKeyMaxLen);
|
||||
|
||||
extern char *fido2GetCmdMemberDescription(uint8_t cmdCode, bool isResponse, int memberNum);
|
||||
extern char *fido2GetCmdErrorDescription(uint8_t errorCode);
|
||||
|
||||
extern bool CheckrpIdHash(json_t *json, uint8_t *hash);
|
||||
extern int FIDO2CreateMakeCredentionalReq(json_t *root, uint8_t *data, size_t maxdatalen, size_t *datalen);
|
||||
extern int FIDO2MakeCredentionalParseRes(json_t *root, uint8_t *data, size_t dataLen, bool verbose, bool verbose2, bool showCBOR, bool showDERTLV);
|
||||
extern int FIDO2CreateGetAssertionReq(json_t *root, uint8_t *data, size_t maxdatalen, size_t *datalen, bool createAllowList);
|
||||
extern int FIDO2GetAssertionParseRes(json_t *root, uint8_t *data, size_t dataLen, bool verbose, bool verbose2, bool showCBOR);
|
||||
|
||||
#endif /* __FIDOCORE_H__ */
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
LIB_A = tinycbor.a
|
||||
tinycbor_SOURCES = \
|
||||
cborencoder.c \
|
||||
cborencoder_close_container_checked.c \
|
||||
cborerrorstrings.c \
|
||||
cborparser.c \
|
||||
cborparser_dup_string.c \
|
||||
cborpretty.c \
|
||||
cbortojson.c \
|
||||
cborvalidation.c \
|
||||
|
||||
CFILES = $(filter %.c, $(tinycbor_SOURCES))
|
||||
CMDOBJS = $(CFILES:%.c=%.o)
|
||||
CLEAN = $(CMDOBJS)
|
||||
|
||||
CC= gcc
|
||||
CFLAGS= -O2 -Wall -Wno-unused-variable -Wno-unused-function
|
||||
LIBS= -lm $(SYSLIBS) $(MYLIBS)
|
||||
DEFAULT_INCLUDES = -I. -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,57 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 Intel Corporation
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is
|
||||
** furnished to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in
|
||||
** all copies or substantial portions of the Software.
|
||||
**
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
** THE SOFTWARE.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#define _BSD_SOURCE 1
|
||||
#define _DEFAULT_SOURCE 1
|
||||
#ifndef __STDC_LIMIT_MACROS
|
||||
# define __STDC_LIMIT_MACROS 1
|
||||
#endif
|
||||
|
||||
#include "cbor.h"
|
||||
|
||||
/**
|
||||
* \addtogroup CborEncoding
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* Closes the CBOR container (array or map) provided by \a containerEncoder and
|
||||
* updates the CBOR stream provided by \a encoder. Both parameters must be the
|
||||
* same as were passed to cbor_encoder_create_array() or
|
||||
* cbor_encoder_create_map().
|
||||
*
|
||||
* Prior to version 0.5, cbor_encoder_close_container() did not check the
|
||||
* number of items added. Since that version, it does and now
|
||||
* cbor_encoder_close_container_checked() is no longer needed.
|
||||
*
|
||||
* \sa cbor_encoder_create_array(), cbor_encoder_create_map()
|
||||
*/
|
||||
CborError cbor_encoder_close_container_checked(CborEncoder *encoder, const CborEncoder *containerEncoder)
|
||||
{
|
||||
return cbor_encoder_close_container(encoder, containerEncoder);
|
||||
}
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,182 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 Intel Corporation
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is
|
||||
** furnished to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in
|
||||
** all copies or substantial portions of the Software.
|
||||
**
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
** THE SOFTWARE.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "cbor.h"
|
||||
|
||||
#ifndef _
|
||||
# define _(msg) msg
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \enum CborError
|
||||
* \ingroup CborGlobals
|
||||
* The CborError enum contains the possible error values used by the CBOR encoder and decoder.
|
||||
*
|
||||
* TinyCBOR functions report success by returning CborNoError, or one error
|
||||
* condition by returning one of the values below. One exception is the
|
||||
* out-of-memory condition (CborErrorOutOfMemory), which the functions for \ref
|
||||
* CborEncoding may report in bit-wise OR with other conditions.
|
||||
*
|
||||
* This technique allows code to determine whether the only error condition was
|
||||
* a lack of buffer space, which may not be a fatal condition if the buffer can
|
||||
* be resized. Additionally, the functions for \ref CborEncoding may continue
|
||||
* to be used even after CborErrorOutOfMemory is returned, and instead they
|
||||
* will simply calculate the extra space needed.
|
||||
*
|
||||
* \value CborNoError No error occurred
|
||||
* \omitvalue CborUnknownError
|
||||
* \value CborErrorUnknownLength Request for the length of an array, map or string whose length is not provided in the CBOR stream
|
||||
* \value CborErrorAdvancePastEOF Not enough data in the stream to decode item (decoding would advance past end of stream)
|
||||
* \value CborErrorIO An I/O error occurred, probably due to an out-of-memory situation
|
||||
* \value CborErrorGarbageAtEnd Bytes exist past the end of the CBOR stream
|
||||
* \value CborErrorUnexpectedEOF End of stream reached unexpectedly
|
||||
* \value CborErrorUnexpectedBreak A CBOR break byte was found where not expected
|
||||
* \value CborErrorUnknownType An unknown type (future extension to CBOR) was found in the stream
|
||||
* \value CborErrorIllegalType An invalid type was found while parsing a chunked CBOR string
|
||||
* \value CborErrorIllegalNumber An illegal initial byte (encoding unspecified additional information) was found
|
||||
* \value CborErrorIllegalSimpleType An illegal encoding of a CBOR Simple Type of value less than 32 was found
|
||||
* \omitvalue CborErrorUnknownSimpleType
|
||||
* \omitvalue CborErrorUnknownTag
|
||||
* \omitvalue CborErrorInappropriateTagForType
|
||||
* \omitvalue CborErrorDuplicateObjectKeys
|
||||
* \value CborErrorInvalidUtf8TextString Illegal UTF-8 encoding found while parsing CBOR Text String
|
||||
* \value CborErrorTooManyItems Too many items were added to CBOR map or array of pre-determined length
|
||||
* \value CborErrorTooFewItems Too few items were added to CBOR map or array of pre-determined length
|
||||
* \value CborErrorDataTooLarge Data item size exceeds TinyCBOR's implementation limits
|
||||
* \value CborErrorNestingTooDeep Data item nesting exceeds TinyCBOR's implementation limits
|
||||
* \omitvalue CborErrorUnsupportedType
|
||||
* \value CborErrorJsonObjectKeyIsAggregate Conversion to JSON failed because the key in a map is a CBOR map or array
|
||||
* \value CborErrorJsonObjectKeyNotString Conversion to JSON failed because the key in a map is not a text string
|
||||
* \value CborErrorOutOfMemory During CBOR encoding, the buffer provided is insufficient for encoding the data item;
|
||||
* in other situations, TinyCBOR failed to allocate memory
|
||||
* \value CborErrorInternalError An internal error occurred in TinyCBOR
|
||||
*/
|
||||
|
||||
/**
|
||||
* \ingroup CborGlobals
|
||||
* Returns the error string corresponding to the CBOR error condition \a error.
|
||||
*/
|
||||
const char *cbor_error_string(CborError error)
|
||||
{
|
||||
switch (error) {
|
||||
case CborNoError:
|
||||
return "";
|
||||
|
||||
case CborUnknownError:
|
||||
return _("unknown error");
|
||||
|
||||
case CborErrorOutOfMemory:
|
||||
return _("out of memory/need more memory");
|
||||
|
||||
case CborErrorUnknownLength:
|
||||
return _("unknown length (attempted to get the length of a map/array/string of indeterminate length");
|
||||
|
||||
case CborErrorAdvancePastEOF:
|
||||
return _("attempted to advance past EOF");
|
||||
|
||||
case CborErrorIO:
|
||||
return _("I/O error");
|
||||
|
||||
case CborErrorGarbageAtEnd:
|
||||
return _("garbage after the end of the content");
|
||||
|
||||
case CborErrorUnexpectedEOF:
|
||||
return _("unexpected end of data");
|
||||
|
||||
case CborErrorUnexpectedBreak:
|
||||
return _("unexpected 'break' byte");
|
||||
|
||||
case CborErrorUnknownType:
|
||||
return _("illegal byte (encodes future extension type)");
|
||||
|
||||
case CborErrorIllegalType:
|
||||
return _("mismatched string type in chunked string");
|
||||
|
||||
case CborErrorIllegalNumber:
|
||||
return _("illegal initial byte (encodes unspecified additional information)");
|
||||
|
||||
case CborErrorIllegalSimpleType:
|
||||
return _("illegal encoding of simple type smaller than 32");
|
||||
|
||||
case CborErrorUnknownSimpleType:
|
||||
return _("unknown simple type");
|
||||
|
||||
case CborErrorUnknownTag:
|
||||
return _("unknown tag");
|
||||
|
||||
case CborErrorInappropriateTagForType:
|
||||
return _("inappropriate tag for type");
|
||||
|
||||
case CborErrorDuplicateObjectKeys:
|
||||
return _("duplicate keys in object");
|
||||
|
||||
case CborErrorInvalidUtf8TextString:
|
||||
return _("invalid UTF-8 content in string");
|
||||
|
||||
case CborErrorExcludedType:
|
||||
return _("excluded type found");
|
||||
|
||||
case CborErrorExcludedValue:
|
||||
return _("excluded value found");
|
||||
|
||||
case CborErrorImproperValue:
|
||||
case CborErrorOverlongEncoding:
|
||||
return _("value encoded in non-canonical form");
|
||||
|
||||
case CborErrorMapKeyNotString:
|
||||
case CborErrorJsonObjectKeyNotString:
|
||||
return _("key in map is not a string");
|
||||
|
||||
case CborErrorMapNotSorted:
|
||||
return _("map is not sorted");
|
||||
|
||||
case CborErrorMapKeysNotUnique:
|
||||
return _("map keys are not unique");
|
||||
|
||||
case CborErrorTooManyItems:
|
||||
return _("too many items added to encoder");
|
||||
|
||||
case CborErrorTooFewItems:
|
||||
return _("too few items added to encoder");
|
||||
|
||||
case CborErrorDataTooLarge:
|
||||
return _("internal error: data too large");
|
||||
|
||||
case CborErrorNestingTooDeep:
|
||||
return _("internal error: too many nested containers found in recursive function");
|
||||
|
||||
case CborErrorUnsupportedType:
|
||||
return _("unsupported type");
|
||||
|
||||
case CborErrorJsonObjectKeyIsAggregate:
|
||||
return _("conversion to JSON failed: key in object is an array or map");
|
||||
|
||||
case CborErrorJsonNotImplemented:
|
||||
return _("conversion to JSON failed: open_memstream unavailable");
|
||||
|
||||
case CborErrorInternalError:
|
||||
return _("internal error");
|
||||
}
|
||||
return cbor_error_string(CborUnknownError);
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2017 Intel Corporation
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is
|
||||
** furnished to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in
|
||||
** all copies or substantial portions of the Software.
|
||||
**
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
** THE SOFTWARE.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CBORINTERNAL_P_H
|
||||
#define CBORINTERNAL_P_H
|
||||
|
||||
#include "compilersupport_p.h"
|
||||
|
||||
#ifndef CBOR_NO_FLOATING_POINT
|
||||
# include <float.h>
|
||||
# include <math.h>
|
||||
#else
|
||||
# ifndef CBOR_NO_HALF_FLOAT_TYPE
|
||||
# define CBOR_NO_HALF_FLOAT_TYPE 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef CBOR_NO_HALF_FLOAT_TYPE
|
||||
# ifdef __F16C__
|
||||
# include <immintrin.h>
|
||||
static inline unsigned short encode_half(double val)
|
||||
{
|
||||
return _cvtss_sh((float)val, 3);
|
||||
}
|
||||
static inline double decode_half(unsigned short half)
|
||||
{
|
||||
return _cvtsh_ss(half);
|
||||
}
|
||||
# else
|
||||
/* software implementation of float-to-fp16 conversions */
|
||||
static inline unsigned short encode_half(double val)
|
||||
{
|
||||
uint64_t v;
|
||||
int sign, exp, mant;
|
||||
memcpy(&v, &val, sizeof(v));
|
||||
sign = v >> 63 << 15;
|
||||
exp = (v >> 52) & 0x7ff;
|
||||
mant = v << 12 >> 12 >> (53-11); /* keep only the 11 most significant bits of the mantissa */
|
||||
exp -= 1023;
|
||||
if (exp == 1024) {
|
||||
/* infinity or NaN */
|
||||
exp = 16;
|
||||
mant >>= 1;
|
||||
} else if (exp >= 16) {
|
||||
/* overflow, as largest number */
|
||||
exp = 15;
|
||||
mant = 1023;
|
||||
} else if (exp >= -14) {
|
||||
/* regular normal */
|
||||
} else if (exp >= -24) {
|
||||
/* subnormal */
|
||||
mant |= 1024;
|
||||
mant >>= -(exp + 14);
|
||||
exp = -15;
|
||||
} else {
|
||||
/* underflow, make zero */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* safe cast here as bit operations above guarantee not to overflow */
|
||||
return (unsigned short)(sign | ((exp + 15) << 10) | mant);
|
||||
}
|
||||
|
||||
/* this function was copied & adapted from RFC 7049 Appendix D */
|
||||
static inline double decode_half(unsigned short half)
|
||||
{
|
||||
int exp = (half >> 10) & 0x1f;
|
||||
int mant = half & 0x3ff;
|
||||
double val;
|
||||
if (exp == 0) val = ldexp(mant, -24);
|
||||
else if (exp != 31) val = ldexp(mant + 1024, exp - 25);
|
||||
else val = mant == 0 ? INFINITY : NAN;
|
||||
return half & 0x8000 ? -val : val;
|
||||
}
|
||||
# endif
|
||||
#endif /* CBOR_NO_HALF_FLOAT_TYPE */
|
||||
|
||||
#ifndef CBOR_INTERNAL_API
|
||||
# define CBOR_INTERNAL_API
|
||||
#endif
|
||||
|
||||
#ifndef CBOR_PARSER_MAX_RECURSIONS
|
||||
# define CBOR_PARSER_MAX_RECURSIONS 1024
|
||||
#endif
|
||||
|
||||
/*
|
||||
* CBOR Major types
|
||||
* Encoded in the high 3 bits of the descriptor byte
|
||||
* See http://tools.ietf.org/html/rfc7049#section-2.1
|
||||
*/
|
||||
typedef enum CborMajorTypes {
|
||||
UnsignedIntegerType = 0U,
|
||||
NegativeIntegerType = 1U,
|
||||
ByteStringType = 2U,
|
||||
TextStringType = 3U,
|
||||
ArrayType = 4U,
|
||||
MapType = 5U, /* a.k.a. object */
|
||||
TagType = 6U,
|
||||
SimpleTypesType = 7U
|
||||
} CborMajorTypes;
|
||||
|
||||
/*
|
||||
* CBOR simple and floating point types
|
||||
* Encoded in the low 8 bits of the descriptor byte when the
|
||||
* Major Type is 7.
|
||||
*/
|
||||
typedef enum CborSimpleTypes {
|
||||
FalseValue = 20,
|
||||
TrueValue = 21,
|
||||
NullValue = 22,
|
||||
UndefinedValue = 23,
|
||||
SimpleTypeInNextByte = 24, /* not really a simple type */
|
||||
HalfPrecisionFloat = 25, /* ditto */
|
||||
SinglePrecisionFloat = 26, /* ditto */
|
||||
DoublePrecisionFloat = 27, /* ditto */
|
||||
Break = 31
|
||||
} CborSimpleTypes;
|
||||
|
||||
enum {
|
||||
SmallValueBitLength = 5U,
|
||||
SmallValueMask = (1U << SmallValueBitLength) - 1, /* 31 */
|
||||
Value8Bit = 24U,
|
||||
Value16Bit = 25U,
|
||||
Value32Bit = 26U,
|
||||
Value64Bit = 27U,
|
||||
IndefiniteLength = 31U,
|
||||
|
||||
MajorTypeShift = SmallValueBitLength,
|
||||
MajorTypeMask = (int) (~0U << MajorTypeShift),
|
||||
|
||||
BreakByte = (unsigned)Break | (SimpleTypesType << MajorTypeShift)
|
||||
};
|
||||
|
||||
CBOR_INTERNAL_API CborError CBOR_INTERNAL_API_CC _cbor_value_extract_number(const uint8_t **ptr, const uint8_t *end, uint64_t *len);
|
||||
CBOR_INTERNAL_API CborError CBOR_INTERNAL_API_CC _cbor_value_prepare_string_iteration(CborValue *it);
|
||||
CBOR_INTERNAL_API CborError CBOR_INTERNAL_API_CC _cbor_value_get_string_chunk(const CborValue *value, const void **bufferptr,
|
||||
size_t *len, CborValue *next);
|
||||
|
||||
|
||||
#endif /* CBORINTERNAL_P_H */
|
||||
@@ -0,0 +1,62 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 Intel Corporation
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is
|
||||
** furnished to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in
|
||||
** all copies or substantial portions of the Software.
|
||||
**
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
** THE SOFTWARE.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CBORJSON_H
|
||||
#define CBORJSON_H
|
||||
|
||||
#include "cbor.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Conversion to JSON */
|
||||
enum CborToJsonFlags
|
||||
{
|
||||
CborConvertAddMetadata = 1,
|
||||
CborConvertTagsToObjects = 2,
|
||||
CborConvertIgnoreTags = 0,
|
||||
|
||||
CborConvertObeyByteStringTags = 0,
|
||||
CborConvertByteStringsToBase64Url = 4,
|
||||
|
||||
CborConvertRequireMapStringKeys = 0,
|
||||
CborConvertStringifyMapKeys = 8,
|
||||
|
||||
CborConvertDefaultFlags = 0
|
||||
};
|
||||
|
||||
CBOR_API CborError cbor_value_to_json_advance(FILE *out, CborValue *value, int flags);
|
||||
CBOR_INLINE_API CborError cbor_value_to_json(FILE *out, const CborValue *value, int flags)
|
||||
{
|
||||
CborValue copy = *value;
|
||||
return cbor_value_to_json_advance(out, ©, flags);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CBORJSON_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,119 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 Intel Corporation
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is
|
||||
** furnished to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in
|
||||
** all copies or substantial portions of the Software.
|
||||
**
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
** THE SOFTWARE.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef _BSD_SOURCE
|
||||
#define _BSD_SOURCE 1
|
||||
#endif
|
||||
#ifndef _DEFAULT_SOURCE
|
||||
#define _DEFAULT_SOURCE 1
|
||||
#endif
|
||||
#ifndef __STDC_LIMIT_MACROS
|
||||
# define __STDC_LIMIT_MACROS 1
|
||||
#endif
|
||||
|
||||
#include "cbor.h"
|
||||
#include "compilersupport_p.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* \fn CborError cbor_value_dup_text_string(const CborValue *value, char **buffer, size_t *buflen, CborValue *next)
|
||||
*
|
||||
* Allocates memory for the string pointed by \a value and copies it into this
|
||||
* buffer. The pointer to the buffer is stored in \a buffer and the number of
|
||||
* bytes copied is stored in \a buflen (those variables must not be NULL).
|
||||
*
|
||||
* If the iterator \a value does not point to a text string, the behaviour is
|
||||
* undefined, so checking with \ref cbor_value_get_type or \ref
|
||||
* cbor_value_is_text_string is recommended.
|
||||
*
|
||||
* If \c malloc returns a NULL pointer, this function will return error
|
||||
* condition \ref CborErrorOutOfMemory.
|
||||
*
|
||||
* On success, \c{*buffer} will contain a valid pointer that must be freed by
|
||||
* calling \c{free()}. This is the case even for zero-length strings.
|
||||
*
|
||||
* The \a next pointer, if not null, will be updated to point to the next item
|
||||
* after this string. If \a value points to the last item, then \a next will be
|
||||
* invalid.
|
||||
*
|
||||
* This function may not run in constant time (it will run in O(n) time on the
|
||||
* number of chunks). It requires constant memory (O(1)) in addition to the
|
||||
* malloc'ed block.
|
||||
*
|
||||
* \note This function does not perform UTF-8 validation on the incoming text
|
||||
* string.
|
||||
*
|
||||
* \sa cbor_value_get_text_string_chunk(), cbor_value_copy_text_string(), cbor_value_dup_byte_string()
|
||||
*/
|
||||
|
||||
/**
|
||||
* \fn CborError cbor_value_dup_byte_string(const CborValue *value, uint8_t **buffer, size_t *buflen, CborValue *next)
|
||||
*
|
||||
* Allocates memory for the string pointed by \a value and copies it into this
|
||||
* buffer. The pointer to the buffer is stored in \a buffer and the number of
|
||||
* bytes copied is stored in \a buflen (those variables must not be NULL).
|
||||
*
|
||||
* If the iterator \a value does not point to a byte string, the behaviour is
|
||||
* undefined, so checking with \ref cbor_value_get_type or \ref
|
||||
* cbor_value_is_byte_string is recommended.
|
||||
*
|
||||
* If \c malloc returns a NULL pointer, this function will return error
|
||||
* condition \ref CborErrorOutOfMemory.
|
||||
*
|
||||
* On success, \c{*buffer} will contain a valid pointer that must be freed by
|
||||
* calling \c{free()}. This is the case even for zero-length strings.
|
||||
*
|
||||
* The \a next pointer, if not null, will be updated to point to the next item
|
||||
* after this string. If \a value points to the last item, then \a next will be
|
||||
* invalid.
|
||||
*
|
||||
* This function may not run in constant time (it will run in O(n) time on the
|
||||
* number of chunks). It requires constant memory (O(1)) in addition to the
|
||||
* malloc'ed block.
|
||||
*
|
||||
* \sa cbor_value_get_text_string_chunk(), cbor_value_copy_byte_string(), cbor_value_dup_text_string()
|
||||
*/
|
||||
CborError _cbor_value_dup_string(const CborValue *value, void **buffer, size_t *buflen, CborValue *next)
|
||||
{
|
||||
CborError err;
|
||||
cbor_assert(buffer);
|
||||
cbor_assert(buflen);
|
||||
*buflen = SIZE_MAX;
|
||||
err = _cbor_value_copy_string(value, NULL, buflen, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
++*buflen;
|
||||
*buffer = malloc(*buflen);
|
||||
if (!*buffer) {
|
||||
/* out of memory */
|
||||
return CborErrorOutOfMemory;
|
||||
}
|
||||
err = _cbor_value_copy_string(value, *buffer, buflen, next);
|
||||
if (err) {
|
||||
free(*buffer);
|
||||
return err;
|
||||
}
|
||||
return CborNoError;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user