Denis' bug fixes
[asdcplib.git] / src / h__Reader.cpp
1 /*
2 Copyright (c) 2004-2006, 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() : m_EssenceStart(0)
40 {
41 }
42
43 ASDCP::h__Reader::~h__Reader()
44 {
45   Close();
46 }
47
48 void
49 ASDCP::h__Reader::Close()
50 {
51   m_File.Close();
52 }
53
54 //------------------------------------------------------------------------------------------
55 //
56
57 //
58 Result_t
59 ASDCP::h__Reader::InitInfo()
60 {
61   InterchangeObject* Object;
62
63   m_Info.LabelSetType = LS_MXF_UNKNOWN;
64   UL OPAtomUL(Dict::ul(MDD_OPAtom));
65   UL Interop_OPAtomUL(Dict::ul(MDD_MXFInterop_OPAtom));
66
67   if ( m_HeaderPart.OperationalPattern == Interop_OPAtomUL )
68     m_Info.LabelSetType = LS_MXF_INTEROP;
69   else if ( m_HeaderPart.OperationalPattern == OPAtomUL )
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);
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() == 3 )
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 class KLReader : public ASDCP::KLVPacket
154 {
155   ASDCP_NO_COPY_CONSTRUCT(KLReader);
156   byte_t m_KeyBuf[32];
157
158 public:
159   KLReader() {}
160   ~KLReader() {}
161
162   inline const byte_t* Key() { return m_KeyBuf; }
163   inline const ui64_t  Length() { return m_ValueLength; }
164   inline const ui64_t  KLLength() { return m_KLLength; }
165
166   Result_t ReadKLFromFile(Kumu::FileReader& Reader)
167   {
168     ui32_t read_count;
169     ui32_t header_length = SMPTE_UL_LENGTH + MXF_BER_LENGTH;
170     Result_t result = Reader.Read(m_KeyBuf, header_length, &read_count);
171
172     if ( read_count != header_length )
173       return RESULT_READFAIL;
174   
175     if ( ASDCP_SUCCESS(result) )
176       result = InitFromBuffer(m_KeyBuf, header_length);
177
178     return result;
179   }
180 };
181
182
183 // standard method of reading a plaintext or encrypted frame
184 Result_t
185 ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
186                                  const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
187 {
188   // look up frame index node
189   IndexTableSegment::IndexEntry TmpEntry;
190
191   if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
192     {
193       DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
194       return RESULT_RANGE;
195     }
196
197   // get frame position and go read the frame's key and length
198   Result_t result = RESULT_OK;
199   KLReader Reader;
200   Kumu::fpos_t FilePosition = m_EssenceStart + TmpEntry.StreamOffset;
201
202   if ( FilePosition != m_LastPosition )
203     {
204       m_LastPosition = FilePosition;
205       result = m_File.Seek(FilePosition);
206     }
207
208   if ( ASDCP_SUCCESS(result) )
209     result = Reader.ReadKLFromFile(m_File);
210
211   if ( ASDCP_FAILURE(result) )
212     return result;
213
214   UL Key(Reader.Key());
215   ui64_t PacketLength = Reader.Length();
216   m_LastPosition = m_LastPosition + Reader.KLLength() + PacketLength;
217
218   if ( memcmp(Key.Value(), Dict::ul(MDD_CryptEssence), Key.Size() - 1) == 0  // ignore the stream numbers
219        || memcmp(Key.Value(), Dict::ul(MDD_MXFInterop_CryptEssence), Key.Size() - 1) == 0 )
220     {
221       if ( ! m_Info.EncryptedEssence )
222         {
223           DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
224           return RESULT_FORMAT;
225         }
226
227       // read encrypted triplet value into internal buffer
228       m_CtFrameBuf.Capacity(PacketLength);
229       ui32_t read_count;
230       result = m_File.Read(m_CtFrameBuf.Data(), PacketLength, &read_count);
231
232       if ( ASDCP_FAILURE(result) )
233         return result;
234
235       if ( read_count != PacketLength )
236         {
237           DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
238           return RESULT_FORMAT;
239         }
240
241       m_CtFrameBuf.Size(PacketLength);
242
243       // should be const but mxflib::ReadBER is not
244       byte_t* ess_p = m_CtFrameBuf.Data();
245
246       // read context ID length
247       if ( ! Kumu::read_test_BER(&ess_p, UUIDlen) )
248         return RESULT_FORMAT;
249
250       // test the context ID
251       if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 )
252         {
253           DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
254           return RESULT_FORMAT;
255         }
256       ess_p += UUIDlen;
257
258       // read PlaintextOffset length
259       if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
260         return RESULT_FORMAT;
261
262       ui32_t PlaintextOffset = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
263       ess_p += sizeof(ui64_t);
264
265       // read essence UL length
266       if ( ! Kumu::read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
267         return RESULT_FORMAT;
268
269       // test essence UL
270       if ( memcmp(ess_p, EssenceUL, SMPTE_UL_LENGTH - 1) != 0 ) // ignore the stream number
271         {
272           char strbuf[IntBufferLen];
273           const MDDEntry* Entry = Dict::FindUL(Key.Value());
274           if ( Entry == 0 )
275             DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
276           else
277             DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
278           return RESULT_FORMAT;
279         }
280       ess_p += SMPTE_UL_LENGTH;
281
282       // read SourceLength length
283       if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
284         return RESULT_FORMAT;
285
286       ui32_t SourceLength = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
287       ess_p += sizeof(ui64_t);
288       assert(SourceLength);
289           
290       if ( FrameBuf.Capacity() < SourceLength )
291         {
292           DefaultLogSink().Error("FrameBuf.Capacity: %u SourceLength: %u\n", FrameBuf.Capacity(), SourceLength);
293           return RESULT_SMALLBUF;
294         }
295
296       ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
297
298       // read ESV length
299       if ( ! Kumu::read_test_BER(&ess_p, esv_length) )
300         {
301           DefaultLogSink().Error("read_test_BER did not return %u\n", esv_length);
302           return RESULT_FORMAT;
303         }
304
305       ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0);
306
307       if ( PacketLength < tmp_len )
308         {
309           DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
310           return RESULT_FORMAT;
311         }
312
313       if ( Ctx )
314         {
315           // wrap the pointer and length as a FrameBuffer for use by
316           // DecryptFrameBuffer() and TestValues()
317           FrameBuffer TmpWrapper;
318           TmpWrapper.SetData(ess_p, tmp_len);
319           TmpWrapper.Size(tmp_len);
320           TmpWrapper.SourceLength(SourceLength);
321           TmpWrapper.PlaintextOffset(PlaintextOffset);
322
323           result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
324           FrameBuf.FrameNumber(FrameNum);
325   
326           // detect and test integrity pack
327           if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC )
328             {
329               IntegrityPack IntPack;
330               result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, FrameNum + 1, HMAC);
331             }
332         }
333       else // return ciphertext to caller
334         {
335           if ( FrameBuf.Capacity() < tmp_len )
336             {
337               char intbuf[IntBufferLen];
338               DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
339                                      FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
340               return RESULT_SMALLBUF;
341             }
342
343           memcpy(FrameBuf.Data(), ess_p, tmp_len);
344           FrameBuf.Size(tmp_len);
345           FrameBuf.SourceLength(SourceLength);
346           FrameBuf.PlaintextOffset(PlaintextOffset);
347         }
348     }
349   else if ( memcmp(Key.Value(), EssenceUL, Key.Size() - 1) == 0 ) // ignore the stream number
350     { // read plaintext frame
351        if ( FrameBuf.Capacity() < PacketLength )
352         {
353           char intbuf[IntBufferLen];
354           DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
355                                  FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
356           return RESULT_SMALLBUF;
357         }
358
359       // read the data into the supplied buffer
360       ui32_t read_count;
361       result = m_File.Read(FrameBuf.Data(), PacketLength, &read_count);
362           
363       if ( ASDCP_FAILURE(result) )
364         return result;
365
366       if ( read_count != PacketLength )
367         {
368           char intbuf1[IntBufferLen];
369           char intbuf2[IntBufferLen];
370           DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
371                                  ui64sz(read_count, intbuf1),
372                                  ui64sz(PacketLength, intbuf2) );
373           
374           return RESULT_READFAIL;
375         }
376
377       FrameBuf.FrameNumber(FrameNum);
378       FrameBuf.Size(read_count);
379     }
380   else
381     {
382       char strbuf[IntBufferLen];
383       const MDDEntry* Entry = Dict::FindUL(Key.Value());
384       if ( Entry == 0 )
385         DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
386       else
387         DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
388       return RESULT_FORMAT;
389     }
390
391   return result;
392 }
393
394
395 //
396 // end h__Reader.cpp
397 //