2 Copyright (c) 2004-2013, 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"
42 //------------------------------------------------------------------------------------------
48 ASDCP::operator << (std::ostream& strm, const WriterInfo& Info)
52 strm << " ProductUUID: " << UUID(Info.ProductUUID).EncodeHex(str_buf, 40) << std::endl;
53 strm << " ProductVersion: " << Info.ProductVersion << std::endl;
54 strm << " CompanyName: " << Info.CompanyName << std::endl;
55 strm << " ProductName: " << Info.ProductName << std::endl;
56 strm << " EncryptedEssence: " << (Info.EncryptedEssence ? "Yes" : "No") << std::endl;
58 if ( Info.EncryptedEssence )
60 strm << " HMAC: " << (Info.UsesHMAC ? "Yes" : "No") << std::endl;
61 strm << " ContextID: " << UUID(Info.ContextID).EncodeHex(str_buf, 40) << std::endl;
62 strm << "CryptographicKeyID: " << UUID(Info.CryptographicKeyID).EncodeHex(str_buf, 40) << std::endl;
65 strm << " AssetUUID: " << UUID(Info.AssetUUID).EncodeHex(str_buf, 40) << std::endl;
66 strm << " Label Set Type: " << (Info.LabelSetType == LS_MXF_SMPTE ? "SMPTE" :
67 (Info.LabelSetType == LS_MXF_INTEROP ? "MXF Interop" :
68 "Unknown")) << std::endl;
74 ASDCP::WriterInfoDump(const WriterInfo& Info, FILE* stream)
81 fprintf(stream," ProductUUID: %s\n", UUID(Info.ProductUUID).EncodeHex(str_buf, 40));
86 EncryptedEssence: %s\n",
87 Info.ProductVersion.c_str(),
88 Info.CompanyName.c_str(),
89 Info.ProductName.c_str(),
90 ( Info.EncryptedEssence ? "Yes" : "No" )
93 if ( Info.EncryptedEssence )
95 fprintf(stream, " HMAC: %s\n", ( Info.UsesHMAC ? "Yes" : "No"));
96 fprintf(stream, " ContextID: %s\n", UUID(Info.ContextID).EncodeHex(str_buf, 40));
97 fprintf(stream, "CryptographicKeyID: %s\n", UUID(Info.CryptographicKeyID).EncodeHex(str_buf, 40));
100 fprintf(stream," AssetUUID: %s\n", UUID(Info.AssetUUID).EncodeHex(str_buf, 40));
101 fprintf(stream," Label Set Type: %s\n", ( Info.LabelSetType == LS_MXF_SMPTE ? "SMPTE" :
102 ( Info.LabelSetType == LS_MXF_INTEROP ? "MXF Interop" :
108 ASDCP::MD_to_WriterInfo(Identification* InfoObj, WriterInfo& Info)
110 ASDCP_TEST_NULL(InfoObj);
111 char tmp_str[IdentBufferLen];
113 Info.ProductName = "Unknown Product";
114 Info.ProductVersion = "Unknown Version";
115 Info.CompanyName = "Unknown Company";
116 memset(Info.ProductUUID, 0, UUIDlen);
118 InfoObj->ProductName.EncodeString(tmp_str, IdentBufferLen);
119 if ( *tmp_str ) Info.ProductName = tmp_str;
121 InfoObj->VersionString.EncodeString(tmp_str, IdentBufferLen);
122 if ( *tmp_str ) Info.ProductVersion = tmp_str;
124 InfoObj->CompanyName.EncodeString(tmp_str, IdentBufferLen);
125 if ( *tmp_str ) Info.CompanyName = tmp_str;
127 memcpy(Info.ProductUUID, InfoObj->ProductUID.Value(), UUIDlen);
135 ASDCP::MD_to_CryptoInfo(CryptographicContext* InfoObj, WriterInfo& Info, const Dictionary& Dict)
137 ASDCP_TEST_NULL(InfoObj);
139 Info.EncryptedEssence = true;
140 memcpy(Info.ContextID, InfoObj->ContextID.Value(), UUIDlen);
141 memcpy(Info.CryptographicKeyID, InfoObj->CryptographicKeyID.Value(), UUIDlen);
143 UL MIC_SHA1(Dict.ul(MDD_MICAlgorithm_HMAC_SHA1));
144 UL MIC_NONE(Dict.ul(MDD_MICAlgorithm_NONE));
146 if ( InfoObj->MICAlgorithm == MIC_SHA1 )
147 Info.UsesHMAC = true;
149 else if ( InfoObj->MICAlgorithm == MIC_NONE )
150 Info.UsesHMAC = false;
154 DefaultLogSink().Error("Unexpected MICAlgorithm UL.\n");
155 return RESULT_FORMAT;
164 ASDCP::EssenceType(const char* filename, EssenceType_t& type)
166 const Dictionary* m_Dict = &DefaultCompositeDict();
169 ASDCP_TEST_NULL_STR(filename);
170 Kumu::FileReader Reader;
171 OP1aHeader TestHeader(m_Dict);
173 Result_t result = Reader.OpenRead(filename);
175 if ( ASDCP_SUCCESS(result) )
176 result = TestHeader.InitFromFile(Reader); // test UL and OP
178 if ( ASDCP_SUCCESS(result) )
181 if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor))) )
183 if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(StereoscopicPictureSubDescriptor))) )
184 type = ESS_JPEG_2000_S;
186 type = ESS_JPEG_2000;
188 else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor))) )
189 type = ESS_PCM_24b_48k;
190 else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor))) )
191 type = ESS_MPEG2_VES;
192 else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(TimedTextDescriptor))) )
193 type = ESS_TIMED_TEXT;
194 else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(DCDataDescriptor))) )
196 if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(DolbyAtmosSubDescriptor))) )
197 type = ESS_DCDATA_DOLBY_ATMOS;
199 type = ESS_DCDATA_UNKNOWN;
208 ASDCP::RawEssenceType(const char* filename, EssenceType_t& type)
210 ASDCP_TEST_NULL_STR(filename);
212 ASDCP::FrameBuffer FB;
213 Kumu::FileReader Reader;
214 ASDCP::Wav::SimpleWaveHeader WavHeader;
215 ASDCP::RF64::SimpleRF64Header RF64Header;
216 ASDCP::AIFF::SimpleAIFFHeader AIFFHeader;
217 Kumu::XMLElement TmpElement("Tmp");
221 Result_t result = FB.Capacity(Wav::MaxWavHeader); // using Wav max because everything else is much smaller
223 if ( Kumu::PathIsFile(filename) )
225 result = Reader.OpenRead(filename);
227 if ( ASDCP_SUCCESS(result) )
229 result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
233 if ( ASDCP_SUCCESS(result) )
235 const byte_t* p = FB.RoData();
239 while ( p[i] == 0 ) i++;
241 if ( i > 1 && p[i] == 1 && (p[i+1] == ASDCP::MPEG2::SEQ_START || p[i+1] == ASDCP::MPEG2::PIC_START) )
243 type = ESS_MPEG2_VES;
245 else if ( memcmp(FB.RoData(), ASDCP::JP2K::Magic, sizeof(ASDCP::JP2K::Magic)) == 0 )
247 type = ESS_JPEG_2000;
249 else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
251 switch ( WavHeader.samplespersec )
253 case 48000: type = ESS_PCM_24b_48k; break;
254 case 96000: type = ESS_PCM_24b_96k; break;
256 return RESULT_FORMAT;
259 else if ( ASDCP_SUCCESS(RF64Header.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
261 switch ( RF64Header.samplespersec )
263 case 48000: type = ESS_PCM_24b_48k; break;
264 case 96000: type = ESS_PCM_24b_96k; break;
266 return RESULT_FORMAT;
269 else if ( ASDCP_SUCCESS(AIFFHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
271 type = ESS_PCM_24b_48k;
273 else if ( Kumu::StringIsXML((const char*)FB.RoData(), FB.Size()) )
275 type = ESS_TIMED_TEXT;
277 else if ( ASDCP::ATMOS::IsDolbyAtmos(filename) )
279 type = ESS_DCDATA_DOLBY_ATMOS;
283 else if ( Kumu::PathIsDirectory(filename) )
285 char next_file[Kumu::MaxFilePath];
286 Kumu::DirScanner Scanner;
287 Result_t result = Scanner.Open(filename);
289 if ( ASDCP_SUCCESS(result) )
291 while ( ASDCP_SUCCESS(Scanner.GetNext(next_file)) )
293 if ( next_file[0] == '.' ) // no hidden files or internal links
296 std::string Str(filename);
299 result = Reader.OpenRead(Str.c_str());
301 if ( ASDCP_SUCCESS(result) )
303 result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
307 if ( ASDCP_SUCCESS(result) )
309 if ( memcmp(FB.RoData(), ASDCP::JP2K::Magic, sizeof(ASDCP::JP2K::Magic)) == 0 )
311 type = ESS_JPEG_2000;
313 else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
315 switch ( WavHeader.samplespersec )
317 case 48000: type = ESS_PCM_24b_48k; break;
318 case 96000: type = ESS_PCM_24b_96k; break;
320 return RESULT_FORMAT;
323 else if ( ASDCP_SUCCESS(RF64Header.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
325 switch ( RF64Header.samplespersec )
327 case 48000: type = ESS_PCM_24b_48k; break;
328 case 96000: type = ESS_PCM_24b_96k; break;
330 return RESULT_FORMAT;
333 else if ( ASDCP::ATMOS::IsDolbyAtmos(Str.c_str()) )
335 type = ESS_DCDATA_DOLBY_ATMOS;
349 ASDCP::EncryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESEncContext* Ctx)
351 ASDCP_TEST_NULL(Ctx);
355 Result_t result = FBout.Capacity(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));
358 byte_t* p = FBout.Data();
360 // write the IV to the frame buffer
365 // encrypt the check value to the frame buffer
366 if ( ASDCP_SUCCESS(result) )
368 result = Ctx->EncryptBlock(ESV_CheckValue, p, CBC_BLOCK_SIZE);
372 // write optional plaintext region
373 if ( FBin.PlaintextOffset() > 0 )
375 assert(FBin.PlaintextOffset() <= FBin.Size());
376 memcpy(p, FBin.RoData(), FBin.PlaintextOffset());
377 p += FBin.PlaintextOffset();
380 ui32_t ct_size = FBin.Size() - FBin.PlaintextOffset();
381 ui32_t diff = ct_size % CBC_BLOCK_SIZE;
382 ui32_t block_size = ct_size - diff;
383 assert((block_size % CBC_BLOCK_SIZE) == 0);
385 // encrypt the ciphertext region essence data
386 if ( ASDCP_SUCCESS(result) )
388 result = Ctx->EncryptBlock(FBin.RoData() + FBin.PlaintextOffset(), p, block_size);
392 // construct and encrypt the padding
393 if ( ASDCP_SUCCESS(result) )
395 byte_t the_last_block[CBC_BLOCK_SIZE];
398 memcpy(the_last_block, FBin.RoData() + FBin.PlaintextOffset() + block_size, diff);
400 for (ui32_t i = 0; diff < CBC_BLOCK_SIZE; diff++, i++ )
401 the_last_block[diff] = i;
403 result = Ctx->EncryptBlock(the_last_block, p, CBC_BLOCK_SIZE);
406 if ( ASDCP_SUCCESS(result) )
407 FBout.Size(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));
414 ASDCP::DecryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESDecContext* Ctx)
416 ASDCP_TEST_NULL(Ctx);
417 assert(FBout.Capacity() >= FBin.SourceLength());
419 ui32_t ct_size = FBin.SourceLength() - FBin.PlaintextOffset();
420 ui32_t diff = ct_size % CBC_BLOCK_SIZE;
421 ui32_t block_size = ct_size - diff;
423 assert((block_size % CBC_BLOCK_SIZE) == 0);
425 const byte_t* buf = FBin.RoData();
429 buf += CBC_BLOCK_SIZE;
431 // decrypt and test check value
432 byte_t CheckValue[CBC_BLOCK_SIZE];
433 Result_t result = Ctx->DecryptBlock(buf, CheckValue, CBC_BLOCK_SIZE);
434 buf += CBC_BLOCK_SIZE;
436 if ( memcmp(CheckValue, ESV_CheckValue, CBC_BLOCK_SIZE) != 0 )
437 return RESULT_CHECKFAIL;
439 // copy plaintext region
440 if ( FBin.PlaintextOffset() > 0 )
442 memcpy(FBout.Data(), buf, FBin.PlaintextOffset());
443 buf += FBin.PlaintextOffset();
446 // decrypt all but last block
447 if ( ASDCP_SUCCESS(result) )
449 result = Ctx->DecryptBlock(buf, FBout.Data() + FBin.PlaintextOffset(), block_size);
453 // decrypt last block
454 if ( ASDCP_SUCCESS(result) )
456 byte_t the_last_block[CBC_BLOCK_SIZE];
457 result = Ctx->DecryptBlock(buf, the_last_block, CBC_BLOCK_SIZE);
459 if ( the_last_block[diff] != 0 )
461 DefaultLogSink().Error("Unexpected non-zero padding value.\n");
462 return RESULT_FORMAT;
466 memcpy(FBout.Data() + FBin.PlaintextOffset() + block_size, the_last_block, diff);
469 if ( ASDCP_SUCCESS(result) )
470 FBout.Size(FBin.SourceLength());
478 ASDCP::IntegrityPack::CalcValues(const ASDCP::FrameBuffer& FB, const byte_t* AssetID,
479 ui32_t sequence, HMACContext* HMAC)
481 ASDCP_TEST_NULL(AssetID);
482 ASDCP_TEST_NULL(HMAC);
486 static byte_t ber_4[MXF_BER_LENGTH] = {0x83, 0, 0, 0};
488 // update HMAC with essence data
489 HMAC->Update(FB.RoData(), FB.Size());
491 // track file ID length
492 memcpy(p, ber_4, MXF_BER_LENGTH);
497 memcpy(p, AssetID, UUIDlen);
501 memcpy(p, ber_4, MXF_BER_LENGTH);
502 *(p+3) = sizeof(ui64_t);
506 Kumu::i2p<ui64_t>(KM_i64_BE(sequence), p);
510 memcpy(p, ber_4, MXF_BER_LENGTH);
514 // update HMAC with intpack values
515 HMAC->Update(Data, klv_intpack_size - HMAC_SIZE);
517 // finish & write HMAC
519 HMAC->GetHMACValue(p);
521 assert(p + HMAC_SIZE == Data + klv_intpack_size);
528 ASDCP::IntegrityPack::TestValues(const ASDCP::FrameBuffer& FB, const byte_t* AssetID,
529 ui32_t sequence, HMACContext* HMAC)
531 ASDCP_TEST_NULL(AssetID);
532 ASDCP_TEST_NULL(HMAC);
534 // find the start of the intpack
535 byte_t* p = (byte_t*)FB.RoData() + ( FB.Size() - klv_intpack_size );
537 // test the AssetID length
538 if ( ! Kumu::read_test_BER(&p, UUIDlen) )
539 return RESULT_HMACFAIL;
542 if ( memcmp(p, AssetID, UUIDlen) != 0 )
544 DefaultLogSink().Error("IntegrityPack failure: AssetID mismatch.\n");
545 return RESULT_HMACFAIL;
549 // test the sequence length
550 if ( ! Kumu::read_test_BER(&p, sizeof(ui64_t)) )
551 return RESULT_HMACFAIL;
553 ui32_t test_sequence = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(p));
555 // test the sequence value
556 if ( test_sequence != sequence )
558 DefaultLogSink().Error("IntegrityPack failure: sequence is %u, expecting %u.\n", test_sequence, sequence);
559 return RESULT_HMACFAIL;
564 // test the HMAC length
565 if ( ! Kumu::read_test_BER(&p, HMAC_SIZE) )
566 return RESULT_HMACFAIL;
570 HMAC->Update(FB.RoData(), FB.Size() - HMAC_SIZE);
573 return HMAC->TestHMACValue(p);
577 // end AS_DCP_MXF.cpp