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"
39 //------------------------------------------------------------------------------------------
45 ASDCP::WriterInfoDump(const WriterInfo& Info, FILE* stream)
52 fprintf(stream," ProductUUID: %s\n", UUID(Info.ProductUUID).EncodeHex(str_buf, 40));
57 EncryptedEssence: %s\n",
58 Info.ProductVersion.c_str(),
59 Info.CompanyName.c_str(),
60 Info.ProductName.c_str(),
61 ( Info.EncryptedEssence ? "Yes" : "No" )
64 if ( Info.EncryptedEssence )
66 fprintf(stream, " HMAC: %s\n", ( Info.UsesHMAC ? "Yes" : "No"));
67 fprintf(stream, " ContextID: %s\n", UUID(Info.ContextID).EncodeHex(str_buf, 40));
68 fprintf(stream, "CryptographicKeyID: %s\n", UUID(Info.CryptographicKeyID).EncodeHex(str_buf, 40));
71 fprintf(stream," AssetUUID: %s\n", UUID(Info.AssetUUID).EncodeHex(str_buf, 40));
72 fprintf(stream," Label Set Type: %s\n", ( Info.LabelSetType == LS_MXF_SMPTE ? "SMPTE" :
73 ( Info.LabelSetType == LS_MXF_INTEROP ? "MXF Interop" :
79 ASDCP::MD_to_WriterInfo(Identification* InfoObj, WriterInfo& Info)
81 ASDCP_TEST_NULL(InfoObj);
82 char tmp_str[IdentBufferLen];
84 Info.ProductName = "Unknown Product";
85 Info.ProductVersion = "Unknown Version";
86 Info.CompanyName = "Unknown Company";
87 memset(Info.ProductUUID, 0, UUIDlen);
89 InfoObj->ProductName.EncodeString(tmp_str, IdentBufferLen);
90 if ( *tmp_str ) Info.ProductName = tmp_str;
92 InfoObj->VersionString.EncodeString(tmp_str, IdentBufferLen);
93 if ( *tmp_str ) Info.ProductVersion = tmp_str;
95 InfoObj->CompanyName.EncodeString(tmp_str, IdentBufferLen);
96 if ( *tmp_str ) Info.CompanyName = tmp_str;
98 memcpy(Info.ProductUUID, InfoObj->ProductUID.Value(), UUIDlen);
106 ASDCP::MD_to_CryptoInfo(CryptographicContext* InfoObj, WriterInfo& Info)
108 ASDCP_TEST_NULL(InfoObj);
110 Info.EncryptedEssence = true;
111 memcpy(Info.ContextID, InfoObj->ContextID.Value(), UUIDlen);
112 memcpy(Info.CryptographicKeyID, InfoObj->CryptographicKeyID.Value(), UUIDlen);
114 UL MIC_SHA1(Dict::ul(MDD_MICAlgorithm_HMAC_SHA1));
115 UL MIC_NONE(Dict::ul(MDD_MICAlgorithm_NONE));
117 if ( InfoObj->MICAlgorithm == MIC_SHA1 )
118 Info.UsesHMAC = true;
120 else if ( InfoObj->MICAlgorithm == MIC_NONE )
121 Info.UsesHMAC = false;
125 DefaultLogSink().Error("Unexpected MICAlgorithm UL.\n");
126 return RESULT_FORMAT;
135 ASDCP::EssenceType(const char* filename, EssenceType_t& type)
137 ASDCP_TEST_NULL_STR(filename);
138 Kumu::FileReader Reader;
139 OPAtomHeader TestHeader;
141 Result_t result = Reader.OpenRead(filename);
143 if ( ASDCP_SUCCESS(result) )
144 result = TestHeader.InitFromFile(Reader); // test UL and OP
146 if ( ASDCP_SUCCESS(result) )
149 if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor))) )
150 type = ESS_JPEG_2000;
153 if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor))) )
154 type = ESS_PCM_24b_48k;
157 if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor))) )
158 type = ESS_MPEG2_VES;
168 ASDCP::RawEssenceType(const char* filename, EssenceType_t& type)
170 ASDCP_TEST_NULL_STR(filename);
172 ASDCP::FrameBuffer FB;
173 Kumu::FileReader Reader;
174 ASDCP::Wav::SimpleWaveHeader WavHeader;
175 ASDCP::AIFF::SimpleAIFFHeader AIFFHeader;
178 Result_t result = FB.Capacity(Wav::MaxWavHeader); // using Wav max because everything else is much smaller
180 if ( Kumu::PathIsFile(filename) )
182 result = Reader.OpenRead(filename);
184 if ( ASDCP_SUCCESS(result) )
186 result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
190 if ( ASDCP_SUCCESS(result) )
192 const byte_t* p = FB.RoData();
194 while ( p[i] == 0 ) i++;
196 if ( i > 1 && p[i] == 1 && (p[i+1] == ASDCP::MPEG2::SEQ_START || p[i+1] == ASDCP::MPEG2::PIC_START) )
197 type = ESS_MPEG2_VES;
199 else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(p, read_count, &data_offset)) )
200 type = ESS_PCM_24b_48k;
202 else if ( ASDCP_SUCCESS(AIFFHeader.ReadFromBuffer(p, read_count, &data_offset)) )
203 type = ESS_PCM_24b_48k;
206 else if ( Kumu::PathIsDirectory(filename) )
208 char next_file[Kumu::MaxFilePath];
209 Kumu::DirScanner Scanner;
210 Result_t result = Scanner.Open(filename);
212 if ( ASDCP_SUCCESS(result) )
214 while ( ASDCP_SUCCESS(Scanner.GetNext(next_file)) )
216 if ( next_file[0] == '.' ) // no hidden files or internal links
219 std::string Str(filename);
222 result = Reader.OpenRead(Str.c_str());
224 if ( ASDCP_SUCCESS(result) )
226 result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
230 if ( ASDCP_SUCCESS(result) )
232 if ( memcmp(FB.RoData(), ASDCP::JP2K::Magic, sizeof(ASDCP::JP2K::Magic)) == 0 )
233 type = ESS_JPEG_2000;
235 else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
236 type = ESS_PCM_24b_48k;
249 ASDCP::EncryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESEncContext* Ctx)
251 ASDCP_TEST_NULL(Ctx);
255 Result_t result = FBout.Capacity(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));
258 byte_t* p = FBout.Data();
260 // write the IV to the frame buffer
265 // encrypt the check value to the frame buffer
266 if ( ASDCP_SUCCESS(result) )
268 result = Ctx->EncryptBlock(ESV_CheckValue, p, CBC_BLOCK_SIZE);
272 // write optional plaintext region
273 if ( FBin.PlaintextOffset() > 0 )
275 assert(FBin.PlaintextOffset() <= FBin.Size());
276 memcpy(p, FBin.RoData(), FBin.PlaintextOffset());
277 p += FBin.PlaintextOffset();
280 ui32_t ct_size = FBin.Size() - FBin.PlaintextOffset();
281 ui32_t diff = ct_size % CBC_BLOCK_SIZE;
282 ui32_t block_size = ct_size - diff;
283 assert((block_size % CBC_BLOCK_SIZE) == 0);
285 // encrypt the ciphertext region essence data
286 if ( ASDCP_SUCCESS(result) )
288 result = Ctx->EncryptBlock(FBin.RoData() + FBin.PlaintextOffset(), p, block_size);
292 // construct and encrypt the padding
293 if ( ASDCP_SUCCESS(result) )
295 byte_t the_last_block[CBC_BLOCK_SIZE];
298 memcpy(the_last_block, FBin.RoData() + FBin.PlaintextOffset() + block_size, diff);
300 for (ui32_t i = 0; diff < CBC_BLOCK_SIZE; diff++, i++ )
301 the_last_block[diff] = i;
303 result = Ctx->EncryptBlock(the_last_block, p, CBC_BLOCK_SIZE);
306 if ( ASDCP_SUCCESS(result) )
307 FBout.Size(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));
314 ASDCP::DecryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESDecContext* Ctx)
316 ASDCP_TEST_NULL(Ctx);
317 assert(FBout.Capacity() >= FBin.SourceLength());
319 ui32_t ct_size = FBin.SourceLength() - FBin.PlaintextOffset();
320 ui32_t diff = ct_size % CBC_BLOCK_SIZE;
321 ui32_t block_size = ct_size - diff;
323 assert((block_size % CBC_BLOCK_SIZE) == 0);
325 const byte_t* buf = FBin.RoData();
329 buf += CBC_BLOCK_SIZE;
331 // decrypt and test check value
332 byte_t CheckValue[CBC_BLOCK_SIZE];
333 Result_t result = Ctx->DecryptBlock(buf, CheckValue, CBC_BLOCK_SIZE);
334 buf += CBC_BLOCK_SIZE;
336 if ( memcmp(CheckValue, ESV_CheckValue, CBC_BLOCK_SIZE) != 0 )
337 return RESULT_CHECKFAIL;
339 // copy plaintext region
340 if ( FBin.PlaintextOffset() > 0 )
342 memcpy(FBout.Data(), buf, FBin.PlaintextOffset());
343 buf += FBin.PlaintextOffset();
346 // decrypt all but last block
347 if ( ASDCP_SUCCESS(result) )
349 result = Ctx->DecryptBlock(buf, FBout.Data() + FBin.PlaintextOffset(), block_size);
353 // decrypt last block
354 if ( ASDCP_SUCCESS(result) )
356 byte_t the_last_block[CBC_BLOCK_SIZE];
357 result = Ctx->DecryptBlock(buf, the_last_block, CBC_BLOCK_SIZE);
359 if ( the_last_block[diff] != 0 )
361 DefaultLogSink().Error("Unexpected non-zero padding value.\n");
362 return RESULT_FORMAT;
366 memcpy(FBout.Data() + FBin.PlaintextOffset() + block_size, the_last_block, diff);
369 if ( ASDCP_SUCCESS(result) )
370 FBout.Size(FBin.SourceLength());
378 ASDCP::IntegrityPack::CalcValues(const ASDCP::FrameBuffer& FB, byte_t* AssetID,
379 ui32_t sequence, HMACContext* HMAC)
381 ASDCP_TEST_NULL(AssetID);
382 ASDCP_TEST_NULL(HMAC);
386 static byte_t ber_4[MXF_BER_LENGTH] = {0x83, 0};
388 // update HMAC with essence data
389 HMAC->Update(FB.RoData(), FB.Size());
391 // track file ID length
392 memcpy(p, ber_4, MXF_BER_LENGTH);
397 memcpy(p, AssetID, UUIDlen);
401 memcpy(p, ber_4, MXF_BER_LENGTH);
402 *(p+3) = sizeof(ui64_t);
406 Kumu::i2p<ui64_t>(KM_i64_BE(sequence), p);
410 memcpy(p, ber_4, MXF_BER_LENGTH);
414 // update HMAC with intpack values
415 HMAC->Update(Data, klv_intpack_size - HMAC_SIZE);
417 // finish & write HMAC
419 HMAC->GetHMACValue(p);
421 assert(p + HMAC_SIZE == Data + klv_intpack_size);
428 ASDCP::IntegrityPack::TestValues(const ASDCP::FrameBuffer& FB, byte_t* AssetID,
429 ui32_t sequence, HMACContext* HMAC)
431 ASDCP_TEST_NULL(AssetID);
432 ASDCP_TEST_NULL(HMAC);
434 // find the start of the intpack
435 byte_t* p = (byte_t*)FB.RoData() + ( FB.Size() - klv_intpack_size );
437 // test the AssetID length
438 if ( ! Kumu::read_test_BER(&p, UUIDlen) )
439 return RESULT_HMACFAIL;
442 if ( memcmp(p, AssetID, UUIDlen) != 0 )
444 DefaultLogSink().Error("IntegrityPack failure: AssetID mismatch.\n");
445 return RESULT_HMACFAIL;
449 // test the sequence length
450 if ( ! Kumu::read_test_BER(&p, sizeof(ui64_t)) )
451 return RESULT_HMACFAIL;
453 ui32_t test_sequence = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(p));
455 // test the sequence value
456 if ( test_sequence != sequence )
458 DefaultLogSink().Error("IntegrityPack failure: sequence is %u, expecting %u.\n", test_sequence, sequence);
459 return RESULT_HMACFAIL;
464 // test the HMAC length
465 if ( ! Kumu::read_test_BER(&p, HMAC_SIZE) )
466 return RESULT_HMACFAIL;
470 HMAC->Update(FB.RoData(), FB.Size() - HMAC_SIZE);
473 return HMAC->TestHMACValue(p);
477 // end AS_DCP_MXF.cpp