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

@@ -381,6 +381,7 @@ libbitcoin_common_a_SOURCES = \
hash.cpp \ hash.cpp \
key.cpp \ key.cpp \
keystore.cpp \ keystore.cpp \
komodo_cc.cpp \
netbase.cpp \ netbase.cpp \
primitives/block.cpp \ primitives/block.cpp \
primitives/transaction.cpp \ primitives/transaction.cpp \

View File

@@ -28,3 +28,4 @@ zcash_CreateJoinSplit_LDADD = \
$(LIBZCASH_LIBS) \ $(LIBZCASH_LIBS) \
$(LIBCRYPTOCONDITIONS) \ $(LIBCRYPTOCONDITIONS) \
$(LIBSECP256K1) $(LIBSECP256K1)

View File

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

View File

@@ -16,6 +16,7 @@ struct CCType;
enum CCTypeId { enum CCTypeId {
CC_Condition = -1,
CC_Preimage = 0, CC_Preimage = 0,
CC_Prefix = 1, CC_Prefix = 1,
CC_Threshold = 2, CC_Threshold = 2,
@@ -31,19 +32,27 @@ enum CCTypeId {
typedef int (*VerifyEval)(struct CC *cond, void *context); typedef int (*VerifyEval)(struct CC *cond, void *context);
/* /*
* Crypto Condition * Crypto Condition
*/ */
typedef struct CC { typedef struct CC {
struct CCType *type; struct CCType *type;
union { union {
// public key types
struct { unsigned char *publicKey, *signature; }; struct { unsigned char *publicKey, *signature; };
// preimage
struct { unsigned char *preimage; size_t preimageLength; }; struct { unsigned char *preimage; size_t preimageLength; };
// threshold
struct { long threshold; int size; struct CC **subconditions; }; struct { long threshold; int size; struct CC **subconditions; };
// prefix
struct { unsigned char *prefix; size_t prefixLength; struct CC *subcondition; struct { unsigned char *prefix; size_t prefixLength; struct CC *subcondition;
unsigned long maxMessageLength; }; unsigned long maxMessageLength; };
// eval
struct { char method[64]; unsigned char *paramsBin; size_t paramsBinLength; }; 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; } 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); static int cc_secp256k1VerifyTreeMsg32(const CC *cond, const unsigned char *msg32);
struct CC* cc_conditionFromJSON(cJSON *params, unsigned char *err); struct CC* cc_conditionFromJSON(cJSON *params, unsigned char *err);
struct CC* cc_conditionFromJSONString(const unsigned char *json, 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_readConditionBinary(const 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_readFulfillmentBinary(const unsigned char *ffill_bin, size_t ffill_bin_len);
struct cJSON* cc_conditionToJSON(const CC *cond); struct cJSON* cc_conditionToJSON(const CC *cond);
unsigned char* cc_conditionToJSONString(const CC *cond); unsigned char* cc_conditionToJSONString(const CC *cond);
unsigned char* cc_conditionUri(const CC *cond); unsigned char* cc_conditionUri(const CC *cond);
unsigned char* cc_jsonRPC(unsigned char *request); unsigned char* cc_jsonRPC(unsigned char *request);
unsigned long cc_getCost(const CC *cond); unsigned long cc_getCost(const CC *cond);
enum CCTypeId cc_typeId(const CC *cond); enum CCTypeId cc_typeId(const CC *cond);
char* cc_typeName(const CC *cond);
uint32_t cc_typeMask(const CC *cond); uint32_t cc_typeMask(const CC *cond);
void cc_free(struct 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) { static CC *mkAnon(const Condition_t *asnCond) {
CCType *realType = getTypeByAsnEnum(asnCond->present); CCType *realType = getTypeByAsnEnum(asnCond->present);
if (!realType) { if (!realType) {
printf("Unknown ASN type: %i", asnCond->present); printf("Unknown ASN type: %i", asnCond->present);
return 0; return 0;
} }
CC *cond = calloc(1, sizeof(CC)); CC *cond = calloc(1, sizeof(CC));
cond->type = (CCType*) calloc(1, sizeof(CCType)); cond->type = &cc_anonType;
*cond->type = cc_anonType; cond->conditionType = realType;
strcpy(cond->type->name, realType->name);
cond->type->hasSubtypes = realType->hasSubtypes;
cond->type->typeId = realType->typeId;
cond->type->asnType = realType->asnType;
const CompoundSha256Condition_t *deets = &asnCond->choice.thresholdSha256; const CompoundSha256Condition_t *deets = &asnCond->choice.thresholdSha256;
memcpy(cond->fingerprint, deets->fingerprint.buf, 32); memcpy(cond->fingerprint, deets->fingerprint.buf, 32);
cond->cost = deets->cost; cond->cost = deets->cost;
if (realType->hasSubtypes) { if (realType->getSubtypes) {
cond->subtypes = fromAsnSubtypes(deets->subtypes); cond->subtypes = fromAsnSubtypes(deets->subtypes);
} }
return cond; return cond;
@@ -66,8 +63,6 @@ static Fulfillment_t *anonFulfillment(const CC *cond) {
static void anonFree(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/anon.c"
#include "src/eval.c" #include "src/eval.c"
#include "src/json_rpc.c" #include "src/json_rpc.c"
#include "src/utils.c"
#include <cJSON.h> #include <cJSON.h>
#include <malloc.h> #include <malloc.h>
@@ -57,9 +56,9 @@ unsigned char *cc_conditionUri(const CC *cond) {
unsigned char *out = calloc(1, 1000); unsigned char *out = calloc(1, 1000);
sprintf(out, "ni:///sha-256;%s?fpt=%s&cost=%lu", 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); 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) { static ConditionTypes_t asnSubtypes(uint32_t mask) {
ConditionTypes_t types; ConditionTypes_t types;
uint8_t buf[4] = {0,0,0,0}; 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); asnCondition(cond, asn);
asn_enc_rval_t rc = der_encode_to_buffer(&asn_DEF_Condition, asn, buf, 1000); asn_enc_rval_t rc = der_encode_to_buffer(&asn_DEF_Condition, asn, buf, 1000);
if (rc.encoded == -1) { if (rc.encoded == -1) {
printf("CONDITION NOT ENCODED\n"); fprintf(stderr, "CONDITION NOT ENCODED\n");
return 0; return 0;
} }
ASN_STRUCT_FREE(asn_DEF_Condition, asn); 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); Fulfillment_t *ffill = asnFulfillmentNew(cond);
asn_enc_rval_t rc = der_encode_to_buffer(&asn_DEF_Fulfillment, ffill, buf, length); asn_enc_rval_t rc = der_encode_to_buffer(&asn_DEF_Fulfillment, ffill, buf, length);
if (rc.encoded == -1) { if (rc.encoded == -1) {
printf("FULFILLMENT NOT ENCODED\n"); fprintf(stderr, "FULFILLMENT NOT ENCODED\n");
return 0; return 0;
} }
ASN_STRUCT_FREE(asn_DEF_Fulfillment, ffill); 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) { 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 // 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 // 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) { CC *cc_readFulfillmentBinary(const unsigned char *ffill_bin, size_t ffill_bin_len) {
Fulfillment_t *ffill = 0;
CC *cond = 0; 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); asn_dec_rval_t rval = ber_decode(0, &asn_DEF_Fulfillment, (void **)&ffill, ffill_bin, ffill_bin_len);
if (rval.code == RC_OK) { if (rval.code != RC_OK) {
cond = fulfillmentToCC(ffill); goto end;
ASN_STRUCT_FREE(asn_DEF_Fulfillment, ffill);
} }
// 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; return cond;
} }
@@ -224,6 +228,7 @@ int cc_verify(const struct CC *cond, const unsigned char *msg, size_t msgLength,
unsigned char msgHash[32]; unsigned char msgHash[32];
if (doHashMsg) sha256(msg, msgLength, msgHash); if (doHashMsg) sha256(msg, msgLength, msgHash);
else memcpy(msgHash, msg, 32); else memcpy(msgHash, msg, 32);
if (!cc_secp256k1VerifyTreeMsg32(cond, msgHash)) { if (!cc_secp256k1VerifyTreeMsg32(cond, msgHash)) {
return 0; 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; Condition_t *asnCond = 0;
asn_dec_rval_t rval; asn_dec_rval_t rval;
rval = ber_decode(0, &asn_DEF_Condition, (void **)&asnCond, cond_bin, length); 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) { 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) { char *cc_typeName(const CC *cond) {
if (cond) return cc_isAnon(cond) ? cond->conditionType->name : cond->type->name;
cond->type->free(cond); }
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) { if (cond->signature) {
free(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) { static void evalFree(CC *cond) {
free(cond->paramsBin); 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 "include/cJSON.h"
#include "asn/asn_application.h" #include "asn/asn_application.h"
#include "cryptoconditions.h" #include "cryptoconditions.h"
@@ -17,10 +19,9 @@ extern "C" {
/* /*
* Condition Type */ * Condition Type */
typedef struct CCType { typedef struct CCType {
uint8_t typeId; int typeId;
unsigned char name[100]; unsigned char name[100];
Condition_PR asnType; Condition_PR asnType;
int hasSubtypes;
int (*visitChildren)(CC *cond, CCVisitor visitor); int (*visitChildren)(CC *cond, CCVisitor visitor);
unsigned char *(*fingerprint)(const CC *cond); unsigned char *(*fingerprint)(const CC *cond);
unsigned long (*getCost)(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) { 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) { static void prefixFree(CC *cond) {
free(cond->prefix); free(cond->prefix);
cc_free(cond->subcondition); 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) { static void preimageFree(CC *cond) {
free(cond->preimage); 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) { if (cond->signature) {
free(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++) { for (int i=0; i<cond->size; i++) {
mask |= cc_typeMask(cond->subconditions[i]); mask |= cc_typeMask(cond->subconditions[i]);
} }
mask &= ~(1 << cc_thresholdType.typeId); mask &= ~(1 << CC_Threshold);
return mask; 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 */ /* Compare conditions by their ASN binary representation */
unsigned char bufa[BUF_SIZE], bufb[BUF_SIZE]; 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 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); 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)); ThresholdFingerprintContents_t *fp = calloc(1, sizeof(ThresholdFingerprintContents_t));
fp->threshold = cond->threshold; fp->threshold = cond->threshold;
for (int i=0; i<cond->size; i++) { 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); 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) { static CC *thresholdFromFulfillment(const Fulfillment_t *ffill) {
ThresholdFulfillment_t *t = ffill->choice.thresholdSha256; ThresholdFulfillment_t *t = ffill->choice.thresholdSha256;
int threshold = t->subfulfillments.list.count; int threshold = t->subfulfillments.list.count;
@@ -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) { static Fulfillment_t *thresholdToFulfillment(const CC *cond) {
CC *sub; CC *sub;
Fulfillment_t *fulfillment; 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)); 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); asn_set_add(&tf->subfulfillments, fulfillment);
needed--; needed--;
} else { } 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]); cc_free(cond->subconditions[i]);
} }
free(cond->subconditions); 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); return checkDecodeBase64(item, key, err, data, size);
} }
void jsonAddBase64(cJSON *params, unsigned char *key, unsigned char *bin, size_t size) { void jsonAddBase64(cJSON *params, unsigned char *key, unsigned char *bin, size_t size) {
unsigned char *b64 = base64_encode(bin, size); unsigned char *b64 = base64_encode(bin, size);
cJSON_AddItemToObject(params, key, cJSON_CreateString(b64)); cJSON_AddItemToObject(params, key, cJSON_CreateString(b64));

View File

@@ -1,7 +1,7 @@
import json import json
import ctypes import ctypes
import base64 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. As such, the main functions to test are decoding and verifying fulfillment payloads.
''' '''
cc_rfb = lambda f: so.cc_readFulfillmentBinary(f, len(f)) cc_rfb = lambda f: so.cc_readFulfillmentBinary(f, len(f))
cc_rcb = lambda f: so.cc_readConditionBinary(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(): def test_decode_valid_condition():
# Valid preimage # 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 # Somewhat bogus condition (prefix with no subtypes) but nonetheless valid structure
assert cc_rcb(unhex("a10a8001618101ff82020700")) assert cc_rcb(unhex("a10a8001618101ff82020700"))
@@ -76,3 +75,11 @@ def test_non_canonical_secp256k1():
'message': '' 'message': ''
}) })
assert res['valid'] == False 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

44
src/komodo_cc.cpp Normal file
View File

@@ -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; i<cond->size; i++)
if (IsSignedCryptoCondition(cond->subconditions[i])) return true;
return false;
}

View File

@@ -2,39 +2,22 @@
#define KOMODO_CC_H #define KOMODO_CC_H
#include "cryptoconditions/include/cryptoconditions.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; extern int32_t ASSETCHAINS_CC;
static bool IsCryptoConditionsEnabled() bool IsCryptoConditionsEnabled();
{
return 0 != ASSETCHAINS_CC;
}
/* /*
* Check if the server can accept the condition based on it's structure / types * Check if the server can accept the condition based on it's structure / types
*/ */
static bool IsAcceptableCryptoCondition(const CC *cond) bool IsSupportedCryptoCondition(const CC *cond);
{
int32_t typeMask = cc_typeMask(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 * Check if crypto condition is signed. Can only accept signed conditions.
// RSA not enabled because no current use case, not implemented */
int enabledTypes = 1 << CC_Secp256k1 | 1 << CC_Threshold | 1 << CC_Eval | \ bool IsSignedCryptoCondition(const CC *cond);
1 << CC_Preimage | 1 << CC_Ed25519;
if (typeMask & ~enabledTypes) return false;
return true;
}
#endif /* KOMODO_CC_H */ #endif /* KOMODO_CC_H */

View File

@@ -950,27 +950,19 @@ bool EvalScript(
if (stack.size() < 2) if (stack.size() < 2)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype& vchFulfillment = stacktop(-2); int fResult = checker.CheckCryptoCondition(stacktop(-1), stacktop(-2), script, consensusBranchId);
valtype& vchCondition = stacktop(-1); if (fResult == -1) {
CC *cond = cc_readFulfillmentBinary((unsigned char*)vchFulfillment.data(),
vchFulfillment.size());
if (!cond) {
return set_error(serror, SCRIPT_ERR_CRYPTOCONDITION_INVALID_FULFILLMENT); return set_error(serror, SCRIPT_ERR_CRYPTOCONDITION_INVALID_FULFILLMENT);
} }
bool fSuccess = checker.CheckCryptoCondition(cond, vchCondition, script, consensusBranchId);
cc_free(cond);
popstack(stack); popstack(stack);
popstack(stack); popstack(stack);
stack.push_back(fSuccess ? vchTrue : vchFalse); stack.push_back(fResult == 1 ? vchTrue : vchFalse);
if (opcode == OP_CHECKCRYPTOCONDITIONVERIFY) if (opcode == OP_CHECKCRYPTOCONDITIONVERIFY)
{ {
if (fSuccess) if (fResult == 1)
popstack(stack); popstack(stack);
else else
return set_error(serror, SCRIPT_ERR_CRYPTOCONDITION_VERIFY); return set_error(serror, SCRIPT_ERR_CRYPTOCONDITION_VERIFY);
@@ -1295,18 +1287,33 @@ bool TransactionSignatureChecker::CheckSig(
} }
bool TransactionSignatureChecker::CheckCryptoCondition(const CC *cond, const std::vector<unsigned char>& condBin, const CScript& scriptCode, uint32_t consensusBranchId) const int TransactionSignatureChecker::CheckCryptoCondition(
const std::vector<unsigned char>& condBin,
const std::vector<unsigned char>& 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; uint256 sighash;
int nHashType = ffillBin.back();
try { 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) { } catch (logic_error ex) {
return false; return 0;
} }
return cc_verify(cond, (const unsigned char*)&sighash, 32, 0, int out = cc_verify(cond, (const unsigned char*)&sighash, 32, 0,
condBin.data(), condBin.size(), GetCCEval(), (void*)this); condBin.data(), condBin.size(), GetCCEval(), (void*)this);
cc_free(cond);
return out;
} }

View File

@@ -128,7 +128,11 @@ public:
return false; return false;
} }
virtual bool CheckCryptoCondition(const CC *cond, const std::vector<unsigned char>& condBin, const CScript& scriptCode, uint32_t consensusBranchId) const virtual int CheckCryptoCondition(
const std::vector<unsigned char>& condBin,
const std::vector<unsigned char>& ffillBin,
const CScript& scriptCode,
uint32_t consensusBranchId) const
{ {
return false; 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) {} 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<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, uint32_t consensusBranchId) const; bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, uint32_t consensusBranchId) const;
bool CheckLockTime(const CScriptNum& nLockTime) const; bool CheckLockTime(const CScriptNum& nLockTime) const;
bool CheckCryptoCondition(const CC *cond, const std::vector<unsigned char>& condBin, const CScript& scriptCode, uint32_t consensusBranchId) const; int CheckCryptoCondition(
const std::vector<unsigned char>& condBin,
const std::vector<unsigned char>& ffillBin,
const CScript& scriptCode,
uint32_t consensusBranchId) const;
VerifyEval GetCCEval() const; VerifyEval GetCCEval() const;
}; };

View File

@@ -250,8 +250,9 @@ bool CScript::MayAcceptCryptoCondition() const
if (!(opcode > OP_0 && opcode < OP_PUSHDATA1)) return false; if (!(opcode > OP_0 && opcode < OP_PUSHDATA1)) return false;
CC *cond = cc_readConditionBinary(data.data(), data.size()); CC *cond = cc_readConditionBinary(data.data(), data.size());
if (!cond) return false; if (!cond) return false;
bool accept = IsAcceptableCryptoCondition(cond); bool out = IsSupportedCryptoCondition(cond);
return accept; cc_free(cond);
return out;
} }
bool CScript::IsPushOnly() const bool CScript::IsPushOnly() const

View File

@@ -11,6 +11,7 @@
char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN];
int64_t MAX_MONEY = 200000000 * 100000000LL; int64_t MAX_MONEY = 200000000 * 100000000LL;
uint16_t BITCOIND_PORT = 7771; uint16_t BITCOIND_PORT = 7771;
uint32_t ASSETCHAINS_CC = 0;
using namespace libzcash; using namespace libzcash;