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