Bump patch version post tag.
[asdcplib.git] / src / AS_DCP_MXF.cpp
1 /*
2 Copyright (c) 2004-2016, 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 "ACES.h"
37 #include "MPEG.h"
38 #include "Wav.h"
39 #include "KM_util.h"
40 #include <iostream>
41 #include <iomanip>
42
43
44 //------------------------------------------------------------------------------------------
45 // misc subroutines
46
47
48 //
49 std::ostream&
50 ASDCP::operator << (std::ostream& strm, const WriterInfo& Info)
51 {
52   char str_buf[40];
53
54   strm << "       ProductUUID: " << UUID(Info.ProductUUID).EncodeHex(str_buf, 40) << std::endl;
55   strm << "    ProductVersion: " << Info.ProductVersion << std::endl;
56   strm << "       CompanyName: " << Info.CompanyName << std::endl;
57   strm << "       ProductName: " << Info.ProductName << std::endl;
58   strm << "  EncryptedEssence: " << (Info.EncryptedEssence ? "Yes" : "No") << std::endl;
59
60   if ( Info.EncryptedEssence )
61     {
62       strm << "              HMAC: " << (Info.UsesHMAC ? "Yes" : "No") << std::endl;
63       strm << "         ContextID: " << UUID(Info.ContextID).EncodeHex(str_buf, 40) << std::endl;
64       strm << "CryptographicKeyID: " << UUID(Info.CryptographicKeyID).EncodeHex(str_buf, 40) << std::endl;
65     }
66
67   strm << "         AssetUUID: " << UUID(Info.AssetUUID).EncodeHex(str_buf, 40) << std::endl;
68   strm << "    Label Set Type: " << (Info.LabelSetType == LS_MXF_SMPTE ? "SMPTE" :
69                                      (Info.LabelSetType == LS_MXF_INTEROP ? "MXF Interop" :
70                                       "Unknown")) << std::endl;
71   return strm;
72 }
73
74 //
75 void
76 ASDCP::WriterInfoDump(const WriterInfo& Info, FILE* stream)
77 {
78   if ( stream == 0 )
79     stream = stderr;
80
81   char str_buf[40];
82
83   fprintf(stream,"       ProductUUID: %s\n", UUID(Info.ProductUUID).EncodeHex(str_buf, 40));
84   fprintf(stream,"\
85     ProductVersion: %s\n\
86        CompanyName: %s\n\
87        ProductName: %s\n\
88   EncryptedEssence: %s\n",
89           Info.ProductVersion.c_str(),
90           Info.CompanyName.c_str(),
91           Info.ProductName.c_str(),
92           ( Info.EncryptedEssence ? "Yes" : "No" )
93           );
94
95   if ( Info.EncryptedEssence )
96     {
97       fprintf(stream, "              HMAC: %s\n", ( Info.UsesHMAC ? "Yes" : "No"));
98       fprintf(stream, "         ContextID: %s\n", UUID(Info.ContextID).EncodeHex(str_buf, 40));
99       fprintf(stream, "CryptographicKeyID: %s\n", UUID(Info.CryptographicKeyID).EncodeHex(str_buf, 40));
100     }
101
102   fprintf(stream,"         AssetUUID: %s\n", UUID(Info.AssetUUID).EncodeHex(str_buf, 40));
103   fprintf(stream,"    Label Set Type: %s\n", ( Info.LabelSetType == LS_MXF_SMPTE ? "SMPTE" :
104                                                ( Info.LabelSetType == LS_MXF_INTEROP ? "MXF Interop" :
105                                                  "Unknown" ) ));
106 }
107
108 //
109 Result_t
110 ASDCP::MD_to_WriterInfo(Identification* InfoObj, WriterInfo& Info)
111 {
112   ASDCP_TEST_NULL(InfoObj);
113   char tmp_str[IdentBufferLen];
114
115   Info.ProductName = "Unknown Product";
116   Info.ProductVersion = "Unknown Version";
117   Info.CompanyName = "Unknown Company";
118   memset(Info.ProductUUID, 0, UUIDlen);
119
120   InfoObj->ProductName.EncodeString(tmp_str, IdentBufferLen);
121   if ( *tmp_str ) Info.ProductName = tmp_str;
122
123   InfoObj->VersionString.EncodeString(tmp_str, IdentBufferLen);
124   if ( *tmp_str ) Info.ProductVersion = tmp_str;
125
126   InfoObj->CompanyName.EncodeString(tmp_str, IdentBufferLen);
127   if ( *tmp_str ) Info.CompanyName = tmp_str;
128
129   memcpy(Info.ProductUUID, InfoObj->ProductUID.Value(), UUIDlen);
130
131   return RESULT_OK;
132 }
133
134
135 //
136 Result_t
137 ASDCP::MD_to_CryptoInfo(CryptographicContext* InfoObj, WriterInfo& Info, const Dictionary& Dict)
138 {
139   ASDCP_TEST_NULL(InfoObj);
140
141   Info.EncryptedEssence = true;
142   memcpy(Info.ContextID, InfoObj->ContextID.Value(), UUIDlen);
143   memcpy(Info.CryptographicKeyID, InfoObj->CryptographicKeyID.Value(), UUIDlen);
144
145   UL MIC_SHA1(Dict.ul(MDD_MICAlgorithm_HMAC_SHA1));
146   UL MIC_NONE(Dict.ul(MDD_MICAlgorithm_NONE));
147
148   if ( InfoObj->MICAlgorithm == MIC_SHA1 )
149     Info.UsesHMAC = true;
150
151   else if ( InfoObj->MICAlgorithm == MIC_NONE )
152     Info.UsesHMAC = false;
153
154   else
155     {
156       DefaultLogSink().Error("Unexpected MICAlgorithm UL.\n");
157       return RESULT_FORMAT;
158     }
159
160   return RESULT_OK;
161 }
162
163 //
164 //
165 ASDCP::Result_t
166 ASDCP::EssenceType(const std::string& filename, EssenceType_t& type)
167 {
168   const Dictionary* m_Dict = &DefaultCompositeDict();
169   InterchangeObject* md_object = 0;
170
171   assert(m_Dict);
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
184       if ( TestHeader.OperationalPattern == UL(m_Dict->ul(MDD_OPAtom))
185            || TestHeader.OperationalPattern == UL(m_Dict->ul(MDD_MXFInterop_OPAtom)) )
186         {
187           if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor))) )
188             {
189               if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(StereoscopicPictureSubDescriptor))) )
190                 {
191                   type = ESS_JPEG_2000_S;
192                 }
193               else
194                 {
195                   type = ESS_JPEG_2000;
196                 }
197             }
198           else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &md_object)) )
199             {
200               assert(md_object);
201               if ( static_cast<ASDCP::MXF::WaveAudioDescriptor*>(md_object)->AudioSamplingRate == SampleRate_96k )
202                 {
203                   type = ESS_PCM_24b_96k;
204                 }
205               else
206                 {
207                   type = ESS_PCM_24b_48k;
208                 }
209             }
210           else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor))) )
211             {
212               type = ESS_MPEG2_VES;
213             }
214           else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(TimedTextDescriptor))) )
215             {
216               type = ESS_TIMED_TEXT;
217             }
218           else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(DCDataDescriptor)))
219                     || ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(PrivateDCDataDescriptor))) )
220             {
221               if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(DolbyAtmosSubDescriptor))) )
222                 {
223                   type = ESS_DCDATA_DOLBY_ATMOS;
224                 }
225               else
226                 {
227                   type = ESS_DCDATA_UNKNOWN;
228                 }
229             }
230         }
231       else if (  TestHeader.OperationalPattern == UL(m_Dict->ul(MDD_OP1a)) )
232         {
233           // ST 2065-5 Picture Descriptor does not have a mandatory SubDescriptor, check EssenceContainer instead
234           if (ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor))) )
235           {
236             MXF::RGBAEssenceDescriptor *rgba_descriptor = 0;
237             char buf[64];
238
239             if ASDCP_SUCCESS(TestHeader.GetMDObjectByType(m_Dict->ul(MDD_RGBAEssenceDescriptor), reinterpret_cast<MXF::InterchangeObject**>(&rgba_descriptor)))
240             {
241                 if (rgba_descriptor->EssenceContainer == m_Dict->ul(MDD_MXFGCFrameWrappedACESPictures))
242                     type = ESS_AS02_ACES;
243             }
244           }
245           if (type == ESS_UNKNOWN)
246           {
247
248             if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor))) )
249               {
250                 type = ESS_AS02_JPEG_2000;
251               }
252             else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &md_object)) )
253               {
254                 assert(md_object);
255                 if ( static_cast<ASDCP::MXF::WaveAudioDescriptor*>(md_object)->AudioSamplingRate == SampleRate_96k )
256                   {
257                     type = ESS_AS02_PCM_24b_96k;
258                   }
259                 else
260                   {
261                     type = ESS_AS02_PCM_24b_48k;
262                   }
263               }
264             else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(TimedTextDescriptor))) )
265               {
266                 type = ESS_AS02_TIMED_TEXT;
267               }
268             else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(PIMFDynamicMetadataDescriptor))) )
269               {
270                 type = ESS_DCDATA_UNKNOWN;
271               }
272             else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(ISXDDataEssenceDescriptor))) )
273               {
274                 type = ESS_AS02_ISXD;
275               }
276             else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(ACESPictureSubDescriptor))) )
277               {
278                 type = ESS_AS02_ACES;
279               }
280           }
281         }
282       else
283         {
284           DefaultLogSink().Error("Unsupported MXF Operational Pattern.\n");
285           return RESULT_FORMAT;
286         }
287     }
288
289   return result;
290 }
291
292 //
293 static bool
294 string_is_xml(const ASDCP::FrameBuffer& buffer)
295 {
296   return (strncmp((const char *)buffer.RoData(),             "<?xml", 5) == 0 ||
297           strncmp((const char *)buffer.RoData(), "\xEF\xBB\xBF<?xml", 8) == 0); // Allow BOM
298  }
299  
300  //
301
302 //
303 ASDCP::Result_t
304 ASDCP::RawEssenceType(const std::string& filename, EssenceType_t& type)
305 {
306   type = ESS_UNKNOWN;
307   ASDCP::FrameBuffer FB;
308   Kumu::FileReader Reader;
309   ASDCP::Wav::SimpleWaveHeader WavHeader;
310   ASDCP::RF64::SimpleRF64Header RF64Header;
311   ASDCP::AIFF::SimpleAIFFHeader AIFFHeader;
312   Kumu::XMLElement TmpElement("Tmp");
313
314   ui32_t data_offset;
315   ui32_t read_count;
316   Result_t result = FB.Capacity(Wav::MaxWavHeader); // using Wav max because everything else is much smaller
317
318   if ( Kumu::PathIsFile(filename) )
319     {
320       result = Reader.OpenRead(filename);
321
322       if ( ASDCP_SUCCESS(result) )
323         {
324           result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
325           Reader.Close();
326         }
327
328       if ( ASDCP_SUCCESS(result) )
329         {
330           const byte_t* p = FB.RoData();
331           FB.Size(read_count);
332
333           ui32_t i = 0;
334           while ( p[i] == 0 ) i++;
335
336           if ( i > 1 && p[i] == 1 &&  (p[i+1] == ASDCP::MPEG2::SEQ_START || p[i+1] == ASDCP::MPEG2::PIC_START) )
337             {
338               type = ESS_MPEG2_VES;
339             }
340           else if ( memcmp(FB.RoData(), ASDCP::JP2K::Magic, sizeof(ASDCP::JP2K::Magic)) == 0 )
341             {
342               type = ESS_JPEG_2000;
343             }
344           else if(memcmp(FB.RoData(), AS_02::ACES::Magic, sizeof(AS_02::ACES::Magic)) == 0)
345             {
346               type = ESS_AS02_ACES;
347             }
348           else if ( std::string((const char*)FB.RoData() + 8, 4) == "WAVE" )
349             {
350               if ( std::string((const char*)FB.RoData(), 4) == "RIFF" )
351                 {
352                   result = WavHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset);
353
354                   if ( ASDCP_SUCCESS(result) )
355                     {
356                       switch ( WavHeader.samplespersec )
357                         {
358                         case 48000: type = ESS_PCM_24b_48k; break;
359                         case 96000: type = ESS_PCM_24b_96k; break;
360                         default:
361                           DefaultLogSink().Error("Unexpected sample rate: %d\n", WavHeader.samplespersec);
362                           result = RESULT_FORMAT;
363                         }
364                     }
365                 }
366               else
367                 {
368                   result = RF64Header.ReadFromBuffer(FB.RoData(), read_count, &data_offset);
369                 
370                   if ( ASDCP_SUCCESS(result) )
371                     {
372                       switch ( RF64Header.samplespersec )
373                         {
374                         case 48000: type = ESS_PCM_24b_48k; break;
375                         case 96000: type = ESS_PCM_24b_96k; break;
376                         default:
377                           DefaultLogSink().Error("Unexpected sample rate: %d\n", WavHeader.samplespersec);
378                           result = RESULT_FORMAT;
379                         }
380                     }
381                 }
382             }
383           else if ( ASDCP_SUCCESS(AIFFHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
384             {
385               type = ESS_PCM_24b_48k;
386             }
387           else if ( string_is_xml(FB) )
388             {
389               type = ESS_TIMED_TEXT;
390             }
391           else if ( ASDCP::ATMOS::IsDolbyAtmos(filename) )
392             {
393               type = ESS_DCDATA_DOLBY_ATMOS;
394             }
395         }
396     }
397   else if ( Kumu::PathIsDirectory(filename) )
398     {
399       char next_file[Kumu::MaxFilePath];
400       Kumu::DirScanner Scanner;
401       Result_t result = Scanner.Open(filename);
402
403       if ( ASDCP_SUCCESS(result) )
404         {
405           while ( ASDCP_SUCCESS(Scanner.GetNext(next_file)) )
406             {
407               if ( next_file[0] == '.' ) // no hidden files or internal links
408                 continue;
409
410               result = Reader.OpenRead(Kumu::PathJoin(filename, next_file));
411
412               if ( ASDCP_SUCCESS(result) )
413                 {
414                   result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
415                   Reader.Close();
416                 }
417
418               if ( ASDCP_SUCCESS(result) )
419                 {
420                   if ( memcmp(FB.RoData(), ASDCP::JP2K::Magic, sizeof(ASDCP::JP2K::Magic)) == 0 )
421                     {
422                       type = ESS_JPEG_2000;
423                     }
424                   else if(memcmp(FB.RoData(), AS_02::ACES::Magic, sizeof(AS_02::ACES::Magic)) == 0)
425                     {
426                         type = ESS_AS02_ACES;
427                     }
428                   else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
429                     {
430                       switch ( WavHeader.samplespersec )
431                         {
432                         case 48000: type = ESS_PCM_24b_48k; break;
433                         case 96000: type = ESS_PCM_24b_96k; break;
434                         default:
435                           return RESULT_FORMAT;
436                         }
437                     }
438                   else if ( ASDCP_SUCCESS(RF64Header.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
439                     {
440                       switch ( RF64Header.samplespersec )
441                         {
442                         case 48000: type = ESS_PCM_24b_48k; break;
443                         case 96000: type = ESS_PCM_24b_96k; break;
444                         default:
445                           return RESULT_FORMAT;
446                         }
447                     }
448                   else if ( ASDCP::ATMOS::IsDolbyAtmos(Kumu::PathJoin(filename, next_file)) )
449                     {
450                       type = ESS_DCDATA_DOLBY_ATMOS;
451                     }
452                   else
453                     {
454                       type = ESS_DCDATA_UNKNOWN;
455                     }
456                 }
457               
458               break;
459             }
460         }
461     }
462
463   return result;
464 }
465
466 //
467 Result_t
468 ASDCP::EncryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESEncContext* Ctx)
469 {
470   ASDCP_TEST_NULL(Ctx);
471   FBout.Size(0);
472
473   // size the buffer
474   Result_t result = FBout.Capacity(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));
475
476   // write the IV
477   byte_t* p = FBout.Data();
478
479   // write the IV to the frame buffer
480   Ctx->GetIVec(p);
481   p += CBC_BLOCK_SIZE;
482
483
484   // encrypt the check value to the frame buffer
485   if ( ASDCP_SUCCESS(result) )
486     {
487       result = Ctx->EncryptBlock(ESV_CheckValue, p, CBC_BLOCK_SIZE);
488       p += CBC_BLOCK_SIZE;
489     }
490
491   // write optional plaintext region
492   if ( FBin.PlaintextOffset() > 0 )
493     {
494       assert(FBin.PlaintextOffset() <= FBin.Size());
495       memcpy(p, FBin.RoData(), FBin.PlaintextOffset());
496       p += FBin.PlaintextOffset();
497     }
498
499   ui32_t ct_size = FBin.Size() - FBin.PlaintextOffset();
500   ui32_t diff = ct_size % CBC_BLOCK_SIZE;
501   ui32_t block_size = ct_size - diff;
502   assert((block_size % CBC_BLOCK_SIZE) == 0);
503
504   // encrypt the ciphertext region essence data
505   if ( ASDCP_SUCCESS(result) )
506     {
507       result = Ctx->EncryptBlock(FBin.RoData() + FBin.PlaintextOffset(), p, block_size);
508       p += block_size;
509     }
510
511   // construct and encrypt the padding
512   if ( ASDCP_SUCCESS(result) )
513     {
514       byte_t the_last_block[CBC_BLOCK_SIZE];
515
516       if ( diff > 0 )
517         memcpy(the_last_block, FBin.RoData() + FBin.PlaintextOffset() + block_size, diff);
518
519       for (ui32_t i = 0; diff < CBC_BLOCK_SIZE; diff++, i++ )
520         the_last_block[diff] = i;
521
522       result = Ctx->EncryptBlock(the_last_block, p, CBC_BLOCK_SIZE);
523     }
524
525   if ( ASDCP_SUCCESS(result) )
526     FBout.Size(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));
527
528   return result;
529 }
530
531 //
532 Result_t
533 ASDCP::DecryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESDecContext* Ctx)
534 {
535   ASDCP_TEST_NULL(Ctx);
536   assert(FBout.Capacity() >= FBin.SourceLength());
537
538   ui32_t ct_size = FBin.SourceLength() - FBin.PlaintextOffset();
539   ui32_t diff = ct_size % CBC_BLOCK_SIZE;
540   ui32_t block_size = ct_size - diff;
541   assert(block_size);
542   assert((block_size % CBC_BLOCK_SIZE) == 0);
543
544   const byte_t* buf = FBin.RoData();
545
546   // get ivec
547   Ctx->SetIVec(buf);
548   buf += CBC_BLOCK_SIZE;
549
550   // decrypt and test check value
551   byte_t CheckValue[CBC_BLOCK_SIZE];
552   Result_t result = Ctx->DecryptBlock(buf, CheckValue, CBC_BLOCK_SIZE);
553   buf += CBC_BLOCK_SIZE;
554
555   if ( memcmp(CheckValue, ESV_CheckValue, CBC_BLOCK_SIZE) != 0 )
556     return RESULT_CHECKFAIL;
557
558   // copy plaintext region
559   if ( FBin.PlaintextOffset() > 0 )
560     {
561       memcpy(FBout.Data(), buf, FBin.PlaintextOffset());
562       buf += FBin.PlaintextOffset();
563     }
564
565   // decrypt all but last block
566   if ( ASDCP_SUCCESS(result) )
567     {
568       result = Ctx->DecryptBlock(buf, FBout.Data() + FBin.PlaintextOffset(), block_size);
569       buf += block_size;
570     }
571
572   // decrypt last block
573   if ( ASDCP_SUCCESS(result) )
574     {
575       byte_t the_last_block[CBC_BLOCK_SIZE];
576       result = Ctx->DecryptBlock(buf, the_last_block, CBC_BLOCK_SIZE);
577
578       if ( the_last_block[diff] != 0 )
579         {
580           DefaultLogSink().Error("Unexpected non-zero padding value.\n");
581           return RESULT_FORMAT;
582         }
583
584       if ( diff > 0 )
585         memcpy(FBout.Data() + FBin.PlaintextOffset() + block_size, the_last_block, diff);
586     }
587
588   if ( ASDCP_SUCCESS(result) )
589     FBout.Size(FBin.SourceLength());
590
591   return result;
592 }
593
594
595 //
596 Result_t
597 ASDCP::IntegrityPack::CalcValues(const ASDCP::FrameBuffer& FB, const byte_t* AssetID,
598                                  ui32_t sequence, HMACContext* HMAC)
599 {
600   ASDCP_TEST_NULL(AssetID);
601   ASDCP_TEST_NULL(HMAC);
602   byte_t* p = Data;
603   HMAC->Reset();
604
605   static byte_t ber_4[MXF_BER_LENGTH] = {0x83, 0, 0, 0};
606
607   // update HMAC with essence data
608   HMAC->Update(FB.RoData(), FB.Size());
609
610   // track file ID length
611   memcpy(p, ber_4, MXF_BER_LENGTH);
612   *(p+3) = UUIDlen;;
613   p += MXF_BER_LENGTH;
614
615   // track file ID
616   memcpy(p, AssetID, UUIDlen);
617   p += UUIDlen;
618
619   // sequence length
620   memcpy(p, ber_4, MXF_BER_LENGTH);
621   *(p+3) = sizeof(ui64_t);
622   p += MXF_BER_LENGTH;
623
624   // sequence number
625   Kumu::i2p<ui64_t>(KM_i64_BE(sequence), p);
626   p += sizeof(ui64_t);
627
628   // HMAC length
629   memcpy(p, ber_4, MXF_BER_LENGTH);
630   *(p+3) = HMAC_SIZE;
631   p += MXF_BER_LENGTH;
632
633   // update HMAC with intpack values
634   HMAC->Update(Data, klv_intpack_size - HMAC_SIZE);
635
636   // finish & write HMAC
637   HMAC->Finalize();
638   HMAC->GetHMACValue(p);
639
640   assert(p + HMAC_SIZE == Data + klv_intpack_size);
641
642   return RESULT_OK;
643 }
644
645
646 Result_t
647 ASDCP::IntegrityPack::TestValues(const ASDCP::FrameBuffer& FB, const byte_t* AssetID,
648                                  ui32_t sequence, HMACContext* HMAC)
649 {
650   ASDCP_TEST_NULL(AssetID);
651   ASDCP_TEST_NULL(HMAC);
652
653   // find the start of the intpack
654   byte_t* p = (byte_t*)FB.RoData() + ( FB.Size() - klv_intpack_size );
655
656   // test the AssetID length
657   if ( ! Kumu::read_test_BER(&p, UUIDlen) )
658         return RESULT_HMACFAIL;
659
660   // test the AssetID
661   if ( memcmp(p, AssetID, UUIDlen) != 0 )
662     {
663       DefaultLogSink().Error("IntegrityPack failure: AssetID mismatch.\n");
664       return RESULT_HMACFAIL;
665     }
666   p += UUIDlen;
667   
668   // test the sequence length
669   if ( ! Kumu::read_test_BER(&p, sizeof(ui64_t)) )
670         return RESULT_HMACFAIL;
671
672   ui32_t test_sequence = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(p));
673
674   // test the sequence value
675   if ( test_sequence != sequence )
676     {
677       DefaultLogSink().Error("IntegrityPack failure: sequence is %u, expecting %u.\n", test_sequence, sequence);
678       return RESULT_HMACFAIL;
679     }
680
681   p += sizeof(ui64_t);
682
683   // test the HMAC length
684   if ( ! Kumu::read_test_BER(&p, HMAC_SIZE) )
685         return RESULT_HMACFAIL;
686
687   // test the HMAC
688   HMAC->Reset();
689   HMAC->Update(FB.RoData(), FB.Size() - HMAC_SIZE);
690   HMAC->Finalize();
691
692   Result_t result = RESULT_OK;
693   result = HMAC->TestHMACValue(p);
694
695   if (KM_FAILURE(result))
696     {
697       Result_t r = RESULT_OK;
698       char hmac_str[HMAC_SIZE*10];
699       char found_str[HMAC_SIZE*10];
700       byte_t hmac_buf[HMAC_SIZE];
701
702
703       Kumu::bin2hex(p, HMAC_SIZE, found_str, HMAC_SIZE*10);
704
705       r = HMAC->GetHMACValue(hmac_buf);
706       if ( KM_SUCCESS( r ) )
707         {
708           Kumu::bin2hex(hmac_buf, HMAC_SIZE, hmac_str, HMAC_SIZE*10);
709         }
710       else
711         {
712           snprintf(hmac_str, HMAC_SIZE*10, " - read error - ");
713         }
714
715       DefaultLogSink().Error("IntegrityPack failure: HMAC is %s, expecting %s.\n", found_str, hmac_str);
716     }
717
718   return result;
719 }
720
721 //
722 // end AS_DCP_MXF.cpp
723 //