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"
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)
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 ASDCP_TEST_NULL_STR(filename);
167 Kumu::FileReader Reader;
168 OPAtomHeader TestHeader;
170 Result_t result = Reader.OpenRead(filename);
172 if ( ASDCP_SUCCESS(result) )
173 result = TestHeader.InitFromFile(Reader); // test UL and OP
175 if ( ASDCP_SUCCESS(result) )
178 if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor))) )
180 if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(StereoscopicPictureSubDescriptor))) )
181 type = ESS_JPEG_2000_S;
183 type = ESS_JPEG_2000;
185 else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor))) )
186 type = ESS_PCM_24b_48k;
187 else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor))) )
188 type = ESS_MPEG2_VES;
189 else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(TimedTextDescriptor))) )
190 type = ESS_TIMED_TEXT;
198 ASDCP::RawEssenceType(const char* filename, EssenceType_t& type)
200 ASDCP_TEST_NULL_STR(filename);
202 ASDCP::FrameBuffer FB;
203 Kumu::FileReader Reader;
204 ASDCP::Wav::SimpleWaveHeader WavHeader;
205 ASDCP::AIFF::SimpleAIFFHeader AIFFHeader;
206 Kumu::XMLElement TmpElement("Tmp");
210 Result_t result = FB.Capacity(Wav::MaxWavHeader); // using Wav max because everything else is much smaller
212 if ( Kumu::PathIsFile(filename) )
214 result = Reader.OpenRead(filename);
216 if ( ASDCP_SUCCESS(result) )
218 result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
222 if ( ASDCP_SUCCESS(result) )
224 const byte_t* p = FB.RoData();
228 while ( p[i] == 0 ) i++;
230 if ( i > 1 && p[i] == 1 && (p[i+1] == ASDCP::MPEG2::SEQ_START || p[i+1] == ASDCP::MPEG2::PIC_START) )
231 type = ESS_MPEG2_VES;
233 else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(p, read_count, &data_offset)) )
234 type = ESS_PCM_24b_48k;
236 else if ( ASDCP_SUCCESS(AIFFHeader.ReadFromBuffer(p, read_count, &data_offset)) )
237 type = ESS_PCM_24b_48k;
239 else if ( Kumu::StringIsXML((const char*)p, FB.Size()) )
240 type = ESS_TIMED_TEXT;
243 else if ( Kumu::PathIsDirectory(filename) )
245 char next_file[Kumu::MaxFilePath];
246 Kumu::DirScanner Scanner;
247 Result_t result = Scanner.Open(filename);
249 if ( ASDCP_SUCCESS(result) )
251 while ( ASDCP_SUCCESS(Scanner.GetNext(next_file)) )
253 if ( next_file[0] == '.' ) // no hidden files or internal links
256 std::string Str(filename);
259 result = Reader.OpenRead(Str.c_str());
261 if ( ASDCP_SUCCESS(result) )
263 result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
267 if ( ASDCP_SUCCESS(result) )
269 if ( memcmp(FB.RoData(), ASDCP::JP2K::Magic, sizeof(ASDCP::JP2K::Magic)) == 0 )
270 type = ESS_JPEG_2000;
272 else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
273 type = ESS_PCM_24b_48k;
286 ASDCP::EncryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESEncContext* Ctx)
288 ASDCP_TEST_NULL(Ctx);
292 Result_t result = FBout.Capacity(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));
295 byte_t* p = FBout.Data();
297 // write the IV to the frame buffer
302 // encrypt the check value to the frame buffer
303 if ( ASDCP_SUCCESS(result) )
305 result = Ctx->EncryptBlock(ESV_CheckValue, p, CBC_BLOCK_SIZE);
309 // write optional plaintext region
310 if ( FBin.PlaintextOffset() > 0 )
312 assert(FBin.PlaintextOffset() <= FBin.Size());
313 memcpy(p, FBin.RoData(), FBin.PlaintextOffset());
314 p += FBin.PlaintextOffset();
317 ui32_t ct_size = FBin.Size() - FBin.PlaintextOffset();
318 ui32_t diff = ct_size % CBC_BLOCK_SIZE;
319 ui32_t block_size = ct_size - diff;
320 assert((block_size % CBC_BLOCK_SIZE) == 0);
322 // encrypt the ciphertext region essence data
323 if ( ASDCP_SUCCESS(result) )
325 result = Ctx->EncryptBlock(FBin.RoData() + FBin.PlaintextOffset(), p, block_size);
329 // construct and encrypt the padding
330 if ( ASDCP_SUCCESS(result) )
332 byte_t the_last_block[CBC_BLOCK_SIZE];
335 memcpy(the_last_block, FBin.RoData() + FBin.PlaintextOffset() + block_size, diff);
337 for (ui32_t i = 0; diff < CBC_BLOCK_SIZE; diff++, i++ )
338 the_last_block[diff] = i;
340 result = Ctx->EncryptBlock(the_last_block, p, CBC_BLOCK_SIZE);
343 if ( ASDCP_SUCCESS(result) )
344 FBout.Size(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));
351 ASDCP::DecryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESDecContext* Ctx)
353 ASDCP_TEST_NULL(Ctx);
354 assert(FBout.Capacity() >= FBin.SourceLength());
356 ui32_t ct_size = FBin.SourceLength() - FBin.PlaintextOffset();
357 ui32_t diff = ct_size % CBC_BLOCK_SIZE;
358 ui32_t block_size = ct_size - diff;
360 assert((block_size % CBC_BLOCK_SIZE) == 0);
362 const byte_t* buf = FBin.RoData();
366 buf += CBC_BLOCK_SIZE;
368 // decrypt and test check value
369 byte_t CheckValue[CBC_BLOCK_SIZE];
370 Result_t result = Ctx->DecryptBlock(buf, CheckValue, CBC_BLOCK_SIZE);
371 buf += CBC_BLOCK_SIZE;
373 if ( memcmp(CheckValue, ESV_CheckValue, CBC_BLOCK_SIZE) != 0 )
374 return RESULT_CHECKFAIL;
376 // copy plaintext region
377 if ( FBin.PlaintextOffset() > 0 )
379 memcpy(FBout.Data(), buf, FBin.PlaintextOffset());
380 buf += FBin.PlaintextOffset();
383 // decrypt all but last block
384 if ( ASDCP_SUCCESS(result) )
386 result = Ctx->DecryptBlock(buf, FBout.Data() + FBin.PlaintextOffset(), block_size);
390 // decrypt last block
391 if ( ASDCP_SUCCESS(result) )
393 byte_t the_last_block[CBC_BLOCK_SIZE];
394 result = Ctx->DecryptBlock(buf, the_last_block, CBC_BLOCK_SIZE);
396 if ( the_last_block[diff] != 0 )
398 DefaultLogSink().Error("Unexpected non-zero padding value.\n");
399 return RESULT_FORMAT;
403 memcpy(FBout.Data() + FBin.PlaintextOffset() + block_size, the_last_block, diff);
406 if ( ASDCP_SUCCESS(result) )
407 FBout.Size(FBin.SourceLength());
415 ASDCP::IntegrityPack::CalcValues(const ASDCP::FrameBuffer& FB, byte_t* AssetID,
416 ui32_t sequence, HMACContext* HMAC)
418 ASDCP_TEST_NULL(AssetID);
419 ASDCP_TEST_NULL(HMAC);
423 static byte_t ber_4[MXF_BER_LENGTH] = {0x83, 0};
425 // update HMAC with essence data
426 HMAC->Update(FB.RoData(), FB.Size());
428 // track file ID length
429 memcpy(p, ber_4, MXF_BER_LENGTH);
434 memcpy(p, AssetID, UUIDlen);
438 memcpy(p, ber_4, MXF_BER_LENGTH);
439 *(p+3) = sizeof(ui64_t);
443 Kumu::i2p<ui64_t>(KM_i64_BE(sequence), p);
447 memcpy(p, ber_4, MXF_BER_LENGTH);
451 // update HMAC with intpack values
452 HMAC->Update(Data, klv_intpack_size - HMAC_SIZE);
454 // finish & write HMAC
456 HMAC->GetHMACValue(p);
458 assert(p + HMAC_SIZE == Data + klv_intpack_size);
465 ASDCP::IntegrityPack::TestValues(const ASDCP::FrameBuffer& FB, byte_t* AssetID,
466 ui32_t sequence, HMACContext* HMAC)
468 ASDCP_TEST_NULL(AssetID);
469 ASDCP_TEST_NULL(HMAC);
471 // find the start of the intpack
472 byte_t* p = (byte_t*)FB.RoData() + ( FB.Size() - klv_intpack_size );
474 // test the AssetID length
475 if ( ! Kumu::read_test_BER(&p, UUIDlen) )
476 return RESULT_HMACFAIL;
479 if ( memcmp(p, AssetID, UUIDlen) != 0 )
481 DefaultLogSink().Error("IntegrityPack failure: AssetID mismatch.\n");
482 return RESULT_HMACFAIL;
486 // test the sequence length
487 if ( ! Kumu::read_test_BER(&p, sizeof(ui64_t)) )
488 return RESULT_HMACFAIL;
490 ui32_t test_sequence = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(p));
492 // test the sequence value
493 if ( test_sequence != sequence )
495 DefaultLogSink().Error("IntegrityPack failure: sequence is %u, expecting %u.\n", test_sequence, sequence);
496 return RESULT_HMACFAIL;
501 // test the HMAC length
502 if ( ! Kumu::read_test_BER(&p, HMAC_SIZE) )
503 return RESULT_HMACFAIL;
507 HMAC->Update(FB.RoData(), FB.Size() - HMAC_SIZE);
510 return HMAC->TestHMACValue(p);
514 // end AS_DCP_MXF.cpp