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