2 Copyright (c) 2004-2005, 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 "AS_DCP_internal.h"
34 #include "DirScanner.h"
40 //------------------------------------------------------------------------------------------
46 ASDCP::WriterInfoDump(const WriterInfo& Info, FILE* stream)
53 fprintf(stream," ProductUUID: %s\n", bin2hex(Info.ProductUUID, 16, 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", bin2hex(Info.ContextID, 16, str_buf, 40));
69 fprintf(stream, "CryptographicKeyID: %s\n", bin2hex(Info.CryptographicKeyID, 16, str_buf, 40));
72 fprintf(stream," AssetUUID: %s\n", bin2hex(Info.AssetUUID, 16, str_buf, 40));
77 ASDCP::MD_to_WriterInfo(Identification* InfoObj, WriterInfo& Info)
79 ASDCP_TEST_NULL(InfoObj);
80 char tmp_str[IdentBufferLen];
82 Info.ProductName = "Unknown Product";
83 Info.ProductVersion = "Unknown Version";
84 Info.CompanyName = "Unknown Company";
85 memset(Info.ProductUUID, 0, UUIDlen);
87 InfoObj->ProductName.ToString(tmp_str);
88 if ( *tmp_str ) Info.ProductName = tmp_str;
90 InfoObj->VersionString.ToString(tmp_str);
91 if ( *tmp_str ) Info.ProductVersion = tmp_str;
93 InfoObj->CompanyName.ToString(tmp_str);
94 if ( *tmp_str ) Info.CompanyName = tmp_str;
96 memcpy(Info.ProductUUID, InfoObj->ProductUID.Data(), UUIDlen);
104 ASDCP::MD_to_CryptoInfo(CryptographicContext* InfoObj, WriterInfo& Info)
106 ASDCP_TEST_NULL(InfoObj);
108 Info.EncryptedEssence = true;
109 memcpy(Info.ContextID, InfoObj->ContextID.Data(), UUIDlen);
110 memcpy(Info.CryptographicKeyID, InfoObj->CryptographicKeyID.Data(), UUIDlen);
112 UL MIC_SHA1(MICAlgorithm_HMAC_SHA1);
113 UL MIC_NONE(MICAlgorithm_NONE);
115 if ( InfoObj->MICAlgorithm == MIC_SHA1 )
116 Info.UsesHMAC = true;
118 else if ( InfoObj->MICAlgorithm == MIC_NONE )
119 Info.UsesHMAC = false;
123 DefaultLogSink().Error("Unexpected MICAlgorithm UL.\n");
124 return RESULT_FORMAT;
134 // add DMS CryptographicFramework entry to source package
136 ASDCP::AddDMScrypt(PackagePtr SourcePackage, WriterInfo& Descr, const byte_t* SourceEssenceContainerLabel)
138 assert(SourceEssenceContainerLabel);
140 TrackPtr MPDMTrack = SourcePackage->AddDMTrack(); // zero parameters = static
141 DMSegmentPtr MPDMSegment = MPDMTrack->AddDMSegment();
143 MDObject* Crypto_DMS_Ptr = new MDObject("CryptographicFramework");
144 MPDMSegment->AddChild("DMFramework")->MakeLink(*Crypto_DMS_Ptr);
146 MDObject* Crypto_DMS_BasicPtr = new MDObject("CryptographicContext");
147 Crypto_DMS_Ptr->AddChild("ContextSR")->MakeLink(*Crypto_DMS_BasicPtr);
149 UUID ContextID(Descr.ContextID);
150 Crypto_DMS_BasicPtr->SetValue("ContextID", DataChunk(UUIDlen, ContextID.GetValue())); // UUID
151 Crypto_DMS_BasicPtr->SetValue("SourceEssenceContainer",
152 DataChunk(klv_key_size, SourceEssenceContainerLabel)); // Label
153 Crypto_DMS_BasicPtr->SetValue("CipherAlgorithm", DataChunk(klv_key_size, CipherAlgorithm_AES)); // UL Key
155 Crypto_DMS_BasicPtr->SetValue("MICAlgorithm",
158 MICAlgorithm_HMAC_SHA1
159 : MICAlgorithm_NONE))); // UL Key
161 UUID CryptographicKeyID(Descr.CryptographicKeyID);
163 Crypto_DMS_BasicPtr->SetValue("CryptographicKeyID", DataChunk(UUIDlen, CryptographicKeyID.GetValue())); // UUID
170 ASDCP::FindObject(const char* filename, const char* objname, FILE* stream)
172 ASDCP_TEST_NULL_STR(filename);
173 ASDCP_TEST_NULL_STR(objname);
178 ASDCP::h__Reader Reader;
179 Result_t result = Reader.OpenMXFRead(filename);
181 if ( ASDCP_FAILURE(result) )
184 MDObject* DescObj = Reader.GetMDObjectByType(objname);
188 DumpMDObject(*DescObj, " ", stream);
199 ASDCP::EssenceType(const char* filename, EssenceType_t& type)
201 ASDCP_TEST_NULL_STR(filename);
203 OPAtomHeader TestHeader;
205 Result_t result = Reader.OpenRead(filename);
207 if ( ASDCP_SUCCESS(result) )
208 result = TestHeader.InitFromFile(Reader); // test UL and OP
210 if ( ASDCP_SUCCESS(result) )
213 if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor))) )
214 type = ESS_JPEG_2000;
217 if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor))) )
218 type = ESS_PCM_24b_48k;
221 if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor))) )
222 type = ESS_MPEG2_VES;
233 ASDCP::RawEssenceType(const char* filename, EssenceType_t& type)
235 ASDCP_TEST_NULL_STR(filename);
237 ASDCP::FrameBuffer FB;
240 Result_t result = FB.Capacity(Wav::MaxWavHeader); // using Wav max because everything else is much smaller
242 if ( ASDCP::PathIsFile(filename) )
244 result = Reader.OpenRead(filename);
246 if ( ASDCP_SUCCESS(result) )
248 result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
252 if ( ASDCP_SUCCESS(result) )
254 ASDCP::Wav::SimpleWaveHeader WavHeader;
256 const byte_t* p = FB.RoData();
258 if ( p[0] == 0 && p[1] == 0 && p[2] == 1 && (p[3] == 0xb3 || p[3] == 0) )
259 type = ESS_MPEG2_VES;
261 else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(p, read_count, &data_offset)) )
262 type = ESS_PCM_24b_48k;
265 else if ( ASDCP::PathIsDirectory(filename) )
267 char next_file[ASDCP_MAX_PATH];
269 Result_t result = Scanner.Open(filename);
271 if ( ASDCP_SUCCESS(result) )
273 while ( ASDCP_SUCCESS(Scanner.GetNext(next_file)) )
275 if ( next_file[0] == '.' ) // no hidden files or internal links
278 std::string Str(filename);
281 result = Reader.OpenRead(Str.c_str());
283 if ( ASDCP_SUCCESS(result) )
285 result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
289 if ( ASDCP_SUCCESS(result)
290 && ( memcmp(FB.RoData(), ASDCP::JP2K::Magic, sizeof(ASDCP::JP2K::Magic)) == 0 ) )
291 type = ESS_JPEG_2000;
303 ASDCP::EncryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESEncContext* Ctx)
305 ASDCP_TEST_NULL(Ctx);
309 Result_t result = FBout.Capacity(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));
312 byte_t* p = FBout.Data();
314 // write the IV to the frame buffer
319 // encrypt the check value to the frame buffer
320 if ( ASDCP_SUCCESS(result) )
322 result = Ctx->EncryptBlock(ESV_CheckValue, p, CBC_BLOCK_SIZE);
326 // write optional plaintext region
327 if ( FBin.PlaintextOffset() > 0 )
329 assert(FBin.PlaintextOffset() <= FBin.Size());
330 memcpy(p, FBin.RoData(), FBin.PlaintextOffset());
331 p += FBin.PlaintextOffset();
334 ui32_t ct_size = FBin.Size() - FBin.PlaintextOffset();
335 ui32_t diff = ct_size % CBC_BLOCK_SIZE;
336 ui32_t block_size = ct_size - diff;
337 assert((block_size % CBC_BLOCK_SIZE) == 0);
339 // encrypt the ciphertext region essence data
340 if ( ASDCP_SUCCESS(result) )
342 result = Ctx->EncryptBlock(FBin.RoData() + FBin.PlaintextOffset(), p, block_size);
346 // construct and encrypt the padding
347 if ( ASDCP_SUCCESS(result) )
349 byte_t the_last_block[CBC_BLOCK_SIZE];
352 memcpy(the_last_block, FBin.RoData() + FBin.PlaintextOffset() + block_size, diff);
354 for (ui32_t i = 0; diff < CBC_BLOCK_SIZE; diff++, i++ )
355 the_last_block[diff] = i;
357 result = Ctx->EncryptBlock(the_last_block, p, CBC_BLOCK_SIZE);
360 if ( ASDCP_SUCCESS(result) )
361 FBout.Size(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));
368 ASDCP::DecryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESDecContext* Ctx)
370 ASDCP_TEST_NULL(Ctx);
371 assert(FBout.Capacity() >= FBin.SourceLength());
373 ui32_t ct_size = FBin.SourceLength() - FBin.PlaintextOffset();
374 ui32_t diff = ct_size % CBC_BLOCK_SIZE;
375 ui32_t block_size = ct_size - diff;
377 assert((block_size % CBC_BLOCK_SIZE) == 0);
379 const byte_t* buf = FBin.RoData();
383 buf += CBC_BLOCK_SIZE;
385 // decrypt and test check value
386 byte_t CheckValue[CBC_BLOCK_SIZE];
387 Result_t result = Ctx->DecryptBlock(buf, CheckValue, CBC_BLOCK_SIZE);
388 buf += CBC_BLOCK_SIZE;
390 if ( memcmp(CheckValue, ESV_CheckValue, CBC_BLOCK_SIZE) != 0 )
391 return RESULT_CHECKFAIL;
393 // copy plaintext region
394 if ( FBin.PlaintextOffset() > 0 )
396 memcpy(FBout.Data(), buf, FBin.PlaintextOffset());
397 buf += FBin.PlaintextOffset();
400 // decrypt all but last block
401 if ( ASDCP_SUCCESS(result) )
403 result = Ctx->DecryptBlock(buf, FBout.Data() + FBin.PlaintextOffset(), block_size);
407 // decrypt last block
408 if ( ASDCP_SUCCESS(result) )
410 byte_t the_last_block[CBC_BLOCK_SIZE];
411 result = Ctx->DecryptBlock(buf, the_last_block, CBC_BLOCK_SIZE);
413 if ( the_last_block[diff] != 0 )
415 DefaultLogSink().Error("Unexpected non-zero padding value.\n");
416 return RESULT_FORMAT;
420 memcpy(FBout.Data() + FBin.PlaintextOffset() + block_size, the_last_block, diff);
423 if ( ASDCP_SUCCESS(result) )
424 FBout.Size(FBin.SourceLength());
432 ASDCP::IntegrityPack::CalcValues(const ASDCP::FrameBuffer& FB, byte_t* AssetID,
433 ui32_t sequence, HMACContext* HMAC)
435 ASDCP_TEST_NULL(AssetID);
436 ASDCP_TEST_NULL(HMAC);
440 static byte_t ber_4[klv_length_size] = {0x83, 0};
442 // update HMAC with essence data
443 HMAC->Update(FB.RoData(), FB.Size());
445 // track file ID length
446 memcpy(p, ber_4, klv_length_size);
448 p += klv_length_size;
451 memcpy(p, AssetID, UUIDlen);
455 memcpy(p, ber_4, klv_length_size);
456 *(p+3) = sizeof(ui64_t);
457 p += klv_length_size;
460 i2p<ui64_t>(ASDCP_i64_BE(sequence), p);
464 memcpy(p, ber_4, klv_length_size);
466 p += klv_length_size;
468 // update HMAC with intpack values
469 HMAC->Update(Data, klv_intpack_size - HMAC_SIZE);
471 // finish & write HMAC
473 HMAC->GetHMACValue(p);
475 assert(p + HMAC_SIZE == Data + klv_intpack_size);
482 ASDCP::IntegrityPack::TestValues(const ASDCP::FrameBuffer& FB, byte_t* AssetID,
483 ui32_t sequence, HMACContext* HMAC)
485 ASDCP_TEST_NULL(AssetID);
486 ASDCP_TEST_NULL(HMAC);
488 // find the start of the intpack
489 byte_t* p = (byte_t*)FB.RoData() + ( FB.Size() - klv_intpack_size );
491 // test the AssetID length
492 if ( ! read_test_BER(&p, UUIDlen) )
493 return RESULT_HMACFAIL;
496 if ( memcmp(p, AssetID, UUIDlen) != 0 )
498 DefaultLogSink().Error("IntegrityPack failure: AssetID mismatch.\n");
499 return RESULT_HMACFAIL;
503 // test the sequence length
504 if ( ! read_test_BER(&p, sizeof(ui64_t)) )
505 return RESULT_HMACFAIL;
507 ui32_t test_sequence = (ui32_t)ASDCP_i64_BE(cp2i<ui64_t>(p));
509 // test the sequence value
510 if ( test_sequence != sequence )
512 DefaultLogSink().Error("IntegrityPack failure: sequence is %lu, expecting %lu.\n", test_sequence, sequence);
513 return RESULT_HMACFAIL;
518 // test the HMAC length
519 if ( ! read_test_BER(&p, HMAC_SIZE) )
520 return RESULT_HMACFAIL;
524 HMAC->Update(FB.RoData(), FB.Size() - HMAC_SIZE);
527 return HMAC->TestHMACValue(p);
530 //------------------------------------------------------------------------------------------
536 ASDCP::KLVReader::ReadKLFromFile(ASDCP::FileReader& Reader)
539 m_HeaderLength = klv_key_size + klv_length_size;
540 Result_t result = Reader.Read(m_Key, m_HeaderLength, &read_count);
541 assert(read_count == m_HeaderLength);
543 if ( ASDCP_SUCCESS(result) )
545 m_BERLength = BER_length(m_Key + klv_key_size);
547 if ( m_BERLength != klv_length_size )
549 ASDCP::DefaultLogSink().Error("Found packet with BER length %lu; being less efficient...\n",
551 // TODO: recover the correct BER value
552 // and reposition the file pointer
556 if ( ! read_BER(m_Key + klv_key_size, &m_Length) )
564 // end AS_DCP_MXF.cpp