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