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"
39 //------------------------------------------------------------------------------------------
45 ASDCP::WriterInfoDump(const WriterInfo& Info, FILE* stream)
52 fprintf(stream," ProductUUID: %s\n", bin2hex(Info.ProductUUID, 16, 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", bin2hex(Info.ContextID, 16, str_buf, 40));
68 fprintf(stream, "CryptographicKeyID: %s\n", bin2hex(Info.CryptographicKeyID, 16, str_buf, 40));
71 fprintf(stream," AssetUUID: %s\n", bin2hex(Info.AssetUUID, 16, str_buf, 40));
76 ASDCP::MD_to_WriterInfo(Identification* InfoObj, WriterInfo& Info)
78 ASDCP_TEST_NULL(InfoObj);
79 char tmp_str[IdentBufferLen];
81 Info.ProductName = "Unknown Product";
82 Info.ProductVersion = "Unknown Version";
83 Info.CompanyName = "Unknown Company";
84 memset(Info.ProductUUID, 0, UUIDlen);
86 InfoObj->ProductName.ToString(tmp_str);
87 if ( *tmp_str ) Info.ProductName = tmp_str;
89 InfoObj->VersionString.ToString(tmp_str);
90 if ( *tmp_str ) Info.ProductVersion = tmp_str;
92 InfoObj->CompanyName.ToString(tmp_str);
93 if ( *tmp_str ) Info.CompanyName = tmp_str;
95 memcpy(Info.ProductUUID, InfoObj->ProductUID.Value(), UUIDlen);
103 ASDCP::MD_to_CryptoInfo(CryptographicContext* InfoObj, WriterInfo& Info)
105 ASDCP_TEST_NULL(InfoObj);
107 Info.EncryptedEssence = true;
108 memcpy(Info.ContextID, InfoObj->ContextID.Value(), UUIDlen);
109 memcpy(Info.CryptographicKeyID, InfoObj->CryptographicKeyID.Value(), UUIDlen);
111 UL MIC_SHA1(MICAlgorithm_HMAC_SHA1);
112 UL MIC_NONE(MICAlgorithm_NONE);
114 if ( InfoObj->MICAlgorithm == MIC_SHA1 )
115 Info.UsesHMAC = true;
117 else if ( InfoObj->MICAlgorithm == MIC_NONE )
118 Info.UsesHMAC = false;
122 DefaultLogSink().Error("Unexpected MICAlgorithm UL.\n");
123 return RESULT_FORMAT;
132 ASDCP::EssenceType(const char* filename, EssenceType_t& type)
134 ASDCP_TEST_NULL_STR(filename);
136 OPAtomHeader TestHeader;
138 Result_t result = Reader.OpenRead(filename);
140 if ( ASDCP_SUCCESS(result) )
141 result = TestHeader.InitFromFile(Reader); // test UL and OP
143 if ( ASDCP_SUCCESS(result) )
146 if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor))) )
147 type = ESS_JPEG_2000;
150 if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor))) )
151 type = ESS_PCM_24b_48k;
154 if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor))) )
155 type = ESS_MPEG2_VES;
166 ASDCP::RawEssenceType(const char* filename, EssenceType_t& type)
168 ASDCP_TEST_NULL_STR(filename);
170 ASDCP::FrameBuffer FB;
173 Result_t result = FB.Capacity(Wav::MaxWavHeader); // using Wav max because everything else is much smaller
175 if ( ASDCP::PathIsFile(filename) )
177 result = Reader.OpenRead(filename);
179 if ( ASDCP_SUCCESS(result) )
181 result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
185 if ( ASDCP_SUCCESS(result) )
187 ASDCP::Wav::SimpleWaveHeader WavHeader;
189 const byte_t* p = FB.RoData();
191 if ( p[0] == 0 && p[1] == 0 && p[2] == 1 && (p[3] == 0xb3 || p[3] == 0) )
192 type = ESS_MPEG2_VES;
194 else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(p, read_count, &data_offset)) )
195 type = ESS_PCM_24b_48k;
198 else if ( ASDCP::PathIsDirectory(filename) )
200 char next_file[ASDCP_MAX_PATH];
202 Result_t result = Scanner.Open(filename);
204 if ( ASDCP_SUCCESS(result) )
206 while ( ASDCP_SUCCESS(Scanner.GetNext(next_file)) )
208 if ( next_file[0] == '.' ) // no hidden files or internal links
211 std::string Str(filename);
214 result = Reader.OpenRead(Str.c_str());
216 if ( ASDCP_SUCCESS(result) )
218 result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
222 if ( ASDCP_SUCCESS(result)
223 && ( memcmp(FB.RoData(), ASDCP::JP2K::Magic, sizeof(ASDCP::JP2K::Magic)) == 0 ) )
224 type = ESS_JPEG_2000;
236 ASDCP::EncryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESEncContext* Ctx)
238 ASDCP_TEST_NULL(Ctx);
242 Result_t result = FBout.Capacity(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));
245 byte_t* p = FBout.Data();
247 // write the IV to the frame buffer
252 // encrypt the check value to the frame buffer
253 if ( ASDCP_SUCCESS(result) )
255 result = Ctx->EncryptBlock(ESV_CheckValue, p, CBC_BLOCK_SIZE);
259 // write optional plaintext region
260 if ( FBin.PlaintextOffset() > 0 )
262 assert(FBin.PlaintextOffset() <= FBin.Size());
263 memcpy(p, FBin.RoData(), FBin.PlaintextOffset());
264 p += FBin.PlaintextOffset();
267 ui32_t ct_size = FBin.Size() - FBin.PlaintextOffset();
268 ui32_t diff = ct_size % CBC_BLOCK_SIZE;
269 ui32_t block_size = ct_size - diff;
270 assert((block_size % CBC_BLOCK_SIZE) == 0);
272 // encrypt the ciphertext region essence data
273 if ( ASDCP_SUCCESS(result) )
275 result = Ctx->EncryptBlock(FBin.RoData() + FBin.PlaintextOffset(), p, block_size);
279 // construct and encrypt the padding
280 if ( ASDCP_SUCCESS(result) )
282 byte_t the_last_block[CBC_BLOCK_SIZE];
285 memcpy(the_last_block, FBin.RoData() + FBin.PlaintextOffset() + block_size, diff);
287 for (ui32_t i = 0; diff < CBC_BLOCK_SIZE; diff++, i++ )
288 the_last_block[diff] = i;
290 result = Ctx->EncryptBlock(the_last_block, p, CBC_BLOCK_SIZE);
293 if ( ASDCP_SUCCESS(result) )
294 FBout.Size(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));
301 ASDCP::DecryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESDecContext* Ctx)
303 ASDCP_TEST_NULL(Ctx);
304 assert(FBout.Capacity() >= FBin.SourceLength());
306 ui32_t ct_size = FBin.SourceLength() - FBin.PlaintextOffset();
307 ui32_t diff = ct_size % CBC_BLOCK_SIZE;
308 ui32_t block_size = ct_size - diff;
310 assert((block_size % CBC_BLOCK_SIZE) == 0);
312 const byte_t* buf = FBin.RoData();
316 buf += CBC_BLOCK_SIZE;
318 // decrypt and test check value
319 byte_t CheckValue[CBC_BLOCK_SIZE];
320 Result_t result = Ctx->DecryptBlock(buf, CheckValue, CBC_BLOCK_SIZE);
321 buf += CBC_BLOCK_SIZE;
323 if ( memcmp(CheckValue, ESV_CheckValue, CBC_BLOCK_SIZE) != 0 )
324 return RESULT_CHECKFAIL;
326 // copy plaintext region
327 if ( FBin.PlaintextOffset() > 0 )
329 memcpy(FBout.Data(), buf, FBin.PlaintextOffset());
330 buf += FBin.PlaintextOffset();
333 // decrypt all but last block
334 if ( ASDCP_SUCCESS(result) )
336 result = Ctx->DecryptBlock(buf, FBout.Data() + FBin.PlaintextOffset(), block_size);
340 // decrypt last block
341 if ( ASDCP_SUCCESS(result) )
343 byte_t the_last_block[CBC_BLOCK_SIZE];
344 result = Ctx->DecryptBlock(buf, the_last_block, CBC_BLOCK_SIZE);
346 if ( the_last_block[diff] != 0 )
348 DefaultLogSink().Error("Unexpected non-zero padding value.\n");
349 return RESULT_FORMAT;
353 memcpy(FBout.Data() + FBin.PlaintextOffset() + block_size, the_last_block, diff);
356 if ( ASDCP_SUCCESS(result) )
357 FBout.Size(FBin.SourceLength());
365 ASDCP::IntegrityPack::CalcValues(const ASDCP::FrameBuffer& FB, byte_t* AssetID,
366 ui32_t sequence, HMACContext* HMAC)
368 ASDCP_TEST_NULL(AssetID);
369 ASDCP_TEST_NULL(HMAC);
373 static byte_t ber_4[klv_length_size] = {0x83, 0};
375 // update HMAC with essence data
376 HMAC->Update(FB.RoData(), FB.Size());
378 // track file ID length
379 memcpy(p, ber_4, klv_length_size);
381 p += klv_length_size;
384 memcpy(p, AssetID, UUIDlen);
388 memcpy(p, ber_4, klv_length_size);
389 *(p+3) = sizeof(ui64_t);
390 p += klv_length_size;
393 i2p<ui64_t>(ASDCP_i64_BE(sequence), p);
397 memcpy(p, ber_4, klv_length_size);
399 p += klv_length_size;
401 // update HMAC with intpack values
402 HMAC->Update(Data, klv_intpack_size - HMAC_SIZE);
404 // finish & write HMAC
406 HMAC->GetHMACValue(p);
408 assert(p + HMAC_SIZE == Data + klv_intpack_size);
415 ASDCP::IntegrityPack::TestValues(const ASDCP::FrameBuffer& FB, byte_t* AssetID,
416 ui32_t sequence, HMACContext* HMAC)
418 ASDCP_TEST_NULL(AssetID);
419 ASDCP_TEST_NULL(HMAC);
421 // find the start of the intpack
422 byte_t* p = (byte_t*)FB.RoData() + ( FB.Size() - klv_intpack_size );
424 // test the AssetID length
425 if ( ! read_test_BER(&p, UUIDlen) )
426 return RESULT_HMACFAIL;
429 if ( memcmp(p, AssetID, UUIDlen) != 0 )
431 DefaultLogSink().Error("IntegrityPack failure: AssetID mismatch.\n");
432 return RESULT_HMACFAIL;
436 // test the sequence length
437 if ( ! read_test_BER(&p, sizeof(ui64_t)) )
438 return RESULT_HMACFAIL;
440 ui32_t test_sequence = (ui32_t)ASDCP_i64_BE(cp2i<ui64_t>(p));
442 // test the sequence value
443 if ( test_sequence != sequence )
445 DefaultLogSink().Error("IntegrityPack failure: sequence is %lu, expecting %lu.\n", test_sequence, sequence);
446 return RESULT_HMACFAIL;
451 // test the HMAC length
452 if ( ! read_test_BER(&p, HMAC_SIZE) )
453 return RESULT_HMACFAIL;
457 HMAC->Update(FB.RoData(), FB.Size() - HMAC_SIZE);
460 return HMAC->TestHMACValue(p);
463 //------------------------------------------------------------------------------------------
469 ASDCP::KLVReader::ReadKLFromFile(ASDCP::FileReader& Reader)
472 m_HeaderLength = klv_key_size + klv_length_size;
473 Result_t result = Reader.Read(m_Key, m_HeaderLength, &read_count);
474 assert(read_count == m_HeaderLength);
476 if ( ASDCP_SUCCESS(result) )
478 m_BERLength = BER_length(m_Key + klv_key_size);
480 if ( m_BERLength == 0 )
482 char intbuf[IntBufferLen];
483 ASDCP::DefaultLogSink().Error("KLV format error, zero BER length not allowed at file position %s\n",
484 i64szx((Reader.Tell() - (fpos_t)klv_key_size), 8, intbuf));
488 if ( m_BERLength != klv_length_size )
491 ASDCP::DefaultLogSink().Error("Found packet with BER length %lu; being less efficient...\n",
493 // TODO: recover the correct BER value
494 // and reposition the file pointer
495 ASDCP::DefaultLogSink().Error("please finish me\n");
499 if ( ! read_BER(m_Key + klv_key_size, &m_Length) )
507 // end AS_DCP_MXF.cpp