many changes: * fix CC malleability * extra validations for CCs such as requiring a signature and limiting types * different SIGHASH types for CC

This commit is contained in:
Scott Sadler
2018-03-31 23:20:03 -03:00
parent 9ef101bc21
commit 563581aff4
21 changed files with 215 additions and 116 deletions

View File

@@ -21,6 +21,7 @@ CRYPTOCONDITIONS_CORE=libcryptoconditions_core.la
libcryptoconditions_core_la_SOURCES = \
src/cryptoconditions.c \
src/utils.c \
src/include/cJSON.c \
src/include/sha256.c \
src/include/ed25519/src/keypair.c \

View File

@@ -16,6 +16,7 @@ struct CCType;
enum CCTypeId {
CC_Condition = -1,
CC_Preimage = 0,
CC_Prefix = 1,
CC_Threshold = 2,
@@ -31,19 +32,27 @@ enum CCTypeId {
typedef int (*VerifyEval)(struct CC *cond, void *context);
/*
* Crypto Condition
*/
typedef struct CC {
struct CCType *type;
union {
// public key types
struct { unsigned char *publicKey, *signature; };
// preimage
struct { unsigned char *preimage; size_t preimageLength; };
// threshold
struct { long threshold; int size; struct CC **subconditions; };
// prefix
struct { unsigned char *prefix; size_t prefixLength; struct CC *subcondition;
unsigned long maxMessageLength; };
// eval
struct { char method[64]; unsigned char *paramsBin; size_t paramsBinLength; };
struct { unsigned char fingerprint[32]; uint32_t subtypes; unsigned long cost; };
// anon
struct { unsigned char fingerprint[32]; uint32_t subtypes; unsigned long cost;
struct CCType *conditionType; };
};
} CC;
@@ -76,14 +85,15 @@ size_t cc_fulfillmentBinary(const CC *cond, unsigned char *buf, size_t
static int cc_secp256k1VerifyTreeMsg32(const CC *cond, const unsigned char *msg32);
struct CC* cc_conditionFromJSON(cJSON *params, unsigned char *err);
struct CC* cc_conditionFromJSONString(const unsigned char *json, unsigned char *err);
struct CC* cc_readConditionBinary(unsigned char *cond_bin, size_t cond_bin_len);
struct CC* cc_readFulfillmentBinary(unsigned char *ffill_bin, size_t ffill_bin_len);
struct CC* cc_readConditionBinary(const unsigned char *cond_bin, size_t cond_bin_len);
struct CC* cc_readFulfillmentBinary(const unsigned char *ffill_bin, size_t ffill_bin_len);
struct cJSON* cc_conditionToJSON(const CC *cond);
unsigned char* cc_conditionToJSONString(const CC *cond);
unsigned char* cc_conditionUri(const CC *cond);
unsigned char* cc_jsonRPC(unsigned char *request);
unsigned long cc_getCost(const CC *cond);
enum CCTypeId cc_typeId(const CC *cond);
char* cc_typeName(const CC *cond);
uint32_t cc_typeMask(const CC *cond);
void cc_free(struct CC *cond);

View File

@@ -11,22 +11,19 @@ struct CCType cc_anonType;
static CC *mkAnon(const Condition_t *asnCond) {
CCType *realType = getTypeByAsnEnum(asnCond->present);
if (!realType) {
printf("Unknown ASN type: %i", asnCond->present);
return 0;
}
CC *cond = calloc(1, sizeof(CC));
cond->type = (CCType*) calloc(1, sizeof(CCType));
*cond->type = cc_anonType;
strcpy(cond->type->name, realType->name);
cond->type->hasSubtypes = realType->hasSubtypes;
cond->type->typeId = realType->typeId;
cond->type->asnType = realType->asnType;
cond->type = &cc_anonType;
cond->conditionType = realType;
const CompoundSha256Condition_t *deets = &asnCond->choice.thresholdSha256;
memcpy(cond->fingerprint, deets->fingerprint.buf, 32);
cond->cost = deets->cost;
if (realType->hasSubtypes) {
if (realType->getSubtypes) {
cond->subtypes = fromAsnSubtypes(deets->subtypes);
}
return cond;
@@ -66,8 +63,6 @@ static Fulfillment_t *anonFulfillment(const CC *cond) {
static void anonFree(CC *cond) {
free(cond->type);
free(cond);
}
@@ -76,4 +71,4 @@ static int anonIsFulfilled(const CC *cond) {
}
struct CCType cc_anonType = { -1, "anon (a buffer large enough to accomodate any type name)", Condition_PR_NOTHING, 0, NULL, &anonFingerprint, &anonCost, &anonSubtypes, NULL, &anonToJSON, NULL, &anonFulfillment, &anonIsFulfilled, &anonFree };
struct CCType cc_anonType = { -1, "(anon)", Condition_PR_NOTHING, NULL, &anonFingerprint, &anonCost, &anonSubtypes, NULL, &anonToJSON, NULL, &anonFulfillment, &anonIsFulfilled, &anonFree };

View File

@@ -12,7 +12,6 @@
#include "src/anon.c"
#include "src/eval.c"
#include "src/json_rpc.c"
#include "src/utils.c"
#include <cJSON.h>
#include <malloc.h>
@@ -57,9 +56,9 @@ unsigned char *cc_conditionUri(const CC *cond) {
unsigned char *out = calloc(1, 1000);
sprintf(out, "ni:///sha-256;%s?fpt=%s&cost=%lu",
encoded, cond->type->name, cc_getCost(cond));
encoded, cc_typeName(cond), cc_getCost(cond));
if (cond->type->hasSubtypes) {
if (cond->type->getSubtypes) {
appendUriSubtypes(cond->type->getSubtypes(cond), out);
}
@@ -70,15 +69,6 @@ unsigned char *cc_conditionUri(const CC *cond) {
}
uint32_t cc_typeMask(const CC *cond) {
uint32_t mask = 1 << cond->type->typeId;
if (cond->type->hasSubtypes) {
mask |= cond->type->getSubtypes(cond);
}
return mask;
}
static ConditionTypes_t asnSubtypes(uint32_t mask) {
ConditionTypes_t types;
uint8_t buf[4] = {0,0,0,0};
@@ -115,7 +105,7 @@ size_t cc_conditionBinary(const CC *cond, unsigned char *buf) {
asnCondition(cond, asn);
asn_enc_rval_t rc = der_encode_to_buffer(&asn_DEF_Condition, asn, buf, 1000);
if (rc.encoded == -1) {
printf("CONDITION NOT ENCODED\n");
fprintf(stderr, "CONDITION NOT ENCODED\n");
return 0;
}
ASN_STRUCT_FREE(asn_DEF_Condition, asn);
@@ -127,7 +117,7 @@ size_t cc_fulfillmentBinary(const CC *cond, unsigned char *buf, size_t length) {
Fulfillment_t *ffill = asnFulfillmentNew(cond);
asn_enc_rval_t rc = der_encode_to_buffer(&asn_DEF_Fulfillment, ffill, buf, length);
if (rc.encoded == -1) {
printf("FULFILLMENT NOT ENCODED\n");
fprintf(stderr, "FULFILLMENT NOT ENCODED\n");
return 0;
}
ASN_STRUCT_FREE(asn_DEF_Fulfillment, ffill);
@@ -136,7 +126,7 @@ size_t cc_fulfillmentBinary(const CC *cond, unsigned char *buf, size_t length) {
static void asnCondition(const CC *cond, Condition_t *asn) {
asn->present = cond->type->asnType;
asn->present = cc_isAnon(cond) ? cond->conditionType->asnType : cond->type->asnType;
// This may look a little weird - we dont have a reference here to the correct
// union choice for the condition type, so we just assign everything to the threshold
@@ -187,14 +177,28 @@ static CC *fulfillmentToCC(Fulfillment_t *ffill) {
}
CC *cc_readFulfillmentBinary(unsigned char *ffill_bin, size_t ffill_bin_len) {
Fulfillment_t *ffill = 0;
CC *cc_readFulfillmentBinary(const unsigned char *ffill_bin, size_t ffill_bin_len) {
CC *cond = 0;
unsigned char *buf = malloc(ffill_bin_len);
Fulfillment_t *ffill = 0;
asn_dec_rval_t rval = ber_decode(0, &asn_DEF_Fulfillment, (void **)&ffill, ffill_bin, ffill_bin_len);
if (rval.code == RC_OK) {
cond = fulfillmentToCC(ffill);
ASN_STRUCT_FREE(asn_DEF_Fulfillment, ffill);
if (rval.code != RC_OK) {
goto end;
}
// Do malleability check
asn_enc_rval_t rc = der_encode_to_buffer(&asn_DEF_Fulfillment, ffill, buf, ffill_bin_len);
if (rc.encoded == -1) {
fprintf(stderr, "FULFILLMENT NOT ENCODED\n");
goto end;
}
if (rc.encoded != ffill_bin_len || 0 != memcmp(ffill_bin, buf, rc.encoded)) {
goto end;
}
cond = fulfillmentToCC(ffill);
end:
free(buf);
if (ffill) ASN_STRUCT_FREE(asn_DEF_Fulfillment, ffill);
return cond;
}
@@ -224,6 +228,7 @@ int cc_verify(const struct CC *cond, const unsigned char *msg, size_t msgLength,
unsigned char msgHash[32];
if (doHashMsg) sha256(msg, msgLength, msgHash);
else memcpy(msgHash, msg, 32);
if (!cc_secp256k1VerifyTreeMsg32(cond, msgHash)) {
return 0;
}
@@ -235,7 +240,7 @@ int cc_verify(const struct CC *cond, const unsigned char *msg, size_t msgLength,
}
CC *cc_readConditionBinary(unsigned char *cond_bin, size_t length) {
CC *cc_readConditionBinary(const unsigned char *cond_bin, size_t length) {
Condition_t *asnCond = 0;
asn_dec_rval_t rval;
rval = ber_decode(0, &asn_DEF_Condition, (void **)&asnCond, cond_bin, length);
@@ -249,8 +254,21 @@ CC *cc_readConditionBinary(unsigned char *cond_bin, size_t length) {
}
int cc_isAnon(const CC *cond) {
return cond->type->typeId == CC_Condition;
}
enum CCTypeId cc_typeId(const CC *cond) {
return cond->type->typeId;
return cc_isAnon(cond) ? cond->conditionType->typeId : cond->type->typeId;
}
uint32_t cc_typeMask(const CC *cond) {
uint32_t mask = 1 << cc_typeId(cond);
if (cond->type->getSubtypes)
mask |= cond->type->getSubtypes(cond);
return mask;
}
@@ -259,9 +277,15 @@ int cc_isFulfilled(const CC *cond) {
}
void cc_free(CC *cond) {
if (cond)
cond->type->free(cond);
char *cc_typeName(const CC *cond) {
return cc_isAnon(cond) ? cond->conditionType->name : cond->type->name;
}
void cc_free(CC *cond) {
if (cond)
cond->type->free(cond);
free(cond);
}

View File

@@ -158,7 +158,6 @@ static void ed25519Free(CC *cond) {
if (cond->signature) {
free(cond->signature);
}
free(cond);
}
@@ -167,4 +166,4 @@ static uint32_t ed25519Subtypes(const CC *cond) {
}
struct CCType cc_ed25519Type = { 4, "ed25519-sha-256", Condition_PR_ed25519Sha256, 0, 0, &ed25519Fingerprint, &ed25519Cost, &ed25519Subtypes, &ed25519FromJSON, &ed25519ToJSON, &ed25519FromFulfillment, &ed25519ToFulfillment, &ed25519IsFulfilled, &ed25519Free };
struct CCType cc_ed25519Type = { 4, "ed25519-sha-256", Condition_PR_ed25519Sha256, 0, &ed25519Fingerprint, &ed25519Cost, &ed25519Subtypes, &ed25519FromJSON, &ed25519ToJSON, &ed25519FromFulfillment, &ed25519ToFulfillment, &ed25519IsFulfilled, &ed25519Free };

View File

@@ -98,7 +98,6 @@ int evalIsFulfilled(const CC *cond) {
static void evalFree(CC *cond) {
free(cond->paramsBin);
free(cond);
}
@@ -139,4 +138,4 @@ int cc_verifyEval(const CC *cond, VerifyEval verify, void *context) {
}
struct CCType cc_evalType = { 15, "eval-sha-256", Condition_PR_evalSha256, 0, 0, &evalFingerprint, &evalCost, &evalSubtypes, &evalFromJSON, &evalToJSON, &evalFromFulfillment, &evalToFulfillment, &evalIsFulfilled, &evalFree };
struct CCType cc_evalType = { 15, "eval-sha-256", Condition_PR_evalSha256, 0, &evalFingerprint, &evalCost, &evalSubtypes, &evalFromJSON, &evalToJSON, &evalFromFulfillment, &evalToFulfillment, &evalIsFulfilled, &evalFree };

View File

@@ -1,3 +1,5 @@
#include <Condition.h>
#include <Fulfillment.h>
#include "include/cJSON.h"
#include "asn/asn_application.h"
#include "cryptoconditions.h"
@@ -17,10 +19,9 @@ extern "C" {
/*
* Condition Type */
typedef struct CCType {
uint8_t typeId;
int typeId;
unsigned char name[100];
Condition_PR asnType;
int hasSubtypes;
int (*visitChildren)(CC *cond, CCVisitor visitor);
unsigned char *(*fingerprint)(const CC *cond);
unsigned long (*getCost)(const CC *cond);

View File

@@ -71,7 +71,7 @@ static Fulfillment_t *prefixToFulfillment(const CC *cond) {
static uint32_t prefixSubtypes(const CC *cond) {
return cc_typeMask(cond->subcondition) & ~(1 << cc_prefixType.typeId);
return cc_typeMask(cond->subcondition) & ~(1 << CC_Prefix);
}
@@ -119,8 +119,7 @@ int prefixIsFulfilled(const CC *cond) {
static void prefixFree(CC *cond) {
free(cond->prefix);
cc_free(cond->subcondition);
free(cond);
}
struct CCType cc_prefixType = { 1, "prefix-sha-256", Condition_PR_prefixSha256, 1, &prefixVisitChildren, &prefixFingerprint, &prefixCost, &prefixSubtypes, &prefixFromJSON, &prefixToJSON, &prefixFromFulfillment, &prefixToFulfillment, &prefixIsFulfilled, &prefixFree };
struct CCType cc_prefixType = { 1, "prefix-sha-256", Condition_PR_prefixSha256, &prefixVisitChildren, &prefixFingerprint, &prefixCost, &prefixSubtypes, &prefixFromJSON, &prefixToJSON, &prefixFromFulfillment, &prefixToFulfillment, &prefixIsFulfilled, &prefixFree };

View File

@@ -71,7 +71,6 @@ int preimageIsFulfilled(const CC *cond) {
static void preimageFree(CC *cond) {
free(cond->preimage);
free(cond);
}
@@ -80,4 +79,4 @@ static uint32_t preimageSubtypes(const CC *cond) {
}
struct CCType cc_preimageType = { 0, "preimage-sha-256", Condition_PR_preimageSha256, 0, 0, &preimageFingerprint, &preimageCost, &preimageSubtypes, &preimageFromJSON, &preimageToJSON, &preimageFromFulfillment, &preimageToFulfillment, &preimageIsFulfilled, &preimageFree };
struct CCType cc_preimageType = { 0, "preimage-sha-256", Condition_PR_preimageSha256, 0, &preimageFingerprint, &preimageCost, &preimageSubtypes, &preimageFromJSON, &preimageToJSON, &preimageFromFulfillment, &preimageToFulfillment, &preimageIsFulfilled, &preimageFree };

View File

@@ -270,7 +270,6 @@ static void secp256k1Free(CC *cond) {
if (cond->signature) {
free(cond->signature);
}
free(cond);
}
@@ -279,4 +278,4 @@ static uint32_t secp256k1Subtypes(const CC *cond) {
}
struct CCType cc_secp256k1Type = { 5, "secp256k1-sha-256", Condition_PR_secp256k1Sha256, 0, 0, &secp256k1Fingerprint, &secp256k1Cost, &secp256k1Subtypes, &secp256k1FromJSON, &secp256k1ToJSON, &secp256k1FromFulfillment, &secp256k1ToFulfillment, &secp256k1IsFulfilled, &secp256k1Free };
struct CCType cc_secp256k1Type = { 5, "secp256k1-sha-256", Condition_PR_secp256k1Sha256, 0, &secp256k1Fingerprint, &secp256k1Cost, &secp256k1Subtypes, &secp256k1FromJSON, &secp256k1ToJSON, &secp256k1FromFulfillment, &secp256k1ToFulfillment, &secp256k1IsFulfilled, &secp256k1Free };

View File

@@ -16,7 +16,7 @@ static uint32_t thresholdSubtypes(const CC *cond) {
for (int i=0; i<cond->size; i++) {
mask |= cc_typeMask(cond->subconditions[i]);
}
mask &= ~(1 << cc_thresholdType.typeId);
mask &= ~(1 << CC_Threshold);
return mask;
}
@@ -53,13 +53,19 @@ static int thresholdVisitChildren(CC *cond, CCVisitor visitor) {
}
static int cmpConditions(const void *a, const void *b) {
static int cmpConditionBin(const void *a, const void *b) {
/* Compare conditions by their ASN binary representation */
unsigned char bufa[BUF_SIZE], bufb[BUF_SIZE];
asn_enc_rval_t r0 = der_encode_to_buffer(&asn_DEF_Condition, *(Condition_t**)a, bufa, BUF_SIZE);
asn_enc_rval_t r1 = der_encode_to_buffer(&asn_DEF_Condition, *(Condition_t**)b, bufb, BUF_SIZE);
int diff = r0.encoded - r1.encoded;
return diff != 0 ? diff : strcmp(bufa, bufb);
// below copied from ASN lib
size_t commonLen = r0.encoded < r1.encoded ? r0.encoded : r1.encoded;
int ret = memcmp(bufa, bufb, commonLen);
if (ret == 0)
return r0.encoded < r1.encoded ? -1 : 1;
return 0;
}
@@ -68,13 +74,32 @@ static unsigned char *thresholdFingerprint(const CC *cond) {
ThresholdFingerprintContents_t *fp = calloc(1, sizeof(ThresholdFingerprintContents_t));
fp->threshold = cond->threshold;
for (int i=0; i<cond->size; i++) {
asn_set_add(&fp->subconditions2, asnConditionNew(cond->subconditions[i]));
Condition_t *asnCond = asnConditionNew(cond->subconditions[i]);
asn_set_add(&fp->subconditions2, asnCond);
}
qsort(fp->subconditions2.list.array, cond->size, sizeof(Condition_t*), cmpConditions);
qsort(fp->subconditions2.list.array, cond->size, sizeof(Condition_t*), cmpConditionBin);
return hashFingerprintContents(&asn_DEF_ThresholdFingerprintContents, fp);
}
static int cmpConditionCost(const void *a, const void *b) {
CC *ca = *((CC**)a);
CC *cb = *((CC**)b);
int out = cc_getCost(ca) - cc_getCost(cb);
if (out != 0) return out;
// Do an additional sort to establish consistent order
// between conditions with the same cost.
Condition_t *asna = asnConditionNew(ca);
Condition_t *asnb = asnConditionNew(cb);
out = cmpConditionBin(&asna, &asnb);
ASN_STRUCT_FREE(asn_DEF_Condition, asna);
ASN_STRUCT_FREE(asn_DEF_Condition, asnb);
return out;
}
static CC *thresholdFromFulfillment(const Fulfillment_t *ffill) {
ThresholdFulfillment_t *t = ffill->choice.thresholdSha256;
int threshold = t->subfulfillments.list.count;
@@ -83,7 +108,7 @@ static CC *thresholdFromFulfillment(const Fulfillment_t *ffill) {
CC **subconditions = calloc(size, sizeof(CC*));
for (int i=0; i<size; i++) {
subconditions[i] = (i < threshold) ?
subconditions[i] = (i < threshold) ?
fulfillmentToCC(t->subfulfillments.list.array[i]) :
mkAnon(t->subconditions.list.array[i-threshold]);
@@ -103,16 +128,11 @@ static CC *thresholdFromFulfillment(const Fulfillment_t *ffill) {
}
static int cmpByCostDesc(const void *c1, const void *c2) {
return cc_getCost(*((CC**)c1)) - cc_getCost(*((CC**)c2));
}
static Fulfillment_t *thresholdToFulfillment(const CC *cond) {
CC *sub;
Fulfillment_t *fulfillment;
qsort(cond->subconditions, cond->size, sizeof(CC*), cmpByCostDesc);
qsort(cond->subconditions, cond->size, sizeof(CC*), cmpConditionCost);
ThresholdFulfillment_t *tf = calloc(1, sizeof(ThresholdFulfillment_t));
@@ -124,7 +144,7 @@ static Fulfillment_t *thresholdToFulfillment(const CC *cond) {
asn_set_add(&tf->subfulfillments, fulfillment);
needed--;
} else {
asn_set_add(&tf->subconditions, asnConditionNew(cond->subconditions[i]));
asn_set_add(&tf->subconditions, asnConditionNew(sub));
}
}
@@ -199,8 +219,7 @@ static void thresholdFree(CC *cond) {
cc_free(cond->subconditions[i]);
}
free(cond->subconditions);
free(cond);
}
struct CCType cc_thresholdType = { 2, "threshold-sha-256", Condition_PR_thresholdSha256, 1, &thresholdVisitChildren, &thresholdFingerprint, &thresholdCost, &thresholdSubtypes, &thresholdFromJSON, &thresholdToJSON, &thresholdFromFulfillment, &thresholdToFulfillment, &thresholdIsFulfilled, &thresholdFree };
struct CCType cc_thresholdType = { 2, "threshold-sha-256", Condition_PR_thresholdSha256, &thresholdVisitChildren, &thresholdFingerprint, &thresholdCost, &thresholdSubtypes, &thresholdFromJSON, &thresholdToJSON, &thresholdFromFulfillment, &thresholdToFulfillment, &thresholdIsFulfilled, &thresholdFree };

View File

@@ -187,6 +187,7 @@ int jsonGetBase64Optional(const cJSON *params, unsigned char *key, unsigned char
return checkDecodeBase64(item, key, err, data, size);
}
void jsonAddBase64(cJSON *params, unsigned char *key, unsigned char *bin, size_t size) {
unsigned char *b64 = base64_encode(bin, size);
cJSON_AddItemToObject(params, key, cJSON_CreateString(b64));

View File

@@ -1,7 +1,7 @@
import json
import ctypes
import base64
from .test_vectors import jsonRPC, so, decode_base64 as d64
from .test_vectors import jsonRPC, so, decode_base64 as d64, encode_base64 as e64
'''
@@ -10,7 +10,6 @@ These tests are aimed at edge cases of serverside deployment.
As such, the main functions to test are decoding and verifying fulfillment payloads.
'''
cc_rfb = lambda f: so.cc_readFulfillmentBinary(f, len(f))
cc_rcb = lambda f: so.cc_readConditionBinary(f, len(f))
@@ -44,7 +43,7 @@ def test_large_fulfillment():
def test_decode_valid_condition():
# Valid preimage
assert cc_rcb(d64('oCWAIMqXgRLKG73K-sIxs5oj3E2nhu_4FHxOcrmAd4Wv7ki7gQEB'))
assert cc_rcb(d64(b'oCWAIMqXgRLKG73K-sIxs5oj3E2nhu_4FHxOcrmAd4Wv7ki7gQEB'))
# Somewhat bogus condition (prefix with no subtypes) but nonetheless valid structure
assert cc_rcb(unhex("a10a8001618101ff82020700"))
@@ -76,3 +75,11 @@ def test_non_canonical_secp256k1():
'message': ''
})
assert res['valid'] == False
def test_malleability_checked():
assert cc_rfb(b'\xa2\x13\xa0\x0f\xa0\x05\x80\x03abc\xa0\x06\x80\x04abcd\xa1\x00')
assert not cc_rfb(b'\xa2\x13\xa0\x0f\xa0\x06\x80\x04abcd\xa0\x05\x80\x03abc\xa1\x00')
so.cc_conditionUri.restype = ctypes.c_char_p