working j2c as-02
[asdcplib.git] / src / AS_DCP_MXF.cpp
1 /*
2 Copyright (c) 2004-2013, John Hurst
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
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.
15
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.
26 */
27 /*! \file    AS_DCP_MXF.cpp
28     \version $Id$
29     \brief   AS-DCP library, misc classes and subroutines
30 */
31
32 #include <KM_fileio.h>
33 #include <KM_xml.h>
34 #include "AS_DCP_internal.h"
35 #include "JP2K.h"
36 #include "MPEG.h"
37 #include "Wav.h"
38 #include <iostream>
39 #include <iomanip>
40
41
42 //------------------------------------------------------------------------------------------
43 // misc subroutines
44
45
46 //
47 std::ostream&
48 ASDCP::operator << (std::ostream& strm, const WriterInfo& Info)
49 {
50   char str_buf[40];
51
52   strm << "       ProductUUID: " << UUID(Info.ProductUUID).EncodeHex(str_buf, 40) << std::endl;
53   strm << "    ProductVersion: " << Info.ProductVersion << std::endl;
54   strm << "       CompanyName: " << Info.CompanyName << std::endl;
55   strm << "       ProductName: " << Info.ProductName << std::endl;
56   strm << "  EncryptedEssence: " << (Info.EncryptedEssence ? "Yes" : "No") << std::endl;
57
58   if ( Info.EncryptedEssence )
59     {
60       strm << "              HMAC: " << (Info.UsesHMAC ? "Yes" : "No") << std::endl;
61       strm << "         ContextID: " << UUID(Info.ContextID).EncodeHex(str_buf, 40) << std::endl;
62       strm << "CryptographicKeyID: " << UUID(Info.CryptographicKeyID).EncodeHex(str_buf, 40) << std::endl;
63     }
64
65   strm << "         AssetUUID: " << UUID(Info.AssetUUID).EncodeHex(str_buf, 40) << std::endl;
66   strm << "    Label Set Type: " << (Info.LabelSetType == LS_MXF_SMPTE ? "SMPTE" :
67                                      (Info.LabelSetType == LS_MXF_INTEROP ? "MXF Interop" :
68                                       "Unknown")) << std::endl;
69   return strm;
70 }
71
72 //
73 void
74 ASDCP::WriterInfoDump(const WriterInfo& Info, FILE* stream)
75 {
76   if ( stream == 0 )
77     stream = stderr;
78
79   char str_buf[40];
80
81   fprintf(stream,"       ProductUUID: %s\n", UUID(Info.ProductUUID).EncodeHex(str_buf, 40));
82   fprintf(stream,"\
83     ProductVersion: %s\n\
84        CompanyName: %s\n\
85        ProductName: %s\n\
86   EncryptedEssence: %s\n",
87           Info.ProductVersion.c_str(),
88           Info.CompanyName.c_str(),
89           Info.ProductName.c_str(),
90           ( Info.EncryptedEssence ? "Yes" : "No" )
91           );
92
93   if ( Info.EncryptedEssence )
94     {
95       fprintf(stream, "              HMAC: %s\n", ( Info.UsesHMAC ? "Yes" : "No"));
96       fprintf(stream, "         ContextID: %s\n", UUID(Info.ContextID).EncodeHex(str_buf, 40));
97       fprintf(stream, "CryptographicKeyID: %s\n", UUID(Info.CryptographicKeyID).EncodeHex(str_buf, 40));
98     }
99
100   fprintf(stream,"         AssetUUID: %s\n", UUID(Info.AssetUUID).EncodeHex(str_buf, 40));
101   fprintf(stream,"    Label Set Type: %s\n", ( Info.LabelSetType == LS_MXF_SMPTE ? "SMPTE" :
102                                                ( Info.LabelSetType == LS_MXF_INTEROP ? "MXF Interop" :
103                                                  "Unknown" ) ));
104 }
105
106 //
107 Result_t
108 ASDCP::MD_to_WriterInfo(Identification* InfoObj, WriterInfo& Info)
109 {
110   ASDCP_TEST_NULL(InfoObj);
111   char tmp_str[IdentBufferLen];
112
113   Info.ProductName = "Unknown Product";
114   Info.ProductVersion = "Unknown Version";
115   Info.CompanyName = "Unknown Company";
116   memset(Info.ProductUUID, 0, UUIDlen);
117
118   InfoObj->ProductName.EncodeString(tmp_str, IdentBufferLen);
119   if ( *tmp_str ) Info.ProductName = tmp_str;
120
121   InfoObj->VersionString.EncodeString(tmp_str, IdentBufferLen);
122   if ( *tmp_str ) Info.ProductVersion = tmp_str;
123
124   InfoObj->CompanyName.EncodeString(tmp_str, IdentBufferLen);
125   if ( *tmp_str ) Info.CompanyName = tmp_str;
126
127   memcpy(Info.ProductUUID, InfoObj->ProductUID.Value(), UUIDlen);
128
129   return RESULT_OK;
130 }
131
132
133 //
134 Result_t
135 ASDCP::MD_to_CryptoInfo(CryptographicContext* InfoObj, WriterInfo& Info, const Dictionary& Dict)
136 {
137   ASDCP_TEST_NULL(InfoObj);
138
139   Info.EncryptedEssence = true;
140   memcpy(Info.ContextID, InfoObj->ContextID.Value(), UUIDlen);
141   memcpy(Info.CryptographicKeyID, InfoObj->CryptographicKeyID.Value(), UUIDlen);
142
143   UL MIC_SHA1(Dict.ul(MDD_MICAlgorithm_HMAC_SHA1));
144   UL MIC_NONE(Dict.ul(MDD_MICAlgorithm_NONE));
145
146   if ( InfoObj->MICAlgorithm == MIC_SHA1 )
147     Info.UsesHMAC = true;
148
149   else if ( InfoObj->MICAlgorithm == MIC_NONE )
150     Info.UsesHMAC = false;
151
152   else
153     {
154       DefaultLogSink().Error("Unexpected MICAlgorithm UL.\n");
155       return RESULT_FORMAT;
156     }
157
158   return RESULT_OK;
159 }
160
161 //
162 //
163 ASDCP::Result_t
164 ASDCP::EssenceType(const char* filename, EssenceType_t& type)
165 {
166   const Dictionary* m_Dict = &DefaultCompositeDict();
167   InterchangeObject* md_object = 0;
168
169   assert(m_Dict);
170
171   ASDCP_TEST_NULL_STR(filename);
172   Kumu::FileReader   Reader;
173   OP1aHeader TestHeader(m_Dict);
174
175   Result_t result = Reader.OpenRead(filename);
176
177   if ( ASDCP_SUCCESS(result) )
178     result = TestHeader.InitFromFile(Reader); // test UL and OP
179
180   if ( ASDCP_SUCCESS(result) )
181     {
182       type = ESS_UNKNOWN;
183       if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor))) )
184         {
185           if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(StereoscopicPictureSubDescriptor))) )
186             {
187               type = ESS_JPEG_2000_S;
188             }
189           else
190             {
191               type = ESS_JPEG_2000;
192             }
193         }
194       else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &md_object)) )
195         {
196           assert(md_object);
197           if ( static_cast<ASDCP::MXF::WaveAudioDescriptor*>(md_object)->AudioSamplingRate == SampleRate_96k )
198             {
199               type = ESS_PCM_24b_96k;
200             }
201           else
202             {
203               type = ESS_PCM_24b_48k;
204             }
205         }
206       else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor))) )
207         {
208         type = ESS_MPEG2_VES;
209         }
210       else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(TimedTextDescriptor))) )
211         {
212         type = ESS_TIMED_TEXT;
213         }
214       else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(DCDataDescriptor))) )
215         {
216           if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(DolbyAtmosSubDescriptor))) )
217             {
218               type = ESS_DCDATA_DOLBY_ATMOS;
219             }
220           else
221             {
222               type = ESS_DCDATA_UNKNOWN;
223             }
224         }
225     }
226
227   return result;
228 }
229
230 //
231 ASDCP::Result_t
232 ASDCP::RawEssenceType(const char* filename, EssenceType_t& type)
233 {
234   ASDCP_TEST_NULL_STR(filename);
235   type = ESS_UNKNOWN;
236   ASDCP::FrameBuffer FB;
237   Kumu::FileReader Reader;
238   ASDCP::Wav::SimpleWaveHeader WavHeader;
239   ASDCP::RF64::SimpleRF64Header RF64Header;
240   ASDCP::AIFF::SimpleAIFFHeader AIFFHeader;
241   Kumu::XMLElement TmpElement("Tmp");
242
243   ui32_t data_offset;
244   ui32_t read_count;
245   Result_t result = FB.Capacity(Wav::MaxWavHeader); // using Wav max because everything else is much smaller
246
247   if ( Kumu::PathIsFile(filename) )
248     {
249       result = Reader.OpenRead(filename);
250
251       if ( ASDCP_SUCCESS(result) )
252         {
253           result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
254           Reader.Close();
255         }
256
257       if ( ASDCP_SUCCESS(result) )
258         {
259           const byte_t* p = FB.RoData();
260           FB.Size(read_count);
261
262           ui32_t i = 0;
263           while ( p[i] == 0 ) i++;
264
265           if ( i > 1 && p[i] == 1 &&  (p[i+1] == ASDCP::MPEG2::SEQ_START || p[i+1] == ASDCP::MPEG2::PIC_START) )
266             {
267               type = ESS_MPEG2_VES;
268             }
269           else if ( memcmp(FB.RoData(), ASDCP::JP2K::Magic, sizeof(ASDCP::JP2K::Magic)) == 0 )
270             {
271               type = ESS_JPEG_2000;
272             }
273           else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
274             {
275               switch ( WavHeader.samplespersec )
276                 {
277                 case 48000: type = ESS_PCM_24b_48k; break;
278                 case 96000: type = ESS_PCM_24b_96k; break;
279                 default:
280                   return RESULT_FORMAT;
281                 }
282             }
283           else if ( ASDCP_SUCCESS(RF64Header.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
284             {
285               switch ( RF64Header.samplespersec )
286                 {
287                 case 48000: type = ESS_PCM_24b_48k; break;
288                 case 96000: type = ESS_PCM_24b_96k; break;
289                 default:
290                   return RESULT_FORMAT;
291                 }
292             }
293           else if ( ASDCP_SUCCESS(AIFFHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
294             {
295               type = ESS_PCM_24b_48k;
296             }
297           else if ( Kumu::StringIsXML((const char*)FB.RoData(), FB.Size()) )
298             {
299               type = ESS_TIMED_TEXT;
300             }
301           else if ( ASDCP::ATMOS::IsDolbyAtmos(filename) )
302             {
303               type = ESS_DCDATA_DOLBY_ATMOS;
304             }
305         }
306     }
307   else if ( Kumu::PathIsDirectory(filename) )
308     {
309       char next_file[Kumu::MaxFilePath];
310       Kumu::DirScanner Scanner;
311       Result_t result = Scanner.Open(filename);
312
313       if ( ASDCP_SUCCESS(result) )
314         {
315           while ( ASDCP_SUCCESS(Scanner.GetNext(next_file)) )
316             {
317               if ( next_file[0] == '.' ) // no hidden files or internal links
318                 continue;
319
320               std::string Str(filename);
321               Str += "/";
322               Str += next_file;
323               result = Reader.OpenRead(Str.c_str());
324
325               if ( ASDCP_SUCCESS(result) )
326                 {
327                   result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
328                   Reader.Close();
329                 }
330
331               if ( ASDCP_SUCCESS(result) )
332                 {
333                   if ( memcmp(FB.RoData(), ASDCP::JP2K::Magic, sizeof(ASDCP::JP2K::Magic)) == 0 )
334                     {
335                       type = ESS_JPEG_2000;
336                     }
337                   else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
338                     {
339                       switch ( WavHeader.samplespersec )
340                         {
341                         case 48000: type = ESS_PCM_24b_48k; break;
342                         case 96000: type = ESS_PCM_24b_96k; break;
343                         default:
344                           return RESULT_FORMAT;
345                         }
346                     }
347                   else if ( ASDCP_SUCCESS(RF64Header.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
348                     {
349                       switch ( RF64Header.samplespersec )
350                         {
351                         case 48000: type = ESS_PCM_24b_48k; break;
352                         case 96000: type = ESS_PCM_24b_96k; break;
353                         default:
354                           return RESULT_FORMAT;
355                         }
356                     }
357                   else if ( ASDCP::ATMOS::IsDolbyAtmos(Str.c_str()) )
358                     {
359                       type = ESS_DCDATA_DOLBY_ATMOS;
360                     }
361                 }
362               
363               break;
364             }
365         }
366     }
367
368   return result;
369 }
370
371 //
372 Result_t
373 ASDCP::EncryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESEncContext* Ctx)
374 {
375   ASDCP_TEST_NULL(Ctx);
376   FBout.Size(0);
377
378   // size the buffer
379   Result_t result = FBout.Capacity(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));
380
381   // write the IV
382   byte_t* p = FBout.Data();
383
384   // write the IV to the frame buffer
385   Ctx->GetIVec(p);
386   p += CBC_BLOCK_SIZE;
387
388
389   // encrypt the check value to the frame buffer
390   if ( ASDCP_SUCCESS(result) )
391     {
392       result = Ctx->EncryptBlock(ESV_CheckValue, p, CBC_BLOCK_SIZE);
393       p += CBC_BLOCK_SIZE;
394     }
395
396   // write optional plaintext region
397   if ( FBin.PlaintextOffset() > 0 )
398     {
399       assert(FBin.PlaintextOffset() <= FBin.Size());
400       memcpy(p, FBin.RoData(), FBin.PlaintextOffset());
401       p += FBin.PlaintextOffset();
402     }
403
404   ui32_t ct_size = FBin.Size() - FBin.PlaintextOffset();
405   ui32_t diff = ct_size % CBC_BLOCK_SIZE;
406   ui32_t block_size = ct_size - diff;
407   assert((block_size % CBC_BLOCK_SIZE) == 0);
408
409   // encrypt the ciphertext region essence data
410   if ( ASDCP_SUCCESS(result) )
411     {
412       result = Ctx->EncryptBlock(FBin.RoData() + FBin.PlaintextOffset(), p, block_size);
413       p += block_size;
414     }
415
416   // construct and encrypt the padding
417   if ( ASDCP_SUCCESS(result) )
418     {
419       byte_t the_last_block[CBC_BLOCK_SIZE];
420
421       if ( diff > 0 )
422         memcpy(the_last_block, FBin.RoData() + FBin.PlaintextOffset() + block_size, diff);
423
424       for (ui32_t i = 0; diff < CBC_BLOCK_SIZE; diff++, i++ )
425         the_last_block[diff] = i;
426
427       result = Ctx->EncryptBlock(the_last_block, p, CBC_BLOCK_SIZE);
428     }
429
430   if ( ASDCP_SUCCESS(result) )
431     FBout.Size(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));
432
433   return result;
434 }
435
436 //
437 Result_t
438 ASDCP::DecryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESDecContext* Ctx)
439 {
440   ASDCP_TEST_NULL(Ctx);
441   assert(FBout.Capacity() >= FBin.SourceLength());
442
443   ui32_t ct_size = FBin.SourceLength() - FBin.PlaintextOffset();
444   ui32_t diff = ct_size % CBC_BLOCK_SIZE;
445   ui32_t block_size = ct_size - diff;
446   assert(block_size);
447   assert((block_size % CBC_BLOCK_SIZE) == 0);
448
449   const byte_t* buf = FBin.RoData();
450
451   // get ivec
452   Ctx->SetIVec(buf);
453   buf += CBC_BLOCK_SIZE;
454
455   // decrypt and test check value
456   byte_t CheckValue[CBC_BLOCK_SIZE];
457   Result_t result = Ctx->DecryptBlock(buf, CheckValue, CBC_BLOCK_SIZE);
458   buf += CBC_BLOCK_SIZE;
459
460   if ( memcmp(CheckValue, ESV_CheckValue, CBC_BLOCK_SIZE) != 0 )
461     return RESULT_CHECKFAIL;
462
463   // copy plaintext region
464   if ( FBin.PlaintextOffset() > 0 )
465     {
466       memcpy(FBout.Data(), buf, FBin.PlaintextOffset());
467       buf += FBin.PlaintextOffset();
468     }
469
470   // decrypt all but last block
471   if ( ASDCP_SUCCESS(result) )
472     {
473       result = Ctx->DecryptBlock(buf, FBout.Data() + FBin.PlaintextOffset(), block_size);
474       buf += block_size;
475     }
476
477   // decrypt last block
478   if ( ASDCP_SUCCESS(result) )
479     {
480       byte_t the_last_block[CBC_BLOCK_SIZE];
481       result = Ctx->DecryptBlock(buf, the_last_block, CBC_BLOCK_SIZE);
482
483       if ( the_last_block[diff] != 0 )
484         {
485           DefaultLogSink().Error("Unexpected non-zero padding value.\n");
486           return RESULT_FORMAT;
487         }
488
489       if ( diff > 0 )
490         memcpy(FBout.Data() + FBin.PlaintextOffset() + block_size, the_last_block, diff);
491     }
492
493   if ( ASDCP_SUCCESS(result) )
494     FBout.Size(FBin.SourceLength());
495
496   return result;
497 }
498
499
500 //
501 Result_t
502 ASDCP::IntegrityPack::CalcValues(const ASDCP::FrameBuffer& FB, const byte_t* AssetID,
503                                  ui32_t sequence, HMACContext* HMAC)
504 {
505   ASDCP_TEST_NULL(AssetID);
506   ASDCP_TEST_NULL(HMAC);
507   byte_t* p = Data;
508   HMAC->Reset();
509
510   static byte_t ber_4[MXF_BER_LENGTH] = {0x83, 0, 0, 0};
511
512   // update HMAC with essence data
513   HMAC->Update(FB.RoData(), FB.Size());
514
515   // track file ID length
516   memcpy(p, ber_4, MXF_BER_LENGTH);
517   *(p+3) = UUIDlen;;
518   p += MXF_BER_LENGTH;
519
520   // track file ID
521   memcpy(p, AssetID, UUIDlen);
522   p += UUIDlen;
523
524   // sequence length
525   memcpy(p, ber_4, MXF_BER_LENGTH);
526   *(p+3) = sizeof(ui64_t);
527   p += MXF_BER_LENGTH;
528
529   // sequence number
530   Kumu::i2p<ui64_t>(KM_i64_BE(sequence), p);
531   p += sizeof(ui64_t);
532
533   // HMAC length
534   memcpy(p, ber_4, MXF_BER_LENGTH);
535   *(p+3) = HMAC_SIZE;
536   p += MXF_BER_LENGTH;
537
538   // update HMAC with intpack values
539   HMAC->Update(Data, klv_intpack_size - HMAC_SIZE);
540
541   // finish & write HMAC
542   HMAC->Finalize();
543   HMAC->GetHMACValue(p);
544
545   assert(p + HMAC_SIZE == Data + klv_intpack_size);
546
547   return RESULT_OK;
548 }
549
550
551 Result_t
552 ASDCP::IntegrityPack::TestValues(const ASDCP::FrameBuffer& FB, const byte_t* AssetID,
553                                  ui32_t sequence, HMACContext* HMAC)
554 {
555   ASDCP_TEST_NULL(AssetID);
556   ASDCP_TEST_NULL(HMAC);
557
558   // find the start of the intpack
559   byte_t* p = (byte_t*)FB.RoData() + ( FB.Size() - klv_intpack_size );
560
561   // test the AssetID length
562   if ( ! Kumu::read_test_BER(&p, UUIDlen) )
563         return RESULT_HMACFAIL;
564
565   // test the AssetID
566   if ( memcmp(p, AssetID, UUIDlen) != 0 )
567     {
568       DefaultLogSink().Error("IntegrityPack failure: AssetID mismatch.\n");
569       return RESULT_HMACFAIL;
570     }
571   p += UUIDlen;
572   
573   // test the sequence length
574   if ( ! Kumu::read_test_BER(&p, sizeof(ui64_t)) )
575         return RESULT_HMACFAIL;
576
577   ui32_t test_sequence = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(p));
578
579   // test the sequence value
580   if ( test_sequence != sequence )
581     {
582       DefaultLogSink().Error("IntegrityPack failure: sequence is %u, expecting %u.\n", test_sequence, sequence);
583       return RESULT_HMACFAIL;
584     }
585
586   p += sizeof(ui64_t);
587
588   // test the HMAC length
589   if ( ! Kumu::read_test_BER(&p, HMAC_SIZE) )
590         return RESULT_HMACFAIL;
591
592   // test the HMAC
593   HMAC->Reset();
594   HMAC->Update(FB.RoData(), FB.Size() - HMAC_SIZE);
595   HMAC->Finalize();
596
597   return HMAC->TestHMACValue(p);
598 }
599
600 //
601 // end AS_DCP_MXF.cpp
602 //