Update copyright dates.
[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() : 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       assert(PacketLength <= 0xFFFFFFFFL);
228       m_CtFrameBuf.Capacity((ui32_t) PacketLength);
229       ui32_t read_count;
230       result = m_File.Read(m_CtFrameBuf.Data(), (ui32_t) PacketLength,
231                            &read_count);
232
233       if ( ASDCP_FAILURE(result) )
234         return result;
235
236       if ( read_count != PacketLength )
237         {
238           DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
239           return RESULT_FORMAT;
240         }
241
242       m_CtFrameBuf.Size((ui32_t) PacketLength);
243
244       // should be const but mxflib::ReadBER is not
245       byte_t* ess_p = m_CtFrameBuf.Data();
246
247       // read context ID length
248       if ( ! Kumu::read_test_BER(&ess_p, UUIDlen) )
249         return RESULT_FORMAT;
250
251       // test the context ID
252       if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 )
253         {
254           DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
255           return RESULT_FORMAT;
256         }
257       ess_p += UUIDlen;
258
259       // read PlaintextOffset length
260       if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
261         return RESULT_FORMAT;
262
263       ui32_t PlaintextOffset = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
264       ess_p += sizeof(ui64_t);
265
266       // read essence UL length
267       if ( ! Kumu::read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
268         return RESULT_FORMAT;
269
270       // test essence UL
271       if ( memcmp(ess_p, EssenceUL, SMPTE_UL_LENGTH - 1) != 0 ) // ignore the stream number
272         {
273           char strbuf[IntBufferLen];
274           const MDDEntry* Entry = Dict::FindUL(Key.Value());
275           if ( Entry == 0 )
276             DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
277           else
278             DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
279           return RESULT_FORMAT;
280         }
281       ess_p += SMPTE_UL_LENGTH;
282
283       // read SourceLength length
284       if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
285         return RESULT_FORMAT;
286
287       ui32_t SourceLength = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
288       ess_p += sizeof(ui64_t);
289       assert(SourceLength);
290           
291       if ( FrameBuf.Capacity() < SourceLength )
292         {
293           DefaultLogSink().Error("FrameBuf.Capacity: %u SourceLength: %u\n", FrameBuf.Capacity(), SourceLength);
294           return RESULT_SMALLBUF;
295         }
296
297       ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
298
299       // read ESV length
300       if ( ! Kumu::read_test_BER(&ess_p, esv_length) )
301         {
302           DefaultLogSink().Error("read_test_BER did not return %u\n", esv_length);
303           return RESULT_FORMAT;
304         }
305
306       ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0);
307
308       if ( PacketLength < tmp_len )
309         {
310           DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
311           return RESULT_FORMAT;
312         }
313
314       if ( Ctx )
315         {
316           // wrap the pointer and length as a FrameBuffer for use by
317           // DecryptFrameBuffer() and TestValues()
318           FrameBuffer TmpWrapper;
319           TmpWrapper.SetData(ess_p, tmp_len);
320           TmpWrapper.Size(tmp_len);
321           TmpWrapper.SourceLength(SourceLength);
322           TmpWrapper.PlaintextOffset(PlaintextOffset);
323
324           result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
325           FrameBuf.FrameNumber(FrameNum);
326   
327           // detect and test integrity pack
328           if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC )
329             {
330               IntegrityPack IntPack;
331               result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, SequenceNum, HMAC);
332             }
333         }
334       else // return ciphertext to caller
335         {
336           if ( FrameBuf.Capacity() < tmp_len )
337             {
338               char intbuf[IntBufferLen];
339               DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
340                                      FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
341               return RESULT_SMALLBUF;
342             }
343
344           memcpy(FrameBuf.Data(), ess_p, tmp_len);
345           FrameBuf.Size(tmp_len);
346           FrameBuf.SourceLength(SourceLength);
347           FrameBuf.PlaintextOffset(PlaintextOffset);
348         }
349     }
350   else if ( memcmp(Key.Value(), EssenceUL, Key.Size() - 1) == 0 ) // ignore the stream number
351     { // read plaintext frame
352        if ( FrameBuf.Capacity() < PacketLength )
353         {
354           char intbuf[IntBufferLen];
355           DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
356                                  FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
357           return RESULT_SMALLBUF;
358         }
359
360       // read the data into the supplied buffer
361       ui32_t read_count;
362       assert(PacketLength <= 0xFFFFFFFFL);
363       result = m_File.Read(FrameBuf.Data(), (ui32_t) PacketLength, &read_count);
364           
365       if ( ASDCP_FAILURE(result) )
366         return result;
367
368       if ( read_count != PacketLength )
369         {
370           char intbuf1[IntBufferLen];
371           char intbuf2[IntBufferLen];
372           DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
373                                  ui64sz(read_count, intbuf1),
374                                  ui64sz(PacketLength, intbuf2) );
375           
376           return RESULT_READFAIL;
377         }
378
379       FrameBuf.FrameNumber(FrameNum);
380       FrameBuf.Size(read_count);
381     }
382   else
383     {
384       char strbuf[IntBufferLen];
385       const MDDEntry* Entry = Dict::FindUL(Key.Value());
386       if ( Entry == 0 )
387         DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
388       else
389         DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
390       return RESULT_FORMAT;
391     }
392
393   return result;
394 }
395
396
397 //
398 // end h__Reader.cpp
399 //