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