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