size_t Resource::Reader::readData(char *buffer, size_t size) { if(!mCurrentBlock) return 0; // EOF if(!mKey.empty() && !mCurrentBlock->hasDecryption()) { BinaryString subsalt; subsalt.writeBinary(uint64_t(mCurrentBlockIndex)); // Generate subkey BinaryString subkey; Sha256().pbkdf2_hmac(mKey, subsalt, subkey, 32, 100); // Generate iv BinaryString iv; Sha256().pbkdf2_hmac(mResource->salt(), subsalt, iv, 16, 100); // Initialize decryption process mCurrentBlock->setDecryption(subkey, iv); } size_t ret; if((ret = mCurrentBlock->readData(buffer, size))) { mReadPosition+= ret; return ret; } delete mCurrentBlock; ++mCurrentBlockIndex; mCurrentBlock = mNextBlock; mNextBlock = createBlock(mCurrentBlockIndex + 1); return readData(buffer, size); }
QByteArray Hmac(const QByteArray& key, const QByteArray& data, HashFunction method) { const int kBlockSize = 64; // bytes Q_ASSERT(key.length() <= kBlockSize); QByteArray inner_padding(kBlockSize, char(0x36)); QByteArray outer_padding(kBlockSize, char(0x5c)); for (int i=0 ; i<key.length() ; ++i) { inner_padding[i] = inner_padding[i] ^ key[i]; outer_padding[i] = outer_padding[i] ^ key[i]; } if (Md5_Algo == method) { return QCryptographicHash::hash(outer_padding + QCryptographicHash::hash(inner_padding + data, QCryptographicHash::Md5), QCryptographicHash::Md5); } else if (Sha1_Algo == method) { return QCryptographicHash::hash(outer_padding + QCryptographicHash::hash(inner_padding + data, QCryptographicHash::Sha1), QCryptographicHash::Sha1); } else { // Sha256_Algo, currently default return Sha256(outer_padding + Sha256(inner_padding + data)); } }
void calculateTransactionHash(Transaction * self) { assert(self != NULL); uint8_t tmpHashStore[32]; uint8_t * data = getByteArrayData(getMessage(self)->bytes); Sha256(data, getMessage(self)->bytes->length, tmpHashStore); Sha256(tmpHashStore, 32, self->hash); }
Block::Block(const String &filename, int64_t offset, int64_t size) : mCipher(NULL) { mFile = new File(filename, File::Read); mOffset = offset; mFile->seekRead(mOffset); if(size >= 0) mSize = Sha256().compute(*mFile, size, mDigest); else mSize = Sha256().compute(*mFile, mDigest); mFile->seekRead(mOffset); notifyStore(); }
static void _Sha256HmacInit(Sha256HmacCtx *Ctx, BYTE *key, size_t klen) { BYTE IPad[64]; unsigned int i; memset(IPad, 0x36, sizeof(IPad)); memset(Ctx->OPad, 0x5C, sizeof(Ctx->OPad)); if ( klen > 64 ) { BYTE *temp = (BYTE*)alloca(32); Sha256(key, klen, temp); klen = 32; key = temp; } for (i = 0; i < klen; i++) { IPad[ i ] ^= key[ i ]; Ctx->OPad[ i ] ^= key[ i ]; } Sha256Init(&Ctx->ShaCtx); Sha256Update(&Ctx->ShaCtx, IPad, sizeof(IPad)); }
String Cache::move(const String &filename) { BinaryString digest; File file(filename); Sha256().compute(file, digest); file.close(); String destination = path(digest); File::Rename(filename, destination); return destination; }
bool Block::ProcessFile(File &file, BinaryString &digest) { int64_t offset = file.tellRead(); int64_t size = Sha256().compute(file, Size, digest); if(size) { Store::Instance->notifyBlock(digest, file.name(), offset, size); return true; } return false; }
uint8_t initializeAddressFromRIPEMD160Hash(Address * self,uint8_t netVersionByte,uint8_t * hash,uint8_t cacheString,void (*onErrorReceived)(Error error,char *,...)) { /* Build address and then complete intitialization with VersionChecksumBytes*/ uint8_t * addressData = malloc(25); /* 1 version byte, 20 hash bytes, 4 checksum bytes.*/ if ( addressData==NULL) { onErrorReceived(ERROR_OUT_OF_MEMORY,"Cannot allocate 25 bytes of memory in initializeAddressFromRIPEMD160Hash\n"); return FALSE; } /* Set version byte */ addressData[0] = netVersionByte; /* Move hash */ memmove(addressData+1, hash, 20); /* Make checksum and move it into address; check https://en.bitcoin.it/wiki/Technical_background_of_Bitcoin_addresses for step by step explanation */ uint8_t checksum[32]; uint8_t checksum2[32]; Sha256(addressData,21,checksum); Sha256(checksum,32,checksum2); memmove(addressData+21, checksum2, 4); /* initialise VersionChecksumBytes*/ if (! initializeVersionChecksumBytesFromBytes(getVersionChecksumBytes(self), addressData, 25,cacheString, onErrorReceived)) { return FALSE; } return TRUE; }
Resource::Reader::Reader(Resource *resource, const String &secret, bool nocheck) : mResource(resource), mReadPosition(0), mCurrentBlock(NULL), mNextBlock(NULL) { Assert(mResource); if(!secret.empty()) { if(!nocheck && mResource->salt().empty()) throw Exception("Expected encrypted resource"); Sha256().pbkdf2_hmac(secret, mResource->salt(), mKey, 32, 100000); } else { if(!nocheck && !mResource->salt().empty()) throw Exception("Expected non-encrypted resource"); } seekRead(0); // Initialize positions }
TransactionHashStatus getTransactionInputHashForVerification(void * txIn, ByteArray * prevOutSubScript, uint32_t input, SignatureType signType, uint8_t * hash) { assert(txIn != NULL); assert(prevOutSubScript != NULL); assert(input >= 0); assert(signType != NULL); assert(hash != NULL); Transaction * self = txIn; if (self->numOfTransactionInput < input + 1) { getMessage(self)->onErrorReceived(ERROR_TRANSACTION_FEW_INPUTS, "Receiving transaction hash to sign cannot be done for because the input index goes past the number of inputs."); return TX_HASH_BAD; } uint8_t last5Bits = (signType & 0x1f); /* For some reason this is what the C++ client does.*/ uint32_t sizeOfData = 12 + prevOutSubScript->length; /*Version, lock time and the sign type make up 12 bytes.*/ if (signType & SIGHASH_ANYONECANPAY) { sizeOfData += 41; /* Just this one input. 32 bytes for outPointHash, 4 for outPointIndex, 1 for VarLenInt, 4 for sequence*/ } else { sizeOfData += getSizeOfVarLenInt(self->numOfTransactionInput) + self->numOfTransactionInput * 40; /* All inputs*/ } if (last5Bits == SIGHASH_NONE) { sizeOfData++; /* Just for the VarLenInt and no outputs.*/ } else if ((signType & 0x1f) == SIGHASH_SINGLE) { if (self->numOfTransactionOutput < input + 1) { getMessage(self)->onErrorReceived(ERROR_TRANSACTION_FEW_OUTPUTS, "Receiving transaction hash to sign cannot be done for _SIGHASH_SINGLE because there are not enough outputs."); return TX_HASH_BAD; } sizeOfData += getSizeOfVarLenInt(input + 1) + input * 9; /* For outputs up to the input index*/ /* The size for the output at the input index.*/ uint32_t len = getByteArray(self->outputs[input]->scriptObject)->length; sizeOfData += 8 + getSizeOfVarLenInt(len) + len; } else { /* All outputs. Default to SIGHASH_ALL*/ sizeOfData += getSizeOfVarLenInt(self->numOfTransactionOutput); uint32_t i; for (i = 0; i < self->numOfTransactionOutput; i++) { uint32_t len = getByteArray(self->outputs[i]->scriptObject)->length; sizeOfData += 8 + getSizeOfVarLenInt(len) + len; } } ByteArray * data = createNewByteArrayOfSize(sizeOfData, getMessage(self)->onErrorReceived); if (!data) { return TX_HASH_FAIL; } writeInt32AsLittleEndianIntoByteArray(data, 0, self->version); /* Copy input data. Scripts are not copied for the inputs.*/ uint32_t cursor; if (signType & SIGHASH_ANYONECANPAY) { encodeVarLenInt(data, 4, createVarLenIntFromUInt64(1)); /* Only the input the signature is for.*/ copyByteArrayToByteArray(data, 5, self->inputs[input]->prevOutput.hash); writeInt32AsLittleEndianIntoByteArray(data, 37, self->inputs[input]->prevOutput.index); /*Add prevOutSubScript*/ copyByteArrayToByteArray(data, 41, prevOutSubScript); cursor = 41 + prevOutSubScript->length; writeInt32AsLittleEndianIntoByteArray(data, cursor, self->inputs[input]->sequence); cursor += 4; } else { VarLenInt inputNum = createVarLenIntFromUInt64( self->numOfTransactionInput); encodeVarLenInt(data, 4, inputNum); cursor = 4 + inputNum.storageSize; uint32_t i; for (i = 0; i < self->numOfTransactionInput; i++) { copyByteArrayToByteArray(data, cursor, self->inputs[i]->prevOutput.hash); cursor += 32; writeInt32AsLittleEndianIntoByteArray(data, cursor, self->inputs[i]->prevOutput.index); cursor += 4; /* Add prevOutSubScript if the input is for the signature.*/ if (i == input) { copyByteArrayToByteArray(data, cursor, prevOutSubScript); cursor += prevOutSubScript->length; } if ((signType == SIGHASH_NONE || signType == SIGHASH_SINGLE) && i != input) writeInt32AsLittleEndianIntoByteArray(data, cursor, 0); else /* SIGHASH_ALL or input index for signing sequence*/ writeInt32AsLittleEndianIntoByteArray(data, cursor, self->inputs[i]->sequence); cursor += 4; } } /* Copy output data*/ if (last5Bits == SIGHASH_NONE) { VarLenInt varInt = createVarLenIntFromUInt64(0); encodeVarLenInt(data, cursor, varInt); cursor++; } else if (last5Bits == SIGHASH_SINGLE) { VarLenInt varInt = createVarLenIntFromUInt64(input + 1); encodeVarLenInt(data, cursor, varInt); cursor += varInt.storageSize; uint32_t j; for (j = 0; j < input; j++) { writeInt64AsLittleEndianIntoByteArray(data, cursor, 0xFFFFFFFFFFFFFFFF); /*_OUTPUT_VALUE_NUS_ONE = 0xFFFFFFFFFFFFFFFF*/ cursor += 8; encodeVarLenInt(data, cursor, createVarLenIntFromUInt64(0)); cursor++; } writeInt64AsLittleEndianIntoByteArray(data, cursor, self->outputs[input]->value); cursor += 8; varInt = createVarLenIntFromUInt64( getByteArray(self->outputs[input]->scriptObject)->length); encodeVarLenInt(data, cursor, varInt); cursor += varInt.storageSize; copyByteArrayToByteArray(data, cursor, getByteArray(self->outputs[input]->scriptObject)); cursor += varInt.value; } else { /* SIGHASH_ALL*/ VarLenInt varInt = createVarLenIntFromUInt64( self->numOfTransactionOutput); encodeVarLenInt(data, cursor, varInt); cursor += varInt.storageSize; uint32_t x; for (x = 0; x < self->numOfTransactionOutput; x++) { writeInt64AsLittleEndianIntoByteArray(data, cursor, self->outputs[x]->value); cursor += 8; varInt = createVarLenIntFromUInt64( getByteArray(self->outputs[x]->scriptObject)->length); encodeVarLenInt(data, cursor, varInt); cursor += varInt.storageSize; copyByteArrayToByteArray(data, cursor, getByteArray(self->outputs[x]->scriptObject)); cursor += varInt.value; } } /* Set lockTime*/ writeInt32AsLittleEndianIntoByteArray(data, cursor, self->lockTime); writeInt32AsLittleEndianIntoByteArray(data, cursor + 4, signType); assert(sizeOfData == cursor + 8); /* Must always be like this*/ uint8_t firstHash[32]; Sha256(getByteArrayData(data), sizeOfData, firstHash); Sha256(firstHash, 32, hash); return TX_HASH_GOOD; }
inline static HashSha256 Sha256(std::string s) { return Sha256(s.c_str(), s.size()); }
void Resource::process(const String &filename, const String &name, const String &type, const String &secret) { BinaryString salt; // If secret is not empty then this is an encrypted resource if(!secret.empty()) { // Generate salt from plaintext // Because we need to be able to recognize data is identical even when encrypted BinaryString digest; File file(filename, File::Read); Sha256().compute(file, digest); Sha256().pbkdf2_hmac(digest, type, salt, 32, 100000); Assert(!salt.empty()); } // Fill index record delete mIndexRecord; int64_t size = File::Size(filename); mIndexRecord = new Resource::IndexRecord; mIndexRecord->name = name; mIndexRecord->type = type; mIndexRecord->size = size; mIndexRecord->salt = salt; mIndexRecord->blockDigests.reserve(size/Block::Size); // Process blocks File file(filename, File::Read); BinaryString blockDigest; if(!secret.empty()) { BinaryString key; Sha256().pbkdf2_hmac(secret, salt, key, 32, 100000); uint64_t i = 0; while(true) { BinaryString subsalt; subsalt.writeBinary(i); // Generate subkey BinaryString subkey; Sha256().pbkdf2_hmac(key, subsalt, subkey, 32, 100); // Generate iv BinaryString iv; Sha256().pbkdf2_hmac(salt, subsalt, iv, 16, 100); if(!Block::EncryptFile(file, subkey, iv, blockDigest)) break; mIndexRecord->blockDigests.append(blockDigest); ++i; } } else { while(Block::ProcessFile(file, blockDigest)) mIndexRecord->blockDigests.append(blockDigest); } // Create index String tempFileName = File::TempName(); File tempFile(tempFileName, File::Truncate); BinarySerializer serializer(&tempFile); serializer.write(mIndexRecord); tempFile.close(); String indexFilePath = Cache::Instance->move(tempFileName); // Create index block delete mIndexBlock; mIndexBlock = new Block(indexFilePath); }