diff --git a/src/Makefile.am b/src/Makefile.am index 5bbd26d33..7c64dcfd9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -381,6 +381,7 @@ libbitcoin_common_a_SOURCES = \ hash.cpp \ key.cpp \ keystore.cpp \ + komodo_cc.cpp \ netbase.cpp \ primitives/block.cpp \ primitives/transaction.cpp \ diff --git a/src/Makefile.zcash.include b/src/Makefile.zcash.include index e6730623a..da5b4344d 100644 --- a/src/Makefile.zcash.include +++ b/src/Makefile.zcash.include @@ -28,3 +28,4 @@ zcash_CreateJoinSplit_LDADD = \ $(LIBZCASH_LIBS) \ $(LIBCRYPTOCONDITIONS) \ $(LIBSECP256K1) + diff --git a/src/cryptoconditions/Makefile.am b/src/cryptoconditions/Makefile.am index eed285064..3b482b9c7 100644 --- a/src/cryptoconditions/Makefile.am +++ b/src/cryptoconditions/Makefile.am @@ -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 \ diff --git a/src/cryptoconditions/include/cryptoconditions.h b/src/cryptoconditions/include/cryptoconditions.h index 88a3a6560..64cd1db70 100644 --- a/src/cryptoconditions/include/cryptoconditions.h +++ b/src/cryptoconditions/include/cryptoconditions.h @@ -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); diff --git a/src/cryptoconditions/src/anon.c b/src/cryptoconditions/src/anon.c index e23fffe3e..0d10e0df0 100644 --- a/src/cryptoconditions/src/anon.c +++ b/src/cryptoconditions/src/anon.c @@ -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 }; diff --git a/src/cryptoconditions/src/cryptoconditions.c b/src/cryptoconditions/src/cryptoconditions.c index 805c66236..164d58f9c 100644 --- a/src/cryptoconditions/src/cryptoconditions.c +++ b/src/cryptoconditions/src/cryptoconditions.c @@ -12,7 +12,6 @@ #include "src/anon.c" #include "src/eval.c" #include "src/json_rpc.c" -#include "src/utils.c" #include #include @@ -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); } diff --git a/src/cryptoconditions/src/ed25519.c b/src/cryptoconditions/src/ed25519.c index 85655c2dc..b835cefc0 100644 --- a/src/cryptoconditions/src/ed25519.c +++ b/src/cryptoconditions/src/ed25519.c @@ -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 }; diff --git a/src/cryptoconditions/src/eval.c b/src/cryptoconditions/src/eval.c index 976af32d7..13388b117 100644 --- a/src/cryptoconditions/src/eval.c +++ b/src/cryptoconditions/src/eval.c @@ -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 }; diff --git a/src/cryptoconditions/src/internal.h b/src/cryptoconditions/src/internal.h index bfcc6bd14..28b3661f4 100644 --- a/src/cryptoconditions/src/internal.h +++ b/src/cryptoconditions/src/internal.h @@ -1,3 +1,5 @@ +#include +#include #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); diff --git a/src/cryptoconditions/src/prefix.c b/src/cryptoconditions/src/prefix.c index 527115709..423ee3ecf 100644 --- a/src/cryptoconditions/src/prefix.c +++ b/src/cryptoconditions/src/prefix.c @@ -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 }; diff --git a/src/cryptoconditions/src/preimage.c b/src/cryptoconditions/src/preimage.c index 71d3b2e89..95f19c09d 100644 --- a/src/cryptoconditions/src/preimage.c +++ b/src/cryptoconditions/src/preimage.c @@ -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 }; diff --git a/src/cryptoconditions/src/secp256k1.c b/src/cryptoconditions/src/secp256k1.c index be8fbd761..cfaf7c3de 100644 --- a/src/cryptoconditions/src/secp256k1.c +++ b/src/cryptoconditions/src/secp256k1.c @@ -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 }; diff --git a/src/cryptoconditions/src/threshold.c b/src/cryptoconditions/src/threshold.c index d3f4d3e63..d79c01813 100644 --- a/src/cryptoconditions/src/threshold.c +++ b/src/cryptoconditions/src/threshold.c @@ -16,7 +16,7 @@ static uint32_t thresholdSubtypes(const CC *cond) { for (int i=0; isize; 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; isize; 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; isubfulfillments.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 }; diff --git a/src/cryptoconditions/src/utils.c b/src/cryptoconditions/src/utils.c index d1e63629f..69db32042 100644 --- a/src/cryptoconditions/src/utils.c +++ b/src/cryptoconditions/src/utils.c @@ -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)); diff --git a/src/cryptoconditions/tests/test_failure_modes.py b/src/cryptoconditions/tests/test_failure_modes.py index 839eded3b..52ab3bdb9 100644 --- a/src/cryptoconditions/tests/test_failure_modes.py +++ b/src/cryptoconditions/tests/test_failure_modes.py @@ -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 diff --git a/src/komodo_cc.cpp b/src/komodo_cc.cpp new file mode 100644 index 000000000..1b05ea070 --- /dev/null +++ b/src/komodo_cc.cpp @@ -0,0 +1,44 @@ +#include "cryptoconditions/include/cryptoconditions.h" +#include "komodo_cc.h" + + +bool IsCryptoConditionsEnabled() +{ + return 0 != ASSETCHAINS_CC; +} + +// Limit acceptable condition types +// Prefix not enabled because no current use case, ambiguity on how to combine with secp256k1 +// RSA not enabled because no current use case, not implemented +int CCEnabledTypes = 1 << CC_Secp256k1 | \ + 1 << CC_Threshold | \ + 1 << CC_Eval | \ + 1 << CC_Preimage | \ + 1 << CC_Ed25519; + + +int CCSigningNodes = 1 << CC_Ed25519 | 1 << CC_Secp256k1; + + +bool IsSupportedCryptoCondition(const CC *cond) +{ + int mask = cc_typeMask(cond); + + if (mask & ~CCEnabledTypes) return false; + + // Also require that the condition have at least one signable node + if (!(mask & CCSigningNodes)) return false; + + return true; +} + + +bool IsSignedCryptoCondition(const CC *cond) +{ + if (!cc_isFulfilled(cond)) return false; + if (1 << cc_typeId(cond) & CCSigningNodes) return true; + if (cc_typeId(cond) == CC_Threshold) + for (int i=0; isize; i++) + if (IsSignedCryptoCondition(cond->subconditions[i])) return true; + return false; +} diff --git a/src/komodo_cc.h b/src/komodo_cc.h index fe05d3fc0..57d24f92e 100644 --- a/src/komodo_cc.h +++ b/src/komodo_cc.h @@ -2,39 +2,22 @@ #define KOMODO_CC_H #include "cryptoconditions/include/cryptoconditions.h" -#include "primitives/transaction.h" -/* - * Check if CryptoConditions is enabled based on chain or cmd flag - */ extern int32_t ASSETCHAINS_CC; -static bool IsCryptoConditionsEnabled() -{ - return 0 != ASSETCHAINS_CC; -} +bool IsCryptoConditionsEnabled(); /* * Check if the server can accept the condition based on it's structure / types */ -static bool IsAcceptableCryptoCondition(const CC *cond) -{ - int32_t typeMask = cc_typeMask(cond); +bool IsSupportedCryptoCondition(const CC *cond); - // Require a signature to prevent transaction malleability - if (0 == typeMask & (1 << CC_Secp256k1) || - 0 == typeMask & (1 << CC_Ed25519)) return false; - // Limit acceptable condition types - // Prefix not enabled because no current use case, ambiguity on how to combine with secp256k1 - // RSA not enabled because no current use case, not implemented - int enabledTypes = 1 << CC_Secp256k1 | 1 << CC_Threshold | 1 << CC_Eval | \ - 1 << CC_Preimage | 1 << CC_Ed25519; - if (typeMask & ~enabledTypes) return false; - - return true; -} +/* + * Check if crypto condition is signed. Can only accept signed conditions. + */ +bool IsSignedCryptoCondition(const CC *cond); #endif /* KOMODO_CC_H */ diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 20027ea41..851904eb3 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -950,27 +950,19 @@ bool EvalScript( if (stack.size() < 2) return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); - valtype& vchFulfillment = stacktop(-2); - valtype& vchCondition = stacktop(-1); - - CC *cond = cc_readFulfillmentBinary((unsigned char*)vchFulfillment.data(), - vchFulfillment.size()); - if (!cond) { + int fResult = checker.CheckCryptoCondition(stacktop(-1), stacktop(-2), script, consensusBranchId); + if (fResult == -1) { return set_error(serror, SCRIPT_ERR_CRYPTOCONDITION_INVALID_FULFILLMENT); } - - bool fSuccess = checker.CheckCryptoCondition(cond, vchCondition, script, consensusBranchId); - - cc_free(cond); - + popstack(stack); popstack(stack); - stack.push_back(fSuccess ? vchTrue : vchFalse); + stack.push_back(fResult == 1 ? vchTrue : vchFalse); if (opcode == OP_CHECKCRYPTOCONDITIONVERIFY) { - if (fSuccess) + if (fResult == 1) popstack(stack); else return set_error(serror, SCRIPT_ERR_CRYPTOCONDITION_VERIFY); @@ -1295,18 +1287,33 @@ bool TransactionSignatureChecker::CheckSig( } -bool TransactionSignatureChecker::CheckCryptoCondition(const CC *cond, const std::vector& condBin, const CScript& scriptCode, uint32_t consensusBranchId) const +int TransactionSignatureChecker::CheckCryptoCondition( + const std::vector& condBin, + const std::vector& ffillBin, + const CScript& scriptCode, + uint32_t consensusBranchId) const { - if (!IsAcceptableCryptoCondition(cond)) return false; + // Hash type is one byte tacked on to the end of the fulfillment + if (ffillBin.empty()) + return false; + + CC *cond = cc_readFulfillmentBinary((unsigned char*)ffillBin.data(), ffillBin.size()-1); + if (!cond) return -1; + + if (!IsSupportedCryptoCondition(cond)) return 0; + if (!IsSignedCryptoCondition(cond)) return 0; uint256 sighash; + int nHashType = ffillBin.back(); try { - sighash = SignatureHash(scriptCode, *txTo, nIn, SIGHASH_ALL, amount, consensusBranchId, this->txdata); + sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, consensusBranchId, this->txdata); } catch (logic_error ex) { - return false; + return 0; } - return cc_verify(cond, (const unsigned char*)&sighash, 32, 0, - condBin.data(), condBin.size(), GetCCEval(), (void*)this); + int out = cc_verify(cond, (const unsigned char*)&sighash, 32, 0, + condBin.data(), condBin.size(), GetCCEval(), (void*)this); + cc_free(cond); + return out; } diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 86ab8dac3..699d6e78c 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -128,7 +128,11 @@ public: return false; } - virtual bool CheckCryptoCondition(const CC *cond, const std::vector& condBin, const CScript& scriptCode, uint32_t consensusBranchId) const + virtual int CheckCryptoCondition( + const std::vector& condBin, + const std::vector& ffillBin, + const CScript& scriptCode, + uint32_t consensusBranchId) const { return false; } @@ -152,7 +156,11 @@ public: TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {} bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, uint32_t consensusBranchId) const; bool CheckLockTime(const CScriptNum& nLockTime) const; - bool CheckCryptoCondition(const CC *cond, const std::vector& condBin, const CScript& scriptCode, uint32_t consensusBranchId) const; + int CheckCryptoCondition( + const std::vector& condBin, + const std::vector& ffillBin, + const CScript& scriptCode, + uint32_t consensusBranchId) const; VerifyEval GetCCEval() const; }; diff --git a/src/script/script.cpp b/src/script/script.cpp index d885146a2..cfea13efe 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -250,8 +250,9 @@ bool CScript::MayAcceptCryptoCondition() const if (!(opcode > OP_0 && opcode < OP_PUSHDATA1)) return false; CC *cond = cc_readConditionBinary(data.data(), data.size()); if (!cond) return false; - bool accept = IsAcceptableCryptoCondition(cond); - return accept; + bool out = IsSupportedCryptoCondition(cond); + cc_free(cond); + return out; } bool CScript::IsPushOnly() const diff --git a/src/zcash/CreateJoinSplit.cpp b/src/zcash/CreateJoinSplit.cpp index 8f651f910..166b4fac7 100644 --- a/src/zcash/CreateJoinSplit.cpp +++ b/src/zcash/CreateJoinSplit.cpp @@ -11,6 +11,7 @@ char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; int64_t MAX_MONEY = 200000000 * 100000000LL; uint16_t BITCOIND_PORT = 7771; +uint32_t ASSETCHAINS_CC = 0; using namespace libzcash;