Example #1
0
	/*! DRAGONS: We use the current UTF16String SetString trait to ensure that we always have the correct handling,
	 *           even if the user wants these strings handled differently (i.e. we do it their way!)
	 */
	void SetStringArray(MDObjectPtr &Array, const std::list<std::string> &Strings)
	{
		// Abort if we are send a NULL pointer
		if(!Array) return;

		// Don't bother if we have nothing to do
		if(Strings.empty()) return;

		// Build a working string
		static MDTypePtr ValType = MDType::Find("UTF16String");
		MDObjectPtr Value = ValType ? new MDObject(ValType) : NULL;
		if(!Value)
		{
			error("Can't build UTF16String value required by SetStringArray() - need this type to be defined in the dictionary file\n");
			return;
		}

		// Get a buffer in which to build the final string array, make it quite granular as it will keep growing
		DataChunkPtr Buffer = new DataChunk();
		Buffer->SetGranularity(16 * 1024);

		std::list<std::string>::const_iterator it = Strings.begin();
		while(it != Strings.end())
		{
			Value->SetString(*it);
			DataChunkPtr ThisString = Value->PutData();
			Buffer->Append(ThisString);
			
			// Add terminator if required, i.e. if the sub-string we just added did not end in a zero
			UInt8 *p = &ThisString->Data[ThisString->Size];

			// Terminate if either of the last two bytes in the current buffer is non-zero - or if the string was too short to have a terminator
			if((ThisString->Size) < 2 || ((*(--p) != 0) || (*(--p) != 0)))
			{
				const UInt8 Term[2] = { 0, 0};
				Buffer->Set(2, Term, Buffer->Size);
			}

			it++;
		}

		// Set the value from this buffer
		Array->SetValue(Buffer);
	}
Example #2
0
/*! The primer will be <b>appended</b> to the DataChunk */
UInt32 Primer::WritePrimer(DataChunkPtr &Buffer)
{
	UInt32 Bytes;

	// Work out the primer value size first (to allow us to pre-allocate)
	UInt64 PrimerLen = UInt64(size()) * 18 + 8;

	// Re-size buffer to the probable final size
	Buffer->ResizeBuffer((UInt32)(Buffer->Size + 16 + 4 + PrimerLen));

	// Lookup the type to get the key - Static so only need to lookup once
	MDOTypePtr PrimerType = MDOType::Find(Primer_UL);
	mxflib_assert(PrimerType);

	Buffer->Append(PrimerType->GetKey());
	Bytes = static_cast<UInt32>(PrimerType->GetKey().Size);

	// Add the length
	DataChunkPtr BER = MakeBER(PrimerLen);
	Buffer->Append(*BER);
	Bytes += static_cast<UInt32>(BER->Size);

	// Add the vector header
	UInt8 Temp[4];
	PutU32(static_cast<UInt32>(size()), Temp);
	Buffer->Append(4, Temp);
	Bytes += 4;

	PutU32(18, Temp);
	Buffer->Append(4, Temp);
	Bytes += 4;

	// Write the primer data
	iterator it = begin();
	while(it != end())
	{
		PutU16((*it).first, Temp);
		Buffer->Append(2, Temp);
		Buffer->Append(16, (*it).second.GetValue());
		it++;
	}

	return Bytes;
}
Example #3
0
/*! \param Offset Offset from the start of the KLV value from which to start reading
 *  \param Size Number of bytes to read, if -1 all available bytes will be read (which could be billions!)
 *  \return The number of bytes read
 */
size_t KLVEObject::ReadDataFrom(Position Offset, size_t Size /*=-1*/)
{
	// Don't decrypt if we have no decryption wrapper
	if(!Decrypt) return Base_ReadDataFrom(Offset, Size);

	// Load the header if required (and if we can!)
	if(!DataLoaded) if(!LoadData()) return 0;

	// Don't bother reading zero bytes or off the end of the value
	if((Size == 0) || (Offset >= ValueLength))
	{
		Data.Resize(0);
		return 0;
	}

	// Load the IV and check the Check value if this is the first read
	if(	CurrentReadOffset == 0)
	{
		if( Base_ReadDataFrom(DataOffset - EncryptionOverhead, EncryptionOverhead) < EncryptionOverhead)
		{
			error("Unable to read Initialization Vector and Check Value in KLVEObject::ReadDataFrom()\n");
			return 0;
		}

		// Update the current hash if we are calculating one
		if(ReadHasher) ReadHasher->HashData(Data);

		// Initialize the decryption engine with the specified Initialization Vector
		Decrypt->SetIV(16, Data.Data, true);

		// Decrypt the check value...
		DataChunkPtr PlainCheck = Decrypt->Decrypt(16, &Data.Data[16]);

		// Encrypt the check value... (Which is "CHUKCHUKCHUKCHUK" who ever said Chuck Harrison has no ego?)
		const UInt8 DefinitivePlainCheck[16] = { 0x43, 0x48, 0x55, 0x4B, 0x43, 0x48, 0x55, 0x4B, 0x43, 0x48, 0x55, 0x4B, 0x43, 0x48, 0x55, 0x4B };
		if((!PlainCheck) || (memcmp(PlainCheck->Data, DefinitivePlainCheck, PlainCheck->Size) != 0))
		{
			error("Check value did not correctly decrypt in KLVEObject::ReadDataFrom() - is the encryption key correct?\n");
			return 0;
		}
	}

	// If all the requested bytes are encrypted read-and-decrypt
	if(Offset >= PlaintextOffset) 
	{
		// Check if an attempt is being made to random access the encrypted data - and barf if this is so
		if(Offset != CurrentReadOffset)
		{
			error("Attempt to perform random-access reading of an encrypted KLV value field\n");
			return 0;
		}

		size_t Ret = ReadCryptoDataFrom(Offset, Size);

		// Read the AS-DCP footer if we have read the last of the data
		if(CurrentReadOffset >= ValueLength) if(!ReadFooter()) { Ret = 0; Data.Resize(0); }

		return Ret;
	}

	// If all the bytes requested are plaintext use the base read
	if( (Size != static_cast<size_t>(-1)) && ((Offset + Size) < PlaintextOffset) )
	{
		// Check if an attempt is being made to random access the plaintext while hashing - and barf if this is so
		if(ReadHasher && (Offset != CurrentReadOffset))
		{
			error("Attempt to perform random-access reading of an encrypted KLV value field\n");
			return 0;
		}

		size_t Ret = Base_ReadDataFrom(DataOffset + Offset, Size);

		// Update the read pointer (it is possible to random access within the plaintext area)
		CurrentReadOffset = Offset + Ret;

		// Update the current hash if we are calculating one
		if(ReadHasher) ReadHasher->HashData(Data);

		// Read the AS-DCP footer if we have read the last of the data
		if(CurrentReadOffset >= ValueLength) if(!ReadFooter()) { Ret = 0; Data.Resize(0); }

		return Ret;
	}

	/* We will be mixing plaintext and encrypted */

	// Check if an attempt is being made to random access the encrypted data - and barf if this is so
	// DRAGONS: It is possible to re-load the initial IV to allow a "rewind" but this is not implemented
	if(CurrentReadOffset > PlaintextOffset)
	{
		error("Attempt to perform random-access reading of an encrypted KLV value field\n");
		return 0;
	}

	// Determine how many plaintext bytes could be available (maximum)
	Length PlainSize = PlaintextOffset - Offset;

	// Validate the size
	if((sizeof(size_t) < 8) && (PlainSize > 0xffffffff))
	{
		error("Encrypted KLV contains > 4GBytes of plaintext, but this platform can only handle <= 4GByte chunks\n");
		return 0;
	}

	// Try and read them all
	size_t PlainBytes = Base_ReadDataFrom(DataOffset + Offset, static_cast<size_t>(PlainSize));

	// Update the current hash if we are calculating one
	if(ReadHasher) ReadHasher->HashData(Data);

	// If we couldn't read them all then the data ends before any encrypted bytes so we can exit now
	if(PlainBytes < static_cast<size_t>(PlainSize))
	{
		// DRAGONS: We don't read the footer if we ran out of data early... should we issue an error or a warning?
		//# Read the AS-DCP footer if we have read the last of the data
		//# if(CurrentReadOffset >= ValueLength) ReadFooter();

		return PlainBytes;
	}

	/* We have all the plaintext bytes from Offset forwards, now we read all encrypted bytes too */

	// Take the buffer from the current DataChunk to preserve it
	DataChunkPtr PlainData = new DataChunk;
	PlainData->TakeBuffer(Data, true);

	// Work out how many encrypted bytes to read
	size_t EncSize;
	if(Size == static_cast<size_t>(-1)) EncSize = Size; else EncSize = Size - static_cast<size_t>(PlainSize);

	// Read the encrypted bytes
	ReadCryptoDataFrom(PlaintextOffset, EncSize);

	// Append the decrypted data to the plaintext data
	PlainData->Append(Data);

	// Transfer this data to the "current" DataChunk
	Data.TakeBuffer(PlainData);

	// Set the "next" position to just after the end of what we read
	CurrentReadOffset = Offset + Data.Size;

	// Read the AS-DCP footer if we have read the last of the data
	if(CurrentReadOffset >= ValueLength) if(!ReadFooter()) Data.Resize(0);

	// Return the total number of bytes
	return Data.Size;
}