ginormo merge-back with Kumu, SMPTE MIC key and MPEG parser fix
[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 this is a three partition file, go to the body
112   // partition and read off the partition pack
113   if ( m_HeaderPart.m_RIP.PairArray.size() == 3 )
114     {
115       Array<RIP::Pair>::iterator r_i = m_HeaderPart.m_RIP.PairArray.begin();
116       r_i++;
117       m_File.Seek((*r_i).ByteOffset);
118
119       result = m_BodyPart.InitFromFile(m_File);
120     }
121
122   m_EssenceStart = m_File.Tell();
123   return RESULT_OK;
124 }
125
126
127 // standard method of populating the in-memory index
128 Result_t
129 ASDCP::h__Reader::InitMXFIndex()
130 {
131   if ( ! m_File.IsOpen() )
132     return RESULT_INIT;
133
134   Result_t result = m_File.Seek(m_HeaderPart.FooterPartition);
135
136   if ( ASDCP_SUCCESS(result) )
137     {
138       m_FooterPart.m_Lookup = &m_HeaderPart.m_Primer;
139       result = m_FooterPart.InitFromFile(m_File);
140     }
141
142   if ( ASDCP_SUCCESS(result) )
143     m_File.Seek(m_EssenceStart);
144
145   return result;
146 }
147
148 //
149 class KLReader : public ASDCP::KLVPacket
150 {
151   ASDCP_NO_COPY_CONSTRUCT(KLReader);
152   byte_t m_KeyBuf[32];
153
154 public:
155   KLReader() {}
156   ~KLReader() {}
157
158   inline const byte_t* Key() { return m_KeyBuf; }
159   inline const ui64_t  Length() { return m_ValueLength; }
160   inline const ui64_t  KLLength() { return m_KLLength; }
161
162   Result_t ReadKLFromFile(Kumu::FileReader& Reader)
163   {
164     ui32_t read_count;
165     ui32_t header_length = SMPTE_UL_LENGTH + MXF_BER_LENGTH;
166     Result_t result = Reader.Read(m_KeyBuf, header_length, &read_count);
167
168     if ( read_count != header_length )
169       return RESULT_READFAIL;
170   
171     if ( ASDCP_SUCCESS(result) )
172       result = InitFromBuffer(m_KeyBuf, header_length);
173
174     return result;
175   }
176 };
177
178
179 // standard method of reading a plaintext or encrypted frame
180 Result_t
181 ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
182                                  const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
183 {
184   // look up frame index node
185   IndexTableSegment::IndexEntry TmpEntry;
186
187   if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
188     {
189       DefaultLogSink().Error("Frame value out of range: %lu\n", FrameNum);
190       return RESULT_RANGE;
191     }
192
193   // get frame position and go read the frame's key and length
194   Result_t result = RESULT_OK;
195   KLReader Reader;
196   Kumu::fpos_t FilePosition = m_EssenceStart + TmpEntry.StreamOffset;
197
198   if ( FilePosition != m_LastPosition )
199     {
200       m_LastPosition = FilePosition;
201       result = m_File.Seek(FilePosition);
202     }
203
204   if ( ASDCP_SUCCESS(result) )
205     result = Reader.ReadKLFromFile(m_File);
206
207   if ( ASDCP_FAILURE(result) )
208     return result;
209
210   UL Key(Reader.Key());
211   ui64_t PacketLength = Reader.Length();
212   m_LastPosition = m_LastPosition + Reader.KLLength() + PacketLength;
213
214   if ( memcmp(Key.Value(), Dict::ul(MDD_CryptEssence), Key.Size() - 1) == 0  // ignore the stream numbers
215        || memcmp(Key.Value(), Dict::ul(MDD_MXFInterop_CryptEssence), Key.Size() - 1) == 0 )
216     {
217       if ( ! m_Info.EncryptedEssence )
218         {
219           DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
220           return RESULT_FORMAT;
221         }
222
223       // read encrypted triplet value into internal buffer
224       m_CtFrameBuf.Capacity(PacketLength);
225       ui32_t read_count;
226       result = m_File.Read(m_CtFrameBuf.Data(), PacketLength, &read_count);
227
228       if ( ASDCP_FAILURE(result) )
229         return result;
230
231       if ( read_count != PacketLength )
232         {
233           DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
234           return RESULT_FORMAT;
235         }
236
237       m_CtFrameBuf.Size(PacketLength);
238
239       // should be const but mxflib::ReadBER is not
240       byte_t* ess_p = m_CtFrameBuf.Data();
241
242       // read context ID length
243       if ( ! Kumu::read_test_BER(&ess_p, UUIDlen) )
244         return RESULT_FORMAT;
245
246       // test the context ID
247       if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 )
248         {
249           DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
250           return RESULT_FORMAT;
251         }
252       ess_p += UUIDlen;
253
254       // read PlaintextOffset length
255       if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
256         return RESULT_FORMAT;
257
258       ui32_t PlaintextOffset = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
259       ess_p += sizeof(ui64_t);
260
261       // read essence UL length
262       if ( ! Kumu::read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
263         return RESULT_FORMAT;
264
265       // test essence UL
266       if ( memcmp(ess_p, EssenceUL, SMPTE_UL_LENGTH - 1) != 0 ) // ignore the stream number
267         {
268           char strbuf[IntBufferLen];
269           const MDDEntry* Entry = Dict::FindUL(Key.Value());
270           if ( Entry == 0 )
271             DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
272           else
273             DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
274           return RESULT_FORMAT;
275         }
276       ess_p += SMPTE_UL_LENGTH;
277
278       // read SourceLength length
279       if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
280         return RESULT_FORMAT;
281
282       ui32_t SourceLength = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
283       ess_p += sizeof(ui64_t);
284       assert(SourceLength);
285           
286       if ( FrameBuf.Capacity() < SourceLength )
287         {
288           DefaultLogSink().Error("FrameBuf.Capacity: %lu SourceLength: %lu\n", FrameBuf.Capacity(), SourceLength);
289           return RESULT_SMALLBUF;
290         }
291
292       ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
293
294       // read ESV length
295       if ( ! Kumu::read_test_BER(&ess_p, esv_length) )
296         {
297           DefaultLogSink().Error("read_test_BER did not return %lu\n", esv_length);
298           return RESULT_FORMAT;
299         }
300
301       ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0);
302
303       if ( PacketLength < tmp_len )
304         {
305           DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
306           return RESULT_FORMAT;
307         }
308
309       if ( Ctx )
310         {
311           // wrap the pointer and length as a FrameBuffer for use by
312           // DecryptFrameBuffer() and TestValues()
313           FrameBuffer TmpWrapper;
314           TmpWrapper.SetData(ess_p, tmp_len);
315           TmpWrapper.Size(tmp_len);
316           TmpWrapper.SourceLength(SourceLength);
317           TmpWrapper.PlaintextOffset(PlaintextOffset);
318
319           result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
320           FrameBuf.FrameNumber(FrameNum);
321   
322           // detect and test integrity pack
323           if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC )
324             {
325               IntegrityPack IntPack;
326               result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, FrameNum + 1, HMAC);
327             }
328         }
329       else // return ciphertext to caller
330         {
331           if ( FrameBuf.Capacity() < tmp_len )
332             return RESULT_SMALLBUF;
333
334           memcpy(FrameBuf.Data(), ess_p, tmp_len);
335           FrameBuf.Size(tmp_len);
336           FrameBuf.SourceLength(SourceLength);
337           FrameBuf.PlaintextOffset(PlaintextOffset);
338         }
339     }
340   else if ( memcmp(Key.Value(), EssenceUL, Key.Size() - 1) == 0 ) // ignore the stream number
341     { // read plaintext frame
342        if ( FrameBuf.Capacity() < PacketLength )
343         {
344           char intbuf[IntBufferLen];
345           DefaultLogSink().Error("FrameBuf.Capacity: %lu FrameLength: %s\n",
346                                  FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
347           return RESULT_SMALLBUF;
348         }
349
350       // read the data into the supplied buffer
351       ui32_t read_count;
352       result = m_File.Read(FrameBuf.Data(), PacketLength, &read_count);
353           
354       if ( ASDCP_FAILURE(result) )
355         return result;
356
357       if ( read_count != PacketLength )
358         {
359           char intbuf1[IntBufferLen];
360           char intbuf2[IntBufferLen];
361           DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
362                                  ui64sz(read_count, intbuf1),
363                                  ui64sz(PacketLength, intbuf2) );
364           
365           return RESULT_READFAIL;
366         }
367
368       FrameBuf.FrameNumber(FrameNum);
369       FrameBuf.Size(read_count);
370     }
371   else
372     {
373       char strbuf[IntBufferLen];
374       const MDDEntry* Entry = Dict::FindUL(Key.Value());
375       if ( Entry == 0 )
376         DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
377       else
378         DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
379       return RESULT_FORMAT;
380     }
381
382   return result;
383 }
384
385
386 //
387 // end h__Reader.cpp
388 //