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))) )
152 if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(StereoscopicPictureSubDescriptor))) )
153 type = ESS_JPEG_2000_S;
155 type = ESS_JPEG_2000;
157 else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor))) )
158 type = ESS_PCM_24b_48k;
159 else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor))) )
160 type = ESS_MPEG2_VES;
161 else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(TimedTextDescriptor))) )
162 type = ESS_TIMED_TEXT;
170 ASDCP::RawEssenceType(const char* filename, EssenceType_t& type)
172 ASDCP_TEST_NULL_STR(filename);
174 ASDCP::FrameBuffer FB;
175 Kumu::FileReader Reader;
176 ASDCP::Wav::SimpleWaveHeader WavHeader;
177 ASDCP::AIFF::SimpleAIFFHeader AIFFHeader;
178 Kumu::XMLElement TmpElement("Tmp");
182 Result_t result = FB.Capacity(Wav::MaxWavHeader); // using Wav max because everything else is much smaller
184 if ( Kumu::PathIsFile(filename) )
186 result = Reader.OpenRead(filename);
188 if ( ASDCP_SUCCESS(result) )
190 result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
194 if ( ASDCP_SUCCESS(result) )
196 const byte_t* p = FB.RoData();
200 while ( p[i] == 0 ) i++;
202 if ( i > 1 && p[i] == 1 && (p[i+1] == ASDCP::MPEG2::SEQ_START || p[i+1] == ASDCP::MPEG2::PIC_START) )
203 type = ESS_MPEG2_VES;
205 else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(p, read_count, &data_offset)) )
206 type = ESS_PCM_24b_48k;
208 else if ( ASDCP_SUCCESS(AIFFHeader.ReadFromBuffer(p, read_count, &data_offset)) )
209 type = ESS_PCM_24b_48k;
211 else if ( Kumu::StringIsXML((const char*)p, FB.Size()) )
212 type = ESS_TIMED_TEXT;
215 else if ( Kumu::PathIsDirectory(filename) )
217 char next_file[Kumu::MaxFilePath];
218 Kumu::DirScanner Scanner;
219 Result_t result = Scanner.Open(filename);
221 if ( ASDCP_SUCCESS(result) )
223 while ( ASDCP_SUCCESS(Scanner.GetNext(next_file)) )
225 if ( next_file[0] == '.' ) // no hidden files or internal links
228 std::string Str(filename);
231 result = Reader.OpenRead(Str.c_str());
233 if ( ASDCP_SUCCESS(result) )
235 result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
239 if ( ASDCP_SUCCESS(result) )
241 if ( memcmp(FB.RoData(), ASDCP::JP2K::Magic, sizeof(ASDCP::JP2K::Magic)) == 0 )
242 type = ESS_JPEG_2000;
244 else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
245 type = ESS_PCM_24b_48k;
258 ASDCP::EncryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESEncContext* Ctx)
260 ASDCP_TEST_NULL(Ctx);
264 Result_t result = FBout.Capacity(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));
267 byte_t* p = FBout.Data();
269 // write the IV to the frame buffer
274 // encrypt the check value to the frame buffer
275 if ( ASDCP_SUCCESS(result) )
277 result = Ctx->EncryptBlock(ESV_CheckValue, p, CBC_BLOCK_SIZE);
281 // write optional plaintext region
282 if ( FBin.PlaintextOffset() > 0 )
284 assert(FBin.PlaintextOffset() <= FBin.Size());
285 memcpy(p, FBin.RoData(), FBin.PlaintextOffset());
286 p += FBin.PlaintextOffset();
289 ui32_t ct_size = FBin.Size() - FBin.PlaintextOffset();
290 ui32_t diff = ct_size % CBC_BLOCK_SIZE;
291 ui32_t block_size = ct_size - diff;
292 assert((block_size % CBC_BLOCK_SIZE) == 0);
294 // encrypt the ciphertext region essence data
295 if ( ASDCP_SUCCESS(result) )
297 result = Ctx->EncryptBlock(FBin.RoData() + FBin.PlaintextOffset(), p, block_size);
301 // construct and encrypt the padding
302 if ( ASDCP_SUCCESS(result) )
304 byte_t the_last_block[CBC_BLOCK_SIZE];
307 memcpy(the_last_block, FBin.RoData() + FBin.PlaintextOffset() + block_size, diff);
309 for (ui32_t i = 0; diff < CBC_BLOCK_SIZE; diff++, i++ )
310 the_last_block[diff] = i;
312 result = Ctx->EncryptBlock(the_last_block, p, CBC_BLOCK_SIZE);
315 if ( ASDCP_SUCCESS(result) )
316 FBout.Size(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));
323 ASDCP::DecryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESDecContext* Ctx)
325 ASDCP_TEST_NULL(Ctx);
326 assert(FBout.Capacity() >= FBin.SourceLength());
328 ui32_t ct_size = FBin.SourceLength() - FBin.PlaintextOffset();
329 ui32_t diff = ct_size % CBC_BLOCK_SIZE;
330 ui32_t block_size = ct_size - diff;
332 assert((block_size % CBC_BLOCK_SIZE) == 0);
334 const byte_t* buf = FBin.RoData();
338 buf += CBC_BLOCK_SIZE;
340 // decrypt and test check value
341 byte_t CheckValue[CBC_BLOCK_SIZE];
342 Result_t result = Ctx->DecryptBlock(buf, CheckValue, CBC_BLOCK_SIZE);
343 buf += CBC_BLOCK_SIZE;
345 if ( memcmp(CheckValue, ESV_CheckValue, CBC_BLOCK_SIZE) != 0 )
346 return RESULT_CHECKFAIL;
348 // copy plaintext region
349 if ( FBin.PlaintextOffset() > 0 )
351 memcpy(FBout.Data(), buf, FBin.PlaintextOffset());
352 buf += FBin.PlaintextOffset();
355 // decrypt all but last block
356 if ( ASDCP_SUCCESS(result) )
358 result = Ctx->DecryptBlock(buf, FBout.Data() + FBin.PlaintextOffset(), block_size);
362 // decrypt last block
363 if ( ASDCP_SUCCESS(result) )
365 byte_t the_last_block[CBC_BLOCK_SIZE];
366 result = Ctx->DecryptBlock(buf, the_last_block, CBC_BLOCK_SIZE);
368 if ( the_last_block[diff] != 0 )
370 DefaultLogSink().Error("Unexpected non-zero padding value.\n");
371 return RESULT_FORMAT;
375 memcpy(FBout.Data() + FBin.PlaintextOffset() + block_size, the_last_block, diff);
378 if ( ASDCP_SUCCESS(result) )
379 FBout.Size(FBin.SourceLength());
387 ASDCP::IntegrityPack::CalcValues(const ASDCP::FrameBuffer& FB, byte_t* AssetID,
388 ui32_t sequence, HMACContext* HMAC)
390 ASDCP_TEST_NULL(AssetID);
391 ASDCP_TEST_NULL(HMAC);
395 static byte_t ber_4[MXF_BER_LENGTH] = {0x83, 0};
397 // update HMAC with essence data
398 HMAC->Update(FB.RoData(), FB.Size());
400 // track file ID length
401 memcpy(p, ber_4, MXF_BER_LENGTH);
406 memcpy(p, AssetID, UUIDlen);
410 memcpy(p, ber_4, MXF_BER_LENGTH);
411 *(p+3) = sizeof(ui64_t);
415 Kumu::i2p<ui64_t>(KM_i64_BE(sequence), p);
419 memcpy(p, ber_4, MXF_BER_LENGTH);
423 // update HMAC with intpack values
424 HMAC->Update(Data, klv_intpack_size - HMAC_SIZE);
426 // finish & write HMAC
428 HMAC->GetHMACValue(p);
430 assert(p + HMAC_SIZE == Data + klv_intpack_size);
437 ASDCP::IntegrityPack::TestValues(const ASDCP::FrameBuffer& FB, byte_t* AssetID,
438 ui32_t sequence, HMACContext* HMAC)
440 ASDCP_TEST_NULL(AssetID);
441 ASDCP_TEST_NULL(HMAC);
443 // find the start of the intpack
444 byte_t* p = (byte_t*)FB.RoData() + ( FB.Size() - klv_intpack_size );
446 // test the AssetID length
447 if ( ! Kumu::read_test_BER(&p, UUIDlen) )
448 return RESULT_HMACFAIL;
451 if ( memcmp(p, AssetID, UUIDlen) != 0 )
453 DefaultLogSink().Error("IntegrityPack failure: AssetID mismatch.\n");
454 return RESULT_HMACFAIL;
458 // test the sequence length
459 if ( ! Kumu::read_test_BER(&p, sizeof(ui64_t)) )
460 return RESULT_HMACFAIL;
462 ui32_t test_sequence = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(p));
464 // test the sequence value
465 if ( test_sequence != sequence )
467 DefaultLogSink().Error("IntegrityPack failure: sequence is %u, expecting %u.\n", test_sequence, sequence);
468 return RESULT_HMACFAIL;
473 // test the HMAC length
474 if ( ! Kumu::read_test_BER(&p, HMAC_SIZE) )
475 return RESULT_HMACFAIL;
479 HMAC->Update(FB.RoData(), FB.Size() - HMAC_SIZE);
482 return HMAC->TestHMACValue(p);
486 // end AS_DCP_MXF.cpp