o Added optional Generic Partition to IMF Aux Data prototype, used to carry global...
[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 "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 std::string& filename, EssenceType_t& type)
165 {
166   const Dictionary* m_Dict = &DefaultCompositeDict();
167   InterchangeObject* md_object = 0;
168
169   assert(m_Dict);
170   Kumu::FileReader   Reader;
171   OP1aHeader TestHeader(m_Dict);
172
173   Result_t result = Reader.OpenRead(filename);
174
175   if ( ASDCP_SUCCESS(result) )
176     result = TestHeader.InitFromFile(Reader); // test UL and OP
177
178   if ( ASDCP_SUCCESS(result) )
179     {
180       type = ESS_UNKNOWN;
181
182       if ( TestHeader.OperationalPattern == UL(m_Dict->ul(MDD_OPAtom))
183            || TestHeader.OperationalPattern == UL(m_Dict->ul(MDD_MXFInterop_OPAtom)) )
184         {
185           if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor))) )
186             {
187               if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(StereoscopicPictureSubDescriptor))) )
188                 {
189                   type = ESS_JPEG_2000_S;
190                 }
191               else
192                 {
193                   type = ESS_JPEG_2000;
194                 }
195             }
196           else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &md_object)) )
197             {
198               assert(md_object);
199               if ( static_cast<ASDCP::MXF::WaveAudioDescriptor*>(md_object)->AudioSamplingRate == SampleRate_96k )
200                 {
201                   type = ESS_PCM_24b_96k;
202                 }
203               else
204                 {
205                   type = ESS_PCM_24b_48k;
206                 }
207             }
208           else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor))) )
209             {
210               type = ESS_MPEG2_VES;
211             }
212           else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(TimedTextDescriptor))) )
213             {
214               type = ESS_TIMED_TEXT;
215             }
216           else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(DCDataDescriptor)))
217                     || ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(PrivateDCDataDescriptor))) )
218             {
219               if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(DolbyAtmosSubDescriptor))) )
220                 {
221                   type = ESS_DCDATA_DOLBY_ATMOS;
222                 }
223               else
224                 {
225                   type = ESS_DCDATA_UNKNOWN;
226                 }
227             }
228         }
229       else if (  TestHeader.OperationalPattern == UL(m_Dict->ul(MDD_OP1a)) )
230         {
231           if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor))) )
232             {
233               type = ESS_AS02_JPEG_2000;
234             }
235           else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &md_object)) )
236             {
237               assert(md_object);
238               if ( static_cast<ASDCP::MXF::WaveAudioDescriptor*>(md_object)->AudioSamplingRate == SampleRate_96k )
239                 {
240                   type = ESS_AS02_PCM_24b_96k;
241                 }
242               else
243                 {
244                   type = ESS_AS02_PCM_24b_48k;
245                 }
246             }
247           else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(TimedTextDescriptor))) )
248             {
249               type = ESS_AS02_TIMED_TEXT;
250             }
251           else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(IMFDynamicMetadataDescriptor))) )
252             {
253               type = ESS_DCDATA_UNKNOWN;
254             }
255         }
256       else
257         {
258           DefaultLogSink().Error("Unsupported MXF Operational Pattern.\n");
259           return RESULT_FORMAT;
260         }
261     }
262
263   return result;
264 }
265
266 //
267 static bool
268 string_is_xml(const ASDCP::FrameBuffer& buffer)
269 {
270   return (strncmp((const char *)buffer.RoData(),             "<?xml", 5) == 0 ||
271           strncmp((const char *)buffer.RoData(), "\xEF\xBB\xBF<?xml", 8) == 0); // Allow BOM
272  }
273  
274  //
275
276 //
277 ASDCP::Result_t
278 ASDCP::RawEssenceType(const std::string& filename, EssenceType_t& type)
279 {
280   type = ESS_UNKNOWN;
281   ASDCP::FrameBuffer FB;
282   Kumu::FileReader Reader;
283   ASDCP::Wav::SimpleWaveHeader WavHeader;
284   ASDCP::RF64::SimpleRF64Header RF64Header;
285   ASDCP::AIFF::SimpleAIFFHeader AIFFHeader;
286   Kumu::XMLElement TmpElement("Tmp");
287
288   ui32_t data_offset;
289   ui32_t read_count;
290   Result_t result = FB.Capacity(Wav::MaxWavHeader); // using Wav max because everything else is much smaller
291
292   if ( Kumu::PathIsFile(filename) )
293     {
294       result = Reader.OpenRead(filename);
295
296       if ( ASDCP_SUCCESS(result) )
297         {
298           result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
299           Reader.Close();
300         }
301
302       if ( ASDCP_SUCCESS(result) )
303         {
304           const byte_t* p = FB.RoData();
305           FB.Size(read_count);
306
307           ui32_t i = 0;
308           while ( p[i] == 0 ) i++;
309
310           if ( i > 1 && p[i] == 1 &&  (p[i+1] == ASDCP::MPEG2::SEQ_START || p[i+1] == ASDCP::MPEG2::PIC_START) )
311             {
312               type = ESS_MPEG2_VES;
313             }
314           else if ( memcmp(FB.RoData(), ASDCP::JP2K::Magic, sizeof(ASDCP::JP2K::Magic)) == 0 )
315             {
316               type = ESS_JPEG_2000;
317             }
318           else if ( std::string((const char*)FB.RoData() + 8, 4) == "WAVE" )
319             {
320               if ( std::string((const char*)FB.RoData(), 4) == "RIFF" )
321                 {
322                   result = WavHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset);
323
324                   if ( ASDCP_SUCCESS(result) )
325                     {
326                       switch ( WavHeader.samplespersec )
327                         {
328                         case 48000: type = ESS_PCM_24b_48k; break;
329                         case 96000: type = ESS_PCM_24b_96k; break;
330                         default:
331                           DefaultLogSink().Error("Unexpected sample rate: %d\n", WavHeader.samplespersec);
332                           result = RESULT_FORMAT;
333                         }
334                     }
335                 }
336               else
337                 {
338                   result = RF64Header.ReadFromBuffer(FB.RoData(), read_count, &data_offset);
339                 
340                   if ( ASDCP_SUCCESS(result) )
341                     {
342                       switch ( RF64Header.samplespersec )
343                         {
344                         case 48000: type = ESS_PCM_24b_48k; break;
345                         case 96000: type = ESS_PCM_24b_96k; break;
346                         default:
347                           DefaultLogSink().Error("Unexpected sample rate: %d\n", WavHeader.samplespersec);
348                           result = RESULT_FORMAT;
349                         }
350                     }
351                 }
352             }
353           else if ( ASDCP_SUCCESS(AIFFHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
354             {
355               type = ESS_PCM_24b_48k;
356             }
357           else if ( string_is_xml(FB) )
358             {
359               type = ESS_TIMED_TEXT;
360             }
361           else if ( ASDCP::ATMOS::IsDolbyAtmos(filename) )
362             {
363               type = ESS_DCDATA_DOLBY_ATMOS;
364             }
365         }
366     }
367   else if ( Kumu::PathIsDirectory(filename) )
368     {
369       char next_file[Kumu::MaxFilePath];
370       Kumu::DirScanner Scanner;
371       Result_t result = Scanner.Open(filename);
372
373       if ( ASDCP_SUCCESS(result) )
374         {
375           while ( ASDCP_SUCCESS(Scanner.GetNext(next_file)) )
376             {
377               if ( next_file[0] == '.' ) // no hidden files or internal links
378                 continue;
379
380               result = Reader.OpenRead(Kumu::PathJoin(filename, next_file));
381
382               if ( ASDCP_SUCCESS(result) )
383                 {
384                   result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
385                   Reader.Close();
386                 }
387
388               if ( ASDCP_SUCCESS(result) )
389                 {
390                   if ( memcmp(FB.RoData(), ASDCP::JP2K::Magic, sizeof(ASDCP::JP2K::Magic)) == 0 )
391                     {
392                       type = ESS_JPEG_2000;
393                     }
394                   else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
395                     {
396                       switch ( WavHeader.samplespersec )
397                         {
398                         case 48000: type = ESS_PCM_24b_48k; break;
399                         case 96000: type = ESS_PCM_24b_96k; break;
400                         default:
401                           return RESULT_FORMAT;
402                         }
403                     }
404                   else if ( ASDCP_SUCCESS(RF64Header.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
405                     {
406                       switch ( RF64Header.samplespersec )
407                         {
408                         case 48000: type = ESS_PCM_24b_48k; break;
409                         case 96000: type = ESS_PCM_24b_96k; break;
410                         default:
411                           return RESULT_FORMAT;
412                         }
413                     }
414                   else if ( ASDCP::ATMOS::IsDolbyAtmos(Kumu::PathJoin(filename, next_file)) )
415                     {
416                       type = ESS_DCDATA_DOLBY_ATMOS;
417                     }
418                   else
419                     {
420                       type = ESS_DCDATA_UNKNOWN;
421                     }
422                 }
423               
424               break;
425             }
426         }
427     }
428
429   return result;
430 }
431
432 //
433 Result_t
434 ASDCP::EncryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESEncContext* Ctx)
435 {
436   ASDCP_TEST_NULL(Ctx);
437   FBout.Size(0);
438
439   // size the buffer
440   Result_t result = FBout.Capacity(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));
441
442   // write the IV
443   byte_t* p = FBout.Data();
444
445   // write the IV to the frame buffer
446   Ctx->GetIVec(p);
447   p += CBC_BLOCK_SIZE;
448
449
450   // encrypt the check value to the frame buffer
451   if ( ASDCP_SUCCESS(result) )
452     {
453       result = Ctx->EncryptBlock(ESV_CheckValue, p, CBC_BLOCK_SIZE);
454       p += CBC_BLOCK_SIZE;
455     }
456
457   // write optional plaintext region
458   if ( FBin.PlaintextOffset() > 0 )
459     {
460       assert(FBin.PlaintextOffset() <= FBin.Size());
461       memcpy(p, FBin.RoData(), FBin.PlaintextOffset());
462       p += FBin.PlaintextOffset();
463     }
464
465   ui32_t ct_size = FBin.Size() - FBin.PlaintextOffset();
466   ui32_t diff = ct_size % CBC_BLOCK_SIZE;
467   ui32_t block_size = ct_size - diff;
468   assert((block_size % CBC_BLOCK_SIZE) == 0);
469
470   // encrypt the ciphertext region essence data
471   if ( ASDCP_SUCCESS(result) )
472     {
473       result = Ctx->EncryptBlock(FBin.RoData() + FBin.PlaintextOffset(), p, block_size);
474       p += block_size;
475     }
476
477   // construct and encrypt the padding
478   if ( ASDCP_SUCCESS(result) )
479     {
480       byte_t the_last_block[CBC_BLOCK_SIZE];
481
482       if ( diff > 0 )
483         memcpy(the_last_block, FBin.RoData() + FBin.PlaintextOffset() + block_size, diff);
484
485       for (ui32_t i = 0; diff < CBC_BLOCK_SIZE; diff++, i++ )
486         the_last_block[diff] = i;
487
488       result = Ctx->EncryptBlock(the_last_block, p, CBC_BLOCK_SIZE);
489     }
490
491   if ( ASDCP_SUCCESS(result) )
492     FBout.Size(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));
493
494   return result;
495 }
496
497 //
498 Result_t
499 ASDCP::DecryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESDecContext* Ctx)
500 {
501   ASDCP_TEST_NULL(Ctx);
502   assert(FBout.Capacity() >= FBin.SourceLength());
503
504   ui32_t ct_size = FBin.SourceLength() - FBin.PlaintextOffset();
505   ui32_t diff = ct_size % CBC_BLOCK_SIZE;
506   ui32_t block_size = ct_size - diff;
507   assert(block_size);
508   assert((block_size % CBC_BLOCK_SIZE) == 0);
509
510   const byte_t* buf = FBin.RoData();
511
512   // get ivec
513   Ctx->SetIVec(buf);
514   buf += CBC_BLOCK_SIZE;
515
516   // decrypt and test check value
517   byte_t CheckValue[CBC_BLOCK_SIZE];
518   Result_t result = Ctx->DecryptBlock(buf, CheckValue, CBC_BLOCK_SIZE);
519   buf += CBC_BLOCK_SIZE;
520
521   if ( memcmp(CheckValue, ESV_CheckValue, CBC_BLOCK_SIZE) != 0 )
522     return RESULT_CHECKFAIL;
523
524   // copy plaintext region
525   if ( FBin.PlaintextOffset() > 0 )
526     {
527       memcpy(FBout.Data(), buf, FBin.PlaintextOffset());
528       buf += FBin.PlaintextOffset();
529     }
530
531   // decrypt all but last block
532   if ( ASDCP_SUCCESS(result) )
533     {
534       result = Ctx->DecryptBlock(buf, FBout.Data() + FBin.PlaintextOffset(), block_size);
535       buf += block_size;
536     }
537
538   // decrypt last block
539   if ( ASDCP_SUCCESS(result) )
540     {
541       byte_t the_last_block[CBC_BLOCK_SIZE];
542       result = Ctx->DecryptBlock(buf, the_last_block, CBC_BLOCK_SIZE);
543
544       if ( the_last_block[diff] != 0 )
545         {
546           DefaultLogSink().Error("Unexpected non-zero padding value.\n");
547           return RESULT_FORMAT;
548         }
549
550       if ( diff > 0 )
551         memcpy(FBout.Data() + FBin.PlaintextOffset() + block_size, the_last_block, diff);
552     }
553
554   if ( ASDCP_SUCCESS(result) )
555     FBout.Size(FBin.SourceLength());
556
557   return result;
558 }
559
560
561 //
562 Result_t
563 ASDCP::IntegrityPack::CalcValues(const ASDCP::FrameBuffer& FB, const byte_t* AssetID,
564                                  ui32_t sequence, HMACContext* HMAC)
565 {
566   ASDCP_TEST_NULL(AssetID);
567   ASDCP_TEST_NULL(HMAC);
568   byte_t* p = Data;
569   HMAC->Reset();
570
571   static byte_t ber_4[MXF_BER_LENGTH] = {0x83, 0, 0, 0};
572
573   // update HMAC with essence data
574   HMAC->Update(FB.RoData(), FB.Size());
575
576   // track file ID length
577   memcpy(p, ber_4, MXF_BER_LENGTH);
578   *(p+3) = UUIDlen;;
579   p += MXF_BER_LENGTH;
580
581   // track file ID
582   memcpy(p, AssetID, UUIDlen);
583   p += UUIDlen;
584
585   // sequence length
586   memcpy(p, ber_4, MXF_BER_LENGTH);
587   *(p+3) = sizeof(ui64_t);
588   p += MXF_BER_LENGTH;
589
590   // sequence number
591   Kumu::i2p<ui64_t>(KM_i64_BE(sequence), p);
592   p += sizeof(ui64_t);
593
594   // HMAC length
595   memcpy(p, ber_4, MXF_BER_LENGTH);
596   *(p+3) = HMAC_SIZE;
597   p += MXF_BER_LENGTH;
598
599   // update HMAC with intpack values
600   HMAC->Update(Data, klv_intpack_size - HMAC_SIZE);
601
602   // finish & write HMAC
603   HMAC->Finalize();
604   HMAC->GetHMACValue(p);
605
606   assert(p + HMAC_SIZE == Data + klv_intpack_size);
607
608   return RESULT_OK;
609 }
610
611
612 Result_t
613 ASDCP::IntegrityPack::TestValues(const ASDCP::FrameBuffer& FB, const byte_t* AssetID,
614                                  ui32_t sequence, HMACContext* HMAC)
615 {
616   ASDCP_TEST_NULL(AssetID);
617   ASDCP_TEST_NULL(HMAC);
618
619   // find the start of the intpack
620   byte_t* p = (byte_t*)FB.RoData() + ( FB.Size() - klv_intpack_size );
621
622   // test the AssetID length
623   if ( ! Kumu::read_test_BER(&p, UUIDlen) )
624         return RESULT_HMACFAIL;
625
626   // test the AssetID
627   if ( memcmp(p, AssetID, UUIDlen) != 0 )
628     {
629       DefaultLogSink().Error("IntegrityPack failure: AssetID mismatch.\n");
630       return RESULT_HMACFAIL;
631     }
632   p += UUIDlen;
633   
634   // test the sequence length
635   if ( ! Kumu::read_test_BER(&p, sizeof(ui64_t)) )
636         return RESULT_HMACFAIL;
637
638   ui32_t test_sequence = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(p));
639
640   // test the sequence value
641   if ( test_sequence != sequence )
642     {
643       DefaultLogSink().Error("IntegrityPack failure: sequence is %u, expecting %u.\n", test_sequence, sequence);
644       return RESULT_HMACFAIL;
645     }
646
647   p += sizeof(ui64_t);
648
649   // test the HMAC length
650   if ( ! Kumu::read_test_BER(&p, HMAC_SIZE) )
651         return RESULT_HMACFAIL;
652
653   // test the HMAC
654   HMAC->Reset();
655   HMAC->Update(FB.RoData(), FB.Size() - HMAC_SIZE);
656   HMAC->Finalize();
657
658   return HMAC->TestHMACValue(p);
659 }
660
661 //
662 // end AS_DCP_MXF.cpp
663 //