mega datetime patch
[asdcplib.git] / src / h__Reader.cpp
1 /*
2 Copyright (c) 2004-2012, 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    h__Reader.cpp
28     \version $Id$
29     \brief   MXF file reader base class
30 */
31
32 #define DEFAULT_MD_DECL
33 #include "AS_DCP_internal.h"
34 #include "KLV.h"
35
36 using namespace ASDCP;
37 using namespace ASDCP::MXF;
38
39 static Kumu::Mutex sg_DefaultMDInitLock;
40 static bool        sg_DefaultMDTypesInit = false;
41 static const ASDCP::Dictionary *sg_dict;
42
43 //
44 void
45 ASDCP::default_md_object_init()
46 {
47   if ( ! sg_DefaultMDTypesInit )
48     {
49       Kumu::AutoMutex BlockLock(sg_DefaultMDInitLock);
50
51       if ( ! sg_DefaultMDTypesInit )
52         {
53           sg_dict = &DefaultSMPTEDict();
54           g_OPAtomHeader = new ASDCP::MXF::OPAtomHeader(sg_dict);
55           g_OPAtomIndexFooter = new ASDCP::MXF::OPAtomIndexFooter(sg_dict);
56           sg_DefaultMDTypesInit = true;
57         }
58     }
59 }
60
61
62 //
63 ASDCP::h__Reader::h__Reader(const Dictionary& d) :
64   m_HeaderPart(m_Dict), m_BodyPart(m_Dict), m_FooterPart(m_Dict), m_Dict(&d), m_EssenceStart(0)
65 {
66   default_md_object_init();
67 }
68
69 ASDCP::h__Reader::~h__Reader()
70 {
71   Close();
72 }
73
74 void
75 ASDCP::h__Reader::Close()
76 {
77   m_File.Close();
78 }
79
80 //------------------------------------------------------------------------------------------
81 //
82
83 //
84 Result_t
85 ASDCP::h__Reader::InitInfo()
86 {
87   assert(m_Dict);
88   InterchangeObject* Object;
89
90   m_Info.LabelSetType = LS_MXF_UNKNOWN;
91
92   if ( m_HeaderPart.OperationalPattern.ExactMatch(MXFInterop_OPAtom_Entry().ul) )
93     m_Info.LabelSetType = LS_MXF_INTEROP;
94   else if ( m_HeaderPart.OperationalPattern.ExactMatch(SMPTE_390_OPAtom_Entry().ul) )
95     m_Info.LabelSetType = LS_MXF_SMPTE;
96
97   // Identification
98   Result_t result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object);
99
100   if( ASDCP_SUCCESS(result) )
101     MD_to_WriterInfo((Identification*)Object, m_Info);
102
103   // SourcePackage
104   if( ASDCP_SUCCESS(result) )
105     result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object);
106
107   if( ASDCP_SUCCESS(result) )
108     {
109       SourcePackage* SP = (SourcePackage*)Object;
110       memcpy(m_Info.AssetUUID, SP->PackageUID.Value() + 16, UUIDlen);
111     }
112
113   // optional CryptographicContext
114   if( ASDCP_SUCCESS(result) )
115     {
116       Result_t cr_result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CryptographicContext), &Object);
117
118       if( ASDCP_SUCCESS(cr_result) )
119         MD_to_CryptoInfo((CryptographicContext*)Object, m_Info, *m_Dict);
120     }
121
122   return result;
123 }
124
125
126 // standard method of opening an MXF file for read
127 Result_t
128 ASDCP::h__Reader::OpenMXFRead(const char* filename)
129 {
130   m_LastPosition = 0;
131   Result_t result = m_File.OpenRead(filename);
132
133   if ( ASDCP_SUCCESS(result) )
134     result = m_HeaderPart.InitFromFile(m_File);
135
136   if ( ASDCP_SUCCESS(result) )
137     {
138       // if this is a three partition file, go to the body
139       // partition and read the partition pack
140       if ( m_HeaderPart.m_RIP.PairArray.size() > 2 )
141         {
142           Array<RIP::Pair>::iterator r_i = m_HeaderPart.m_RIP.PairArray.begin();
143           r_i++;
144           m_File.Seek((*r_i).ByteOffset);
145
146           result = m_BodyPart.InitFromFile(m_File);
147         }
148
149       m_EssenceStart = m_File.Tell();
150     }
151
152   return result;
153 }
154
155
156 // standard method of populating the in-memory index
157 Result_t
158 ASDCP::h__Reader::InitMXFIndex()
159 {
160   if ( ! m_File.IsOpen() )
161     return RESULT_INIT;
162
163   Result_t result = m_File.Seek(m_HeaderPart.FooterPartition);
164
165   if ( ASDCP_SUCCESS(result) )
166     {
167       m_FooterPart.m_Lookup = &m_HeaderPart.m_Primer;
168       result = m_FooterPart.InitFromFile(m_File);
169     }
170
171   if ( ASDCP_SUCCESS(result) )
172     m_File.Seek(m_EssenceStart);
173
174   return result;
175 }
176
177 //
178 Result_t
179 ASDCP::KLReader::ReadKLFromFile(Kumu::FileReader& Reader)
180 {
181   ui32_t read_count;
182   ui32_t header_length = SMPTE_UL_LENGTH + MXF_BER_LENGTH;
183   Result_t result = Reader.Read(m_KeyBuf, header_length, &read_count);
184
185   if ( ASDCP_FAILURE(result) )
186     return result;
187
188   if ( read_count != header_length )
189     return RESULT_READFAIL;
190
191   const byte_t* ber_start = m_KeyBuf + SMPTE_UL_LENGTH;
192
193   if ( ( *ber_start & 0x80 ) == 0 )
194     {
195       DefaultLogSink().Error("BER encoding error.\n");
196       return RESULT_FORMAT;
197     }
198
199   ui8_t ber_size = ( *ber_start & 0x0f ) + 1;
200
201   if ( ber_size > 9 )
202     {
203       DefaultLogSink().Error("BER size encoding error.\n");
204       return RESULT_FORMAT;
205     }
206
207   if ( ber_size < MXF_BER_LENGTH )
208     {
209       DefaultLogSink().Error("BER size %d shorter than AS-DCP minimum %d.\n",
210                              ber_size, MXF_BER_LENGTH);
211       return RESULT_FORMAT;
212     }
213
214   if ( ber_size > MXF_BER_LENGTH )
215     {
216       ui32_t diff = ber_size - MXF_BER_LENGTH;
217       assert((SMPTE_UL_LENGTH + MXF_BER_LENGTH + diff) <= (SMPTE_UL_LENGTH * 2));
218       result = Reader.Read(m_KeyBuf + SMPTE_UL_LENGTH + MXF_BER_LENGTH, diff, &read_count);
219
220       if ( ASDCP_FAILURE(result) )
221         return result;
222
223       if ( read_count != diff )
224         return RESULT_READFAIL;
225
226       header_length += diff;
227     }
228
229   return InitFromBuffer(m_KeyBuf, header_length);
230 }
231
232 // standard method of reading a plaintext or encrypted frame
233 Result_t
234 ASDCP::h__Reader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
235                                 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
236 {
237   // look up frame index node
238   IndexTableSegment::IndexEntry TmpEntry;
239
240   if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
241     {
242       DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
243       return RESULT_RANGE;
244     }
245
246   // get frame position and go read the frame's key and length
247   Kumu::fpos_t FilePosition = m_EssenceStart + TmpEntry.StreamOffset;
248   Result_t result = RESULT_OK;
249
250   if ( FilePosition != m_LastPosition )
251     {
252       m_LastPosition = FilePosition;
253       result = m_File.Seek(FilePosition);
254     }
255
256   if( ASDCP_SUCCESS(result) )
257     result = ReadEKLVPacket(FrameNum, FrameNum + 1, FrameBuf, EssenceUL, Ctx, HMAC);
258
259   return result;
260 }
261
262
263 Result_t
264 ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
265                                  const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
266 {
267   KLReader Reader;
268   Result_t result = Reader.ReadKLFromFile(m_File);
269
270   if ( ASDCP_FAILURE(result) )
271     return result;
272
273   UL Key(Reader.Key());
274   ui64_t PacketLength = Reader.Length();
275   m_LastPosition = m_LastPosition + Reader.KLLength() + PacketLength;
276   assert(m_Dict);
277
278   if ( Key.MatchIgnoreStream(m_Dict->ul(MDD_CryptEssence)) )  // ignore the stream numbers
279     {
280       if ( ! m_Info.EncryptedEssence )
281         {
282           DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
283           return RESULT_FORMAT;
284         }
285
286       // read encrypted triplet value into internal buffer
287       assert(PacketLength <= 0xFFFFFFFFL);
288       m_CtFrameBuf.Capacity((ui32_t) PacketLength);
289       ui32_t read_count;
290       result = m_File.Read(m_CtFrameBuf.Data(), (ui32_t) PacketLength,
291                            &read_count);
292
293       if ( ASDCP_FAILURE(result) )
294         return result;
295
296       if ( read_count != PacketLength )
297         {
298           DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
299           return RESULT_FORMAT;
300         }
301
302       m_CtFrameBuf.Size((ui32_t) PacketLength);
303
304       // should be const but mxflib::ReadBER is not
305       byte_t* ess_p = m_CtFrameBuf.Data();
306
307       // read context ID length
308       if ( ! Kumu::read_test_BER(&ess_p, UUIDlen) )
309         return RESULT_FORMAT;
310
311       // test the context ID
312       if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 )
313         {
314           DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
315           return RESULT_FORMAT;
316         }
317       ess_p += UUIDlen;
318
319       // read PlaintextOffset length
320       if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
321         return RESULT_FORMAT;
322
323       ui32_t PlaintextOffset = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
324       ess_p += sizeof(ui64_t);
325
326       // read essence UL length
327       if ( ! Kumu::read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
328         return RESULT_FORMAT;
329
330       // test essence UL
331       if ( ! UL(ess_p).MatchIgnoreStream(EssenceUL) ) // ignore the stream number
332         {
333           char strbuf[IntBufferLen];
334           const MDDEntry* Entry = m_Dict->FindUL(Key.Value());
335           if ( Entry == 0 )
336             DefaultLogSink().Warn("Unexpected Encrypted Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
337           else
338             DefaultLogSink().Warn("Unexpected Encrypted Essence UL found: %s.\n", Entry->name);
339           return RESULT_FORMAT;
340         }
341       ess_p += SMPTE_UL_LENGTH;
342
343       // read SourceLength length
344       if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
345         return RESULT_FORMAT;
346
347       ui32_t SourceLength = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
348       ess_p += sizeof(ui64_t);
349       assert(SourceLength);
350           
351       if ( FrameBuf.Capacity() < SourceLength )
352         {
353           DefaultLogSink().Error("FrameBuf.Capacity: %u SourceLength: %u\n", FrameBuf.Capacity(), SourceLength);
354           return RESULT_SMALLBUF;
355         }
356
357       ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
358
359       // read ESV length
360       if ( ! Kumu::read_test_BER(&ess_p, esv_length) )
361         {
362           DefaultLogSink().Error("read_test_BER did not return %u\n", esv_length);
363           return RESULT_FORMAT;
364         }
365
366       ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0);
367
368       if ( PacketLength < tmp_len )
369         {
370           DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
371           return RESULT_FORMAT;
372         }
373
374       if ( Ctx )
375         {
376           // wrap the pointer and length as a FrameBuffer for use by
377           // DecryptFrameBuffer() and TestValues()
378           FrameBuffer TmpWrapper;
379           TmpWrapper.SetData(ess_p, tmp_len);
380           TmpWrapper.Size(tmp_len);
381           TmpWrapper.SourceLength(SourceLength);
382           TmpWrapper.PlaintextOffset(PlaintextOffset);
383
384           result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
385           FrameBuf.FrameNumber(FrameNum);
386   
387           // detect and test integrity pack
388           if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC )
389             {
390               IntegrityPack IntPack;
391               result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, SequenceNum, HMAC);
392             }
393         }
394       else // return ciphertext to caller
395         {
396           if ( FrameBuf.Capacity() < tmp_len )
397             {
398               char intbuf[IntBufferLen];
399               DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
400                                      FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
401               return RESULT_SMALLBUF;
402             }
403
404           memcpy(FrameBuf.Data(), ess_p, tmp_len);
405           FrameBuf.Size(tmp_len);
406           FrameBuf.FrameNumber(FrameNum);
407           FrameBuf.SourceLength(SourceLength);
408           FrameBuf.PlaintextOffset(PlaintextOffset);
409         }
410     }
411   else if ( Key.MatchIgnoreStream(EssenceUL) ) // ignore the stream number
412     { // read plaintext frame
413        if ( FrameBuf.Capacity() < PacketLength )
414         {
415           char intbuf[IntBufferLen];
416           DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
417                                  FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
418           return RESULT_SMALLBUF;
419         }
420
421       // read the data into the supplied buffer
422       ui32_t read_count;
423       assert(PacketLength <= 0xFFFFFFFFL);
424       result = m_File.Read(FrameBuf.Data(), (ui32_t) PacketLength, &read_count);
425           
426       if ( ASDCP_FAILURE(result) )
427         return result;
428
429       if ( read_count != PacketLength )
430         {
431           char intbuf1[IntBufferLen];
432           char intbuf2[IntBufferLen];
433           DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
434                                  ui64sz(read_count, intbuf1),
435                                  ui64sz(PacketLength, intbuf2) );
436           
437           return RESULT_READFAIL;
438         }
439
440       FrameBuf.FrameNumber(FrameNum);
441       FrameBuf.Size(read_count);
442     }
443   else
444     {
445       char strbuf[IntBufferLen];
446       const MDDEntry* Entry = m_Dict->FindUL(Key.Value());
447       if ( Entry == 0 )
448         DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
449       else
450         DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
451       return RESULT_FORMAT;
452     }
453
454   return result;
455 }
456
457
458 //
459 // end h__Reader.cpp
460 //