the big-pre-as-02-refactor
[asdcplib.git] / src / h__Reader.cpp
1 /*
2 Copyright (c) 2004-2013, 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 #define DEFAULT_MD_DECL
33 #include "AS_DCP_internal.h"
34 #include "KLV.h"
35
36 using namespace ASDCP;
37 using namespace ASDCP::MXF;
38
39 static Kumu::Mutex sg_DefaultMDInitLock;
40 static bool        sg_DefaultMDTypesInit = false;
41 static const ASDCP::Dictionary *sg_dict;
42
43 //
44 void
45 ASDCP::default_md_object_init()
46 {
47   if ( ! sg_DefaultMDTypesInit )
48     {
49       Kumu::AutoMutex BlockLock(sg_DefaultMDInitLock);
50
51       if ( ! sg_DefaultMDTypesInit )
52         {
53           sg_dict = &DefaultSMPTEDict();
54           g_OP1aHeader = new ASDCP::MXF::OP1aHeader(sg_dict);
55           g_OPAtomIndexFooter = new ASDCP::MXF::OPAtomIndexFooter(sg_dict);
56           g_RIP = new ASDCP::MXF::RIP(sg_dict);
57           sg_DefaultMDTypesInit = true;
58         }
59     }
60 }
61
62
63 //------------------------------------------------------------------------------------------
64 //
65
66 //
67 ASDCP::h__ASDCPReader::h__ASDCPReader(const Dictionary& d) : MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>(d), m_BodyPart(m_Dict) {}
68 ASDCP::h__ASDCPReader::~h__ASDCPReader() {}
69
70
71 // AS-DCP method of opening an MXF file for read
72 Result_t
73 ASDCP::h__ASDCPReader::OpenMXFRead(const char* filename)
74 {
75   Result_t result = ASDCP::MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>::OpenMXFRead(filename);
76
77   if ( KM_SUCCESS(result) )
78     result = ASDCP::MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>::InitInfo();
79
80   if( KM_SUCCESS(result) )
81     {
82       //
83       InterchangeObject* Object;
84
85       m_Info.LabelSetType = LS_MXF_UNKNOWN;
86
87       if ( m_HeaderPart.OperationalPattern.ExactMatch(MXFInterop_OPAtom_Entry().ul) )
88         {
89           m_Info.LabelSetType = LS_MXF_INTEROP;
90         }
91       else if ( m_HeaderPart.OperationalPattern.ExactMatch(SMPTE_390_OPAtom_Entry().ul) )
92         {
93           m_Info.LabelSetType = LS_MXF_SMPTE;
94         }
95       else
96         {
97           char strbuf[IdentBufferLen];
98           const MDDEntry* Entry = m_Dict->FindUL(m_HeaderPart.OperationalPattern.Value());
99
100           if ( Entry == 0 )
101             {
102               DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n",
103                                     m_HeaderPart.OperationalPattern.EncodeString(strbuf, IdentBufferLen));
104             }
105           else
106             {
107               DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", Entry->name);
108             }
109         }
110
111       //
112       if ( m_RIP.PairArray.size() < 2 )
113         {
114           // OP-Atom states that there will be either two or three partitions:
115           // one closed header and one closed footer with an optional body
116           // SMPTE 429-5 files may have many partitions, see SMPTE ST 410.
117           DefaultLogSink().Warn("RIP entry count is less than 2: %u\n", m_RIP.PairArray.size());
118         }
119       else if ( m_RIP.PairArray.size() > 2 )
120         {
121           // if this is a three partition file, go to the body
122           // partition and read the partition pack
123           Array<RIP::Pair>::iterator r_i = m_RIP.PairArray.begin();
124           r_i++;
125           m_File.Seek((*r_i).ByteOffset);
126           result = m_BodyPart.InitFromFile(m_File);
127
128           if( ASDCP_FAILURE(result) )
129             {
130               DefaultLogSink().Error("ASDCP::h__ASDCPReader::OpenMXFRead, m_BodyPart.InitFromFile failed\n");
131             }
132         }
133       else if ( m_RIP.PairArray.front().ByteOffset != 0 )
134         {
135           DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
136           result = RESULT_FORMAT;
137         }
138     }
139
140   if ( KM_SUCCESS(result) )
141     {
142       m_HeaderPart.BodyOffset = m_File.Tell();
143
144       result = m_File.Seek(m_HeaderPart.FooterPartition);
145
146       if ( ASDCP_SUCCESS(result) )
147         {
148           m_IndexAccess.m_Lookup = &m_HeaderPart.m_Primer;
149           result = m_IndexAccess.InitFromFile(m_File);
150         }
151     }
152
153   m_File.Seek(m_HeaderPart.BodyOffset);
154   return result;
155 }
156
157 // AS-DCP method of reading a plaintext or encrypted frame
158 Result_t
159 ASDCP::h__ASDCPReader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
160                                      const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
161 {
162   return ASDCP::MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>::ReadEKLVFrame(m_HeaderPart, FrameNum, FrameBuf,
163                                                                                      EssenceUL, Ctx, HMAC);
164 }
165
166 Result_t
167 ASDCP::h__ASDCPReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset,
168                            i8_t& temporalOffset, i8_t& keyFrameOffset)
169 {
170   return ASDCP::MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>::LocateFrame(m_HeaderPart, FrameNum,
171                                                                                    streamOffset, temporalOffset, keyFrameOffset);
172 }
173
174
175 //------------------------------------------------------------------------------------------
176 //
177
178
179 //
180 Result_t
181 ASDCP::KLReader::ReadKLFromFile(Kumu::FileReader& Reader)
182 {
183   ui32_t read_count;
184   ui32_t header_length = SMPTE_UL_LENGTH + MXF_BER_LENGTH;
185   Result_t result = Reader.Read(m_KeyBuf, header_length, &read_count);
186
187   if ( ASDCP_FAILURE(result) )
188     return result;
189
190   if ( read_count != header_length )
191     return RESULT_READFAIL;
192
193   const byte_t* ber_start = m_KeyBuf + SMPTE_UL_LENGTH;
194
195   if ( ( *ber_start & 0x80 ) == 0 )
196     {
197       DefaultLogSink().Error("BER encoding error.\n");
198       return RESULT_FORMAT;
199     }
200
201   ui8_t ber_size = ( *ber_start & 0x0f ) + 1;
202
203   if ( ber_size > 9 )
204     {
205       DefaultLogSink().Error("BER size encoding error.\n");
206       return RESULT_FORMAT;
207     }
208
209   if ( ber_size < MXF_BER_LENGTH )
210     {
211       DefaultLogSink().Error("BER size %d shorter than AS-DCP minimum %d.\n",
212                              ber_size, MXF_BER_LENGTH);
213       return RESULT_FORMAT;
214     }
215
216   if ( ber_size > MXF_BER_LENGTH )
217     {
218       ui32_t diff = ber_size - MXF_BER_LENGTH;
219       assert((SMPTE_UL_LENGTH + MXF_BER_LENGTH + diff) <= (SMPTE_UL_LENGTH * 2));
220       result = Reader.Read(m_KeyBuf + SMPTE_UL_LENGTH + MXF_BER_LENGTH, diff, &read_count);
221
222       if ( ASDCP_FAILURE(result) )
223         return result;
224
225       if ( read_count != diff )
226         return RESULT_READFAIL;
227
228       header_length += diff;
229     }
230
231   return InitFromBuffer(m_KeyBuf, header_length);
232 }
233
234 // base subroutine for reading a KLV packet, assumes file position is at the first byte of the packet
235 Result_t
236 ASDCP::Read_EKLV_Packet(Kumu::FileReader& File, const ASDCP::Dictionary& Dict, const MXF::OP1aHeader& HeaderPart,
237                         const ASDCP::WriterInfo& Info, Kumu::fpos_t& LastPosition, ASDCP::FrameBuffer& CtFrameBuf,
238                         ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
239                         const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
240 {
241   KLReader Reader;
242   Result_t result = Reader.ReadKLFromFile(File);
243
244   if ( KM_FAILURE(result) )
245     return result;
246
247   UL Key(Reader.Key());
248   ui64_t PacketLength = Reader.Length();
249   LastPosition = LastPosition + Reader.KLLength() + PacketLength;
250
251   if ( Key.MatchIgnoreStream(Dict.ul(MDD_CryptEssence)) )  // ignore the stream numbers
252     {
253       if ( ! Info.EncryptedEssence )
254         {
255           DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
256           return RESULT_FORMAT;
257         }
258
259       // read encrypted triplet value into internal buffer
260       assert(PacketLength <= 0xFFFFFFFFL);
261       CtFrameBuf.Capacity((ui32_t) PacketLength);
262       ui32_t read_count;
263       result = File.Read(CtFrameBuf.Data(), (ui32_t) PacketLength, &read_count);
264
265       if ( ASDCP_FAILURE(result) )
266         return result;
267
268       if ( read_count != PacketLength )
269         {
270           DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
271           return RESULT_FORMAT;
272         }
273
274       CtFrameBuf.Size((ui32_t) PacketLength);
275
276       // should be const but mxflib::ReadBER is not
277       byte_t* ess_p = CtFrameBuf.Data();
278
279       // read context ID length
280       if ( ! Kumu::read_test_BER(&ess_p, UUIDlen) )
281         return RESULT_FORMAT;
282
283       // test the context ID
284       if ( memcmp(ess_p, Info.ContextID, UUIDlen) != 0 )
285         {
286           DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
287           return RESULT_FORMAT;
288         }
289       ess_p += UUIDlen;
290
291       // read PlaintextOffset length
292       if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
293         return RESULT_FORMAT;
294
295       ui32_t PlaintextOffset = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
296       ess_p += sizeof(ui64_t);
297
298       // read essence UL length
299       if ( ! Kumu::read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
300         return RESULT_FORMAT;
301
302       // test essence UL
303       if ( ! UL(ess_p).MatchIgnoreStream(EssenceUL) ) // ignore the stream number
304         {
305           char strbuf[IntBufferLen];
306           const MDDEntry* Entry = Dict.FindUL(Key.Value());
307
308           if ( Entry == 0 )
309             {
310               DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
311             }
312           else
313             {
314               DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
315             }
316
317           return RESULT_FORMAT;
318         }
319
320       ess_p += SMPTE_UL_LENGTH;
321
322       // read SourceLength length
323       if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
324         return RESULT_FORMAT;
325
326       ui32_t SourceLength = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
327       ess_p += sizeof(ui64_t);
328       assert(SourceLength);
329           
330       if ( FrameBuf.Capacity() < SourceLength )
331         {
332           DefaultLogSink().Error("FrameBuf.Capacity: %u SourceLength: %u\n", FrameBuf.Capacity(), SourceLength);
333           return RESULT_SMALLBUF;
334         }
335
336       ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
337
338       // read ESV length
339       if ( ! Kumu::read_test_BER(&ess_p, esv_length) )
340         {
341           DefaultLogSink().Error("read_test_BER did not return %u\n", esv_length);
342           return RESULT_FORMAT;
343         }
344
345       ui32_t tmp_len = esv_length + (Info.UsesHMAC ? klv_intpack_size : 0);
346
347       if ( PacketLength < tmp_len )
348         {
349           DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
350           return RESULT_FORMAT;
351         }
352
353       if ( Ctx )
354         {
355           // wrap the pointer and length as a FrameBuffer for use by
356           // DecryptFrameBuffer() and TestValues()
357           FrameBuffer TmpWrapper;
358           TmpWrapper.SetData(ess_p, tmp_len);
359           TmpWrapper.Size(tmp_len);
360           TmpWrapper.SourceLength(SourceLength);
361           TmpWrapper.PlaintextOffset(PlaintextOffset);
362
363           result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
364           FrameBuf.FrameNumber(FrameNum);
365   
366           // detect and test integrity pack
367           if ( ASDCP_SUCCESS(result) && Info.UsesHMAC && HMAC )
368             {
369               IntegrityPack IntPack;
370               result = IntPack.TestValues(TmpWrapper, Info.AssetUUID, SequenceNum, HMAC);
371             }
372         }
373       else // return ciphertext to caller
374         {
375           if ( FrameBuf.Capacity() < tmp_len )
376             {
377               char intbuf[IntBufferLen];
378               DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
379                                      FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
380               return RESULT_SMALLBUF;
381             }
382
383           memcpy(FrameBuf.Data(), ess_p, tmp_len);
384           FrameBuf.Size(tmp_len);
385           FrameBuf.FrameNumber(FrameNum);
386           FrameBuf.SourceLength(SourceLength);
387           FrameBuf.PlaintextOffset(PlaintextOffset);
388         }
389     }
390   else if ( Key.MatchIgnoreStream(EssenceUL) ) // ignore the stream number
391     { // read plaintext frame
392        if ( FrameBuf.Capacity() < PacketLength )
393         {
394           char intbuf[IntBufferLen];
395           DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
396                                  FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
397           return RESULT_SMALLBUF;
398         }
399
400       // read the data into the supplied buffer
401       ui32_t read_count;
402       assert(PacketLength <= 0xFFFFFFFFL);
403       result = File.Read(FrameBuf.Data(), (ui32_t) PacketLength, &read_count);
404           
405       if ( ASDCP_FAILURE(result) )
406         return result;
407
408       if ( read_count != PacketLength )
409         {
410           char intbuf1[IntBufferLen];
411           char intbuf2[IntBufferLen];
412           DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
413                                  ui64sz(read_count, intbuf1),
414                                  ui64sz(PacketLength, intbuf2) );
415           
416           return RESULT_READFAIL;
417         }
418
419       FrameBuf.FrameNumber(FrameNum);
420       FrameBuf.Size(read_count);
421     }
422   else
423     {
424       char strbuf[IntBufferLen];
425       const MDDEntry* Entry = Dict.FindUL(Key.Value());
426
427       if ( Entry == 0 )
428         {
429           DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
430         }
431       else
432         {
433           DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
434         }
435
436       return RESULT_FORMAT;
437     }
438
439   return result;
440 }
441
442
443 //
444 // end h__Reader.cpp
445 //