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