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