2 Copyright (c) 2004-2006, 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>
33 #include "AS_DCP_internal.h"
38 //------------------------------------------------------------------------------------------
44 ASDCP::WriterInfoDump(const WriterInfo& Info, FILE* stream)
51 fprintf(stream," ProductUUID: %s\n", UUID(Info.ProductUUID).EncodeHex(str_buf, 40));
56 EncryptedEssence: %s\n",
57 Info.ProductVersion.c_str(),
58 Info.CompanyName.c_str(),
59 Info.ProductName.c_str(),
60 ( Info.EncryptedEssence ? "Yes" : "No" )
63 if ( Info.EncryptedEssence )
65 fprintf(stream, " HMAC: %s\n", ( Info.UsesHMAC ? "Yes" : "No"));
66 fprintf(stream, " ContextID: %s\n", UUID(Info.ContextID).EncodeHex(str_buf, 40));
67 fprintf(stream, "CryptographicKeyID: %s\n", UUID(Info.CryptographicKeyID).EncodeHex(str_buf, 40));
70 fprintf(stream," AssetUUID: %s\n", UUID(Info.AssetUUID).EncodeHex(str_buf, 40));
71 fprintf(stream," Label Set Type: %s\n", ( Info.LabelSetType == LS_MXF_SMPTE ? "SMPTE" :
72 ( Info.LabelSetType == LS_MXF_INTEROP ? "MXF Interop" :
78 ASDCP::MD_to_WriterInfo(Identification* InfoObj, WriterInfo& Info)
80 ASDCP_TEST_NULL(InfoObj);
81 char tmp_str[IdentBufferLen];
83 Info.ProductName = "Unknown Product";
84 Info.ProductVersion = "Unknown Version";
85 Info.CompanyName = "Unknown Company";
86 memset(Info.ProductUUID, 0, UUIDlen);
88 InfoObj->ProductName.EncodeString(tmp_str, IdentBufferLen);
89 if ( *tmp_str ) Info.ProductName = tmp_str;
91 InfoObj->VersionString.EncodeString(tmp_str, IdentBufferLen);
92 if ( *tmp_str ) Info.ProductVersion = tmp_str;
94 InfoObj->CompanyName.EncodeString(tmp_str, IdentBufferLen);
95 if ( *tmp_str ) Info.CompanyName = tmp_str;
97 memcpy(Info.ProductUUID, InfoObj->ProductUID.Value(), UUIDlen);
105 ASDCP::MD_to_CryptoInfo(CryptographicContext* InfoObj, WriterInfo& Info)
107 ASDCP_TEST_NULL(InfoObj);
109 Info.EncryptedEssence = true;
110 memcpy(Info.ContextID, InfoObj->ContextID.Value(), UUIDlen);
111 memcpy(Info.CryptographicKeyID, InfoObj->CryptographicKeyID.Value(), UUIDlen);
113 UL MIC_SHA1(Dict::ul(MDD_MICAlgorithm_HMAC_SHA1));
114 UL MIC_NONE(Dict::ul(MDD_MICAlgorithm_NONE));
116 if ( InfoObj->MICAlgorithm == MIC_SHA1 )
117 Info.UsesHMAC = true;
119 else if ( InfoObj->MICAlgorithm == MIC_NONE )
120 Info.UsesHMAC = false;
124 DefaultLogSink().Error("Unexpected MICAlgorithm UL.\n");
125 return RESULT_FORMAT;
134 ASDCP::EssenceType(const char* filename, EssenceType_t& type)
136 ASDCP_TEST_NULL_STR(filename);
137 Kumu::FileReader Reader;
138 OPAtomHeader TestHeader;
140 Result_t result = Reader.OpenRead(filename);
142 if ( ASDCP_SUCCESS(result) )
143 result = TestHeader.InitFromFile(Reader); // test UL and OP
145 if ( ASDCP_SUCCESS(result) )
148 if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor))) )
149 type = ESS_JPEG_2000;
152 if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor))) )
153 type = ESS_PCM_24b_48k;
156 if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor))) )
157 type = ESS_MPEG2_VES;
167 ASDCP::RawEssenceType(const char* filename, EssenceType_t& type)
169 ASDCP_TEST_NULL_STR(filename);
171 ASDCP::FrameBuffer FB;
172 Kumu::FileReader Reader;
174 Result_t result = FB.Capacity(Wav::MaxWavHeader); // using Wav max because everything else is much smaller
176 if ( Kumu::PathIsFile(filename) )
178 result = Reader.OpenRead(filename);
180 if ( ASDCP_SUCCESS(result) )
182 result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
186 if ( ASDCP_SUCCESS(result) )
188 ASDCP::Wav::SimpleWaveHeader WavHeader;
189 ASDCP::AIFF::SimpleAIFFHeader AIFFHeader;
191 const byte_t* p = FB.RoData();
193 if ( p[0] == 0 && p[1] == 0 && p[2] == 1 && (p[3] == 0xb3 || p[3] == 0) )
194 type = ESS_MPEG2_VES;
196 else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(p, read_count, &data_offset)) )
197 type = ESS_PCM_24b_48k;
199 else if ( ASDCP_SUCCESS(AIFFHeader.ReadFromBuffer(p, read_count, &data_offset)) )
200 type = ESS_PCM_24b_48k;
203 else if ( Kumu::PathIsDirectory(filename) )
205 char next_file[Kumu::MaxFilePath];
206 Kumu::DirScanner Scanner;
207 Result_t result = Scanner.Open(filename);
209 if ( ASDCP_SUCCESS(result) )
211 while ( ASDCP_SUCCESS(Scanner.GetNext(next_file)) )
213 if ( next_file[0] == '.' ) // no hidden files or internal links
216 std::string Str(filename);
219 result = Reader.OpenRead(Str.c_str());
221 if ( ASDCP_SUCCESS(result) )
223 result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
227 if ( ASDCP_SUCCESS(result)
228 && ( memcmp(FB.RoData(), ASDCP::JP2K::Magic, sizeof(ASDCP::JP2K::Magic)) == 0 ) )
229 type = ESS_JPEG_2000;
241 ASDCP::EncryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESEncContext* Ctx)
243 ASDCP_TEST_NULL(Ctx);
247 Result_t result = FBout.Capacity(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));
250 byte_t* p = FBout.Data();
252 // write the IV to the frame buffer
257 // encrypt the check value to the frame buffer
258 if ( ASDCP_SUCCESS(result) )
260 result = Ctx->EncryptBlock(ESV_CheckValue, p, CBC_BLOCK_SIZE);
264 // write optional plaintext region
265 if ( FBin.PlaintextOffset() > 0 )
267 assert(FBin.PlaintextOffset() <= FBin.Size());
268 memcpy(p, FBin.RoData(), FBin.PlaintextOffset());
269 p += FBin.PlaintextOffset();
272 ui32_t ct_size = FBin.Size() - FBin.PlaintextOffset();
273 ui32_t diff = ct_size % CBC_BLOCK_SIZE;
274 ui32_t block_size = ct_size - diff;
275 assert((block_size % CBC_BLOCK_SIZE) == 0);
277 // encrypt the ciphertext region essence data
278 if ( ASDCP_SUCCESS(result) )
280 result = Ctx->EncryptBlock(FBin.RoData() + FBin.PlaintextOffset(), p, block_size);
284 // construct and encrypt the padding
285 if ( ASDCP_SUCCESS(result) )
287 byte_t the_last_block[CBC_BLOCK_SIZE];
290 memcpy(the_last_block, FBin.RoData() + FBin.PlaintextOffset() + block_size, diff);
292 for (ui32_t i = 0; diff < CBC_BLOCK_SIZE; diff++, i++ )
293 the_last_block[diff] = i;
295 result = Ctx->EncryptBlock(the_last_block, p, CBC_BLOCK_SIZE);
298 if ( ASDCP_SUCCESS(result) )
299 FBout.Size(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));
306 ASDCP::DecryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESDecContext* Ctx)
308 ASDCP_TEST_NULL(Ctx);
309 assert(FBout.Capacity() >= FBin.SourceLength());
311 ui32_t ct_size = FBin.SourceLength() - FBin.PlaintextOffset();
312 ui32_t diff = ct_size % CBC_BLOCK_SIZE;
313 ui32_t block_size = ct_size - diff;
315 assert((block_size % CBC_BLOCK_SIZE) == 0);
317 const byte_t* buf = FBin.RoData();
321 buf += CBC_BLOCK_SIZE;
323 // decrypt and test check value
324 byte_t CheckValue[CBC_BLOCK_SIZE];
325 Result_t result = Ctx->DecryptBlock(buf, CheckValue, CBC_BLOCK_SIZE);
326 buf += CBC_BLOCK_SIZE;
328 if ( memcmp(CheckValue, ESV_CheckValue, CBC_BLOCK_SIZE) != 0 )
329 return RESULT_CHECKFAIL;
331 // copy plaintext region
332 if ( FBin.PlaintextOffset() > 0 )
334 memcpy(FBout.Data(), buf, FBin.PlaintextOffset());
335 buf += FBin.PlaintextOffset();
338 // decrypt all but last block
339 if ( ASDCP_SUCCESS(result) )
341 result = Ctx->DecryptBlock(buf, FBout.Data() + FBin.PlaintextOffset(), block_size);
345 // decrypt last block
346 if ( ASDCP_SUCCESS(result) )
348 byte_t the_last_block[CBC_BLOCK_SIZE];
349 result = Ctx->DecryptBlock(buf, the_last_block, CBC_BLOCK_SIZE);
351 if ( the_last_block[diff] != 0 )
353 DefaultLogSink().Error("Unexpected non-zero padding value.\n");
354 return RESULT_FORMAT;
358 memcpy(FBout.Data() + FBin.PlaintextOffset() + block_size, the_last_block, diff);
361 if ( ASDCP_SUCCESS(result) )
362 FBout.Size(FBin.SourceLength());
370 ASDCP::IntegrityPack::CalcValues(const ASDCP::FrameBuffer& FB, byte_t* AssetID,
371 ui32_t sequence, HMACContext* HMAC)
373 ASDCP_TEST_NULL(AssetID);
374 ASDCP_TEST_NULL(HMAC);
378 static byte_t ber_4[MXF_BER_LENGTH] = {0x83, 0};
380 // update HMAC with essence data
381 HMAC->Update(FB.RoData(), FB.Size());
383 // track file ID length
384 memcpy(p, ber_4, MXF_BER_LENGTH);
389 memcpy(p, AssetID, UUIDlen);
393 memcpy(p, ber_4, MXF_BER_LENGTH);
394 *(p+3) = sizeof(ui64_t);
398 Kumu::i2p<ui64_t>(KM_i64_BE(sequence), p);
402 memcpy(p, ber_4, MXF_BER_LENGTH);
406 // update HMAC with intpack values
407 HMAC->Update(Data, klv_intpack_size - HMAC_SIZE);
409 // finish & write HMAC
411 HMAC->GetHMACValue(p);
413 assert(p + HMAC_SIZE == Data + klv_intpack_size);
420 ASDCP::IntegrityPack::TestValues(const ASDCP::FrameBuffer& FB, byte_t* AssetID,
421 ui32_t sequence, HMACContext* HMAC)
423 ASDCP_TEST_NULL(AssetID);
424 ASDCP_TEST_NULL(HMAC);
426 // find the start of the intpack
427 byte_t* p = (byte_t*)FB.RoData() + ( FB.Size() - klv_intpack_size );
429 // test the AssetID length
430 if ( ! Kumu::read_test_BER(&p, UUIDlen) )
431 return RESULT_HMACFAIL;
434 if ( memcmp(p, AssetID, UUIDlen) != 0 )
436 DefaultLogSink().Error("IntegrityPack failure: AssetID mismatch.\n");
437 return RESULT_HMACFAIL;
441 // test the sequence length
442 if ( ! Kumu::read_test_BER(&p, sizeof(ui64_t)) )
443 return RESULT_HMACFAIL;
445 ui32_t test_sequence = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(p));
447 // test the sequence value
448 if ( test_sequence != sequence )
450 DefaultLogSink().Error("IntegrityPack failure: sequence is %u, expecting %u.\n", test_sequence, sequence);
451 return RESULT_HMACFAIL;
456 // test the HMAC length
457 if ( ! Kumu::read_test_BER(&p, HMAC_SIZE) )
458 return RESULT_HMACFAIL;
462 HMAC->Update(FB.RoData(), FB.Size() - HMAC_SIZE);
465 return HMAC->TestHMACValue(p);
469 // end AS_DCP_MXF.cpp