2 Copyright (c) 2004-2007, John Hurst
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
8 1. Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 /*! \file AS_DCP_MXF.cpp
29 \brief AS-DCP library, misc classes and subroutines
32 #include <KM_fileio.h>
34 #include "AS_DCP_internal.h"
40 //------------------------------------------------------------------------------------------
46 ASDCP::WriterInfoDump(const WriterInfo& Info, FILE* stream)
53 fprintf(stream," ProductUUID: %s\n", UUID(Info.ProductUUID).EncodeHex(str_buf, 40));
58 EncryptedEssence: %s\n",
59 Info.ProductVersion.c_str(),
60 Info.CompanyName.c_str(),
61 Info.ProductName.c_str(),
62 ( Info.EncryptedEssence ? "Yes" : "No" )
65 if ( Info.EncryptedEssence )
67 fprintf(stream, " HMAC: %s\n", ( Info.UsesHMAC ? "Yes" : "No"));
68 fprintf(stream, " ContextID: %s\n", UUID(Info.ContextID).EncodeHex(str_buf, 40));
69 fprintf(stream, "CryptographicKeyID: %s\n", UUID(Info.CryptographicKeyID).EncodeHex(str_buf, 40));
72 fprintf(stream," AssetUUID: %s\n", UUID(Info.AssetUUID).EncodeHex(str_buf, 40));
73 fprintf(stream," Label Set Type: %s\n", ( Info.LabelSetType == LS_MXF_SMPTE ? "SMPTE" :
74 ( Info.LabelSetType == LS_MXF_INTEROP ? "MXF Interop" :
80 ASDCP::MD_to_WriterInfo(Identification* InfoObj, WriterInfo& Info)
82 ASDCP_TEST_NULL(InfoObj);
83 char tmp_str[IdentBufferLen];
85 Info.ProductName = "Unknown Product";
86 Info.ProductVersion = "Unknown Version";
87 Info.CompanyName = "Unknown Company";
88 memset(Info.ProductUUID, 0, UUIDlen);
90 InfoObj->ProductName.EncodeString(tmp_str, IdentBufferLen);
91 if ( *tmp_str ) Info.ProductName = tmp_str;
93 InfoObj->VersionString.EncodeString(tmp_str, IdentBufferLen);
94 if ( *tmp_str ) Info.ProductVersion = tmp_str;
96 InfoObj->CompanyName.EncodeString(tmp_str, IdentBufferLen);
97 if ( *tmp_str ) Info.CompanyName = tmp_str;
99 memcpy(Info.ProductUUID, InfoObj->ProductUID.Value(), UUIDlen);
107 ASDCP::MD_to_CryptoInfo(CryptographicContext* InfoObj, WriterInfo& Info)
109 ASDCP_TEST_NULL(InfoObj);
111 Info.EncryptedEssence = true;
112 memcpy(Info.ContextID, InfoObj->ContextID.Value(), UUIDlen);
113 memcpy(Info.CryptographicKeyID, InfoObj->CryptographicKeyID.Value(), UUIDlen);
115 UL MIC_SHA1(Dict::ul(MDD_MICAlgorithm_HMAC_SHA1));
116 UL MIC_NONE(Dict::ul(MDD_MICAlgorithm_NONE));
118 if ( InfoObj->MICAlgorithm == MIC_SHA1 )
119 Info.UsesHMAC = true;
121 else if ( InfoObj->MICAlgorithm == MIC_NONE )
122 Info.UsesHMAC = false;
126 DefaultLogSink().Error("Unexpected MICAlgorithm UL.\n");
127 return RESULT_FORMAT;
136 ASDCP::EssenceType(const char* filename, EssenceType_t& type)
138 ASDCP_TEST_NULL_STR(filename);
139 Kumu::FileReader Reader;
140 OPAtomHeader TestHeader;
142 Result_t result = Reader.OpenRead(filename);
144 if ( ASDCP_SUCCESS(result) )
145 result = TestHeader.InitFromFile(Reader); // test UL and OP
147 if ( ASDCP_SUCCESS(result) )
150 if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor))) )
151 type = ESS_JPEG_2000;
152 else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor))) )
153 type = ESS_PCM_24b_48k;
154 else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor))) )
155 type = ESS_MPEG2_VES;
156 else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(DCTimedTextDescriptor))) )
157 type = ESS_TIMED_TEXT;
165 ASDCP::RawEssenceType(const char* filename, EssenceType_t& type)
167 ASDCP_TEST_NULL_STR(filename);
169 ASDCP::FrameBuffer FB;
170 Kumu::FileReader Reader;
171 ASDCP::Wav::SimpleWaveHeader WavHeader;
172 ASDCP::AIFF::SimpleAIFFHeader AIFFHeader;
173 Kumu::XMLElement TmpElement("Tmp");
177 Result_t result = FB.Capacity(Wav::MaxWavHeader); // using Wav max because everything else is much smaller
179 if ( Kumu::PathIsFile(filename) )
181 result = Reader.OpenRead(filename);
183 if ( ASDCP_SUCCESS(result) )
185 result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
189 if ( ASDCP_SUCCESS(result) )
191 const byte_t* p = FB.RoData();
195 while ( p[i] == 0 ) i++;
197 if ( i > 1 && p[i] == 1 && (p[i+1] == ASDCP::MPEG2::SEQ_START || p[i+1] == ASDCP::MPEG2::PIC_START) )
198 type = ESS_MPEG2_VES;
200 else if ( Kumu::StringIsXML((const char*)p, FB.Size()) )
201 type = ESS_TIMED_TEXT;
203 else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(p, read_count, &data_offset)) )
204 type = ESS_PCM_24b_48k;
206 else if ( ASDCP_SUCCESS(AIFFHeader.ReadFromBuffer(p, read_count, &data_offset)) )
207 type = ESS_PCM_24b_48k;
210 else if ( Kumu::PathIsDirectory(filename) )
212 char next_file[Kumu::MaxFilePath];
213 Kumu::DirScanner Scanner;
214 Result_t result = Scanner.Open(filename);
216 if ( ASDCP_SUCCESS(result) )
218 while ( ASDCP_SUCCESS(Scanner.GetNext(next_file)) )
220 if ( next_file[0] == '.' ) // no hidden files or internal links
223 std::string Str(filename);
226 result = Reader.OpenRead(Str.c_str());
228 if ( ASDCP_SUCCESS(result) )
230 result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
234 if ( ASDCP_SUCCESS(result) )
236 if ( memcmp(FB.RoData(), ASDCP::JP2K::Magic, sizeof(ASDCP::JP2K::Magic)) == 0 )
237 type = ESS_JPEG_2000;
239 else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
240 type = ESS_PCM_24b_48k;
253 ASDCP::EncryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESEncContext* Ctx)
255 ASDCP_TEST_NULL(Ctx);
259 Result_t result = FBout.Capacity(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));
262 byte_t* p = FBout.Data();
264 // write the IV to the frame buffer
269 // encrypt the check value to the frame buffer
270 if ( ASDCP_SUCCESS(result) )
272 result = Ctx->EncryptBlock(ESV_CheckValue, p, CBC_BLOCK_SIZE);
276 // write optional plaintext region
277 if ( FBin.PlaintextOffset() > 0 )
279 assert(FBin.PlaintextOffset() <= FBin.Size());
280 memcpy(p, FBin.RoData(), FBin.PlaintextOffset());
281 p += FBin.PlaintextOffset();
284 ui32_t ct_size = FBin.Size() - FBin.PlaintextOffset();
285 ui32_t diff = ct_size % CBC_BLOCK_SIZE;
286 ui32_t block_size = ct_size - diff;
287 assert((block_size % CBC_BLOCK_SIZE) == 0);
289 // encrypt the ciphertext region essence data
290 if ( ASDCP_SUCCESS(result) )
292 result = Ctx->EncryptBlock(FBin.RoData() + FBin.PlaintextOffset(), p, block_size);
296 // construct and encrypt the padding
297 if ( ASDCP_SUCCESS(result) )
299 byte_t the_last_block[CBC_BLOCK_SIZE];
302 memcpy(the_last_block, FBin.RoData() + FBin.PlaintextOffset() + block_size, diff);
304 for (ui32_t i = 0; diff < CBC_BLOCK_SIZE; diff++, i++ )
305 the_last_block[diff] = i;
307 result = Ctx->EncryptBlock(the_last_block, p, CBC_BLOCK_SIZE);
310 if ( ASDCP_SUCCESS(result) )
311 FBout.Size(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));
318 ASDCP::DecryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESDecContext* Ctx)
320 ASDCP_TEST_NULL(Ctx);
321 assert(FBout.Capacity() >= FBin.SourceLength());
323 ui32_t ct_size = FBin.SourceLength() - FBin.PlaintextOffset();
324 ui32_t diff = ct_size % CBC_BLOCK_SIZE;
325 ui32_t block_size = ct_size - diff;
327 assert((block_size % CBC_BLOCK_SIZE) == 0);
329 const byte_t* buf = FBin.RoData();
333 buf += CBC_BLOCK_SIZE;
335 // decrypt and test check value
336 byte_t CheckValue[CBC_BLOCK_SIZE];
337 Result_t result = Ctx->DecryptBlock(buf, CheckValue, CBC_BLOCK_SIZE);
338 buf += CBC_BLOCK_SIZE;
340 if ( memcmp(CheckValue, ESV_CheckValue, CBC_BLOCK_SIZE) != 0 )
341 return RESULT_CHECKFAIL;
343 // copy plaintext region
344 if ( FBin.PlaintextOffset() > 0 )
346 memcpy(FBout.Data(), buf, FBin.PlaintextOffset());
347 buf += FBin.PlaintextOffset();
350 // decrypt all but last block
351 if ( ASDCP_SUCCESS(result) )
353 result = Ctx->DecryptBlock(buf, FBout.Data() + FBin.PlaintextOffset(), block_size);
357 // decrypt last block
358 if ( ASDCP_SUCCESS(result) )
360 byte_t the_last_block[CBC_BLOCK_SIZE];
361 result = Ctx->DecryptBlock(buf, the_last_block, CBC_BLOCK_SIZE);
363 if ( the_last_block[diff] != 0 )
365 DefaultLogSink().Error("Unexpected non-zero padding value.\n");
366 return RESULT_FORMAT;
370 memcpy(FBout.Data() + FBin.PlaintextOffset() + block_size, the_last_block, diff);
373 if ( ASDCP_SUCCESS(result) )
374 FBout.Size(FBin.SourceLength());
382 ASDCP::IntegrityPack::CalcValues(const ASDCP::FrameBuffer& FB, byte_t* AssetID,
383 ui32_t sequence, HMACContext* HMAC)
385 ASDCP_TEST_NULL(AssetID);
386 ASDCP_TEST_NULL(HMAC);
390 static byte_t ber_4[MXF_BER_LENGTH] = {0x83, 0};
392 // update HMAC with essence data
393 HMAC->Update(FB.RoData(), FB.Size());
395 // track file ID length
396 memcpy(p, ber_4, MXF_BER_LENGTH);
401 memcpy(p, AssetID, UUIDlen);
405 memcpy(p, ber_4, MXF_BER_LENGTH);
406 *(p+3) = sizeof(ui64_t);
410 Kumu::i2p<ui64_t>(KM_i64_BE(sequence), p);
414 memcpy(p, ber_4, MXF_BER_LENGTH);
418 // update HMAC with intpack values
419 HMAC->Update(Data, klv_intpack_size - HMAC_SIZE);
421 // finish & write HMAC
423 HMAC->GetHMACValue(p);
425 assert(p + HMAC_SIZE == Data + klv_intpack_size);
432 ASDCP::IntegrityPack::TestValues(const ASDCP::FrameBuffer& FB, byte_t* AssetID,
433 ui32_t sequence, HMACContext* HMAC)
435 ASDCP_TEST_NULL(AssetID);
436 ASDCP_TEST_NULL(HMAC);
438 // find the start of the intpack
439 byte_t* p = (byte_t*)FB.RoData() + ( FB.Size() - klv_intpack_size );
441 // test the AssetID length
442 if ( ! Kumu::read_test_BER(&p, UUIDlen) )
443 return RESULT_HMACFAIL;
446 if ( memcmp(p, AssetID, UUIDlen) != 0 )
448 DefaultLogSink().Error("IntegrityPack failure: AssetID mismatch.\n");
449 return RESULT_HMACFAIL;
453 // test the sequence length
454 if ( ! Kumu::read_test_BER(&p, sizeof(ui64_t)) )
455 return RESULT_HMACFAIL;
457 ui32_t test_sequence = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(p));
459 // test the sequence value
460 if ( test_sequence != sequence )
462 DefaultLogSink().Error("IntegrityPack failure: sequence is %u, expecting %u.\n", test_sequence, sequence);
463 return RESULT_HMACFAIL;
468 // test the HMAC length
469 if ( ! Kumu::read_test_BER(&p, HMAC_SIZE) )
470 return RESULT_HMACFAIL;
474 HMAC->Update(FB.RoData(), FB.Size() - HMAC_SIZE);
477 return HMAC->TestHMACValue(p);
481 // end AS_DCP_MXF.cpp