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