4e20a98360a53d376d2318924bdec95a80f52c08
[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(WriterInfo& Info)
60 {
61   InterchangeObject* Object;
62
63   // Identification
64   Result_t result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object);
65
66   if( ASDCP_SUCCESS(result) )
67     MD_to_WriterInfo((Identification*)Object, m_Info);
68
69   // SourcePackage
70   if( ASDCP_SUCCESS(result) )
71     result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object);
72
73   if( ASDCP_SUCCESS(result) )
74     {
75       SourcePackage* SP = (SourcePackage*)Object;
76       memcpy(Info.AssetUUID, SP->PackageUID.Value() + 16, UUIDlen);
77     }
78
79   // optional CryptographicContext
80   if( ASDCP_SUCCESS(result) )
81     {
82       Result_t cr_result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CryptographicContext), &Object);
83
84       if( ASDCP_SUCCESS(cr_result) )
85         MD_to_CryptoInfo((CryptographicContext*)Object, m_Info);
86     }
87
88   return result;
89 }
90
91
92 // standard method of opening an MXF file for read
93 Result_t
94 ASDCP::h__Reader::OpenMXFRead(const char* filename)
95 {
96   m_LastPosition = 0;
97   Result_t result = m_File.OpenRead(filename);
98
99   if ( ASDCP_SUCCESS(result) )
100     result = m_HeaderPart.InitFromFile(m_File);
101
102   // if this is a three partition file, go to the body
103   // partition and read off the partition pack
104   if ( m_HeaderPart.m_RIP.PairArray.size() == 3 )
105     {
106       DefaultLogSink().Error("RIP count is 3: must write code...\n");
107       return RESULT_FORMAT;
108     }
109   // TODO: check the partition pack to make sure it is
110   //       really a body with a single essence container
111
112   m_EssenceStart = m_File.Tell();
113
114   return RESULT_OK;
115 }
116
117
118 // standard method of populating the in-memory index
119 Result_t
120 ASDCP::h__Reader::InitMXFIndex()
121 {
122   if ( ! m_File.IsOpen() )
123     return RESULT_INIT;
124
125   Result_t result = m_File.Seek(m_HeaderPart.FooterPartition);
126
127   if ( ASDCP_SUCCESS(result) )
128     {
129       m_FooterPart.m_Lookup = &m_HeaderPart.m_Primer;
130       result = m_FooterPart.InitFromFile(m_File);
131     }
132
133   if ( ASDCP_SUCCESS(result) )
134     m_File.Seek(m_EssenceStart);
135
136   return result;
137 }
138
139
140 // standard method of reading a plaintext or encrypted frame
141 Result_t
142 ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
143                                  const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
144 {
145   // look up frame index node
146   IndexTableSegment::IndexEntry TmpEntry;
147
148   if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
149     {
150       DefaultLogSink().Error("Frame value out of range: %lu\n", FrameNum);
151       return RESULT_RANGE;
152     }
153
154   // get frame position and go read the frame's key and length
155   Result_t result = RESULT_OK;
156   ASDCP::KLVReader Reader;
157   ASDCP::fpos_t FilePosition = m_EssenceStart + TmpEntry.StreamOffset;
158
159   if ( FilePosition != m_LastPosition )
160     {
161       m_LastPosition = FilePosition;
162       result = m_File.Seek(FilePosition);
163     }
164
165   if ( ASDCP_SUCCESS(result) )
166     result = Reader.ReadKLFromFile(m_File);
167
168   if ( ASDCP_FAILURE(result) )
169     return result;
170
171   UL Key(Reader.Key());
172   UL CryptEssenceUL(Dict::ul(MDD_CryptEssence));
173   ui64_t PacketLength = Reader.Length();
174   m_LastPosition = m_LastPosition + Reader.KLLength() + PacketLength;
175
176   if ( Key == CryptEssenceUL )
177     {
178       if ( ! m_Info.EncryptedEssence )
179         {
180           DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
181           return RESULT_FORMAT;
182         }
183
184       // read encrypted triplet value into internal buffer
185       m_CtFrameBuf.Capacity(PacketLength);
186       ui32_t read_count;
187       result = m_File.Read(m_CtFrameBuf.Data(), PacketLength, &read_count);
188
189       if ( ASDCP_FAILURE(result) )
190         return result;
191
192       if ( read_count != PacketLength )
193         {
194           DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
195           return RESULT_FORMAT;
196         }
197
198       m_CtFrameBuf.Size(PacketLength);
199
200       // should be const but mxflib::ReadBER is not
201       byte_t* ess_p = m_CtFrameBuf.Data();
202
203       // read context ID length
204       if ( ! read_test_BER(&ess_p, UUIDlen) )
205         return RESULT_FORMAT;
206
207       // test the context ID
208       if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 )
209         {
210           DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
211           return RESULT_FORMAT;
212         }
213       ess_p += UUIDlen;
214
215       // read PlaintextOffset length
216       if ( ! read_test_BER(&ess_p, sizeof(ui64_t)) )
217         return RESULT_FORMAT;
218
219       ui32_t PlaintextOffset = (ui32_t)ASDCP_i64_BE(cp2i<ui64_t>(ess_p));
220       ess_p += sizeof(ui64_t);
221
222       // read essence UL length
223       if ( ! read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
224         return RESULT_FORMAT;
225
226       // TODO: test essence UL
227       ess_p += SMPTE_UL_LENGTH;
228
229       // read SourceLength length
230       if ( ! read_test_BER(&ess_p, sizeof(ui64_t)) )
231         return RESULT_FORMAT;
232
233       ui32_t SourceLength = (ui32_t)ASDCP_i64_BE(cp2i<ui64_t>(ess_p));
234       ess_p += sizeof(ui64_t);
235       assert(SourceLength);
236           
237       if ( FrameBuf.Capacity() < SourceLength )
238         {
239           DefaultLogSink().Error("FrameBuf.Capacity: %lu SourceLength: %lu\n", FrameBuf.Capacity(), SourceLength);
240           return RESULT_SMALLBUF;
241         }
242
243       ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
244
245       // read ESV length
246       if ( ! read_test_BER(&ess_p, esv_length) )
247         {
248           DefaultLogSink().Error("read_test_BER did not return %lu\n", esv_length);
249           return RESULT_FORMAT;
250         }
251
252       ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0);
253
254       if ( PacketLength < tmp_len )
255         {
256           DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
257           return RESULT_FORMAT;
258         }
259
260       if ( Ctx )
261         {
262           // wrap the pointer and length as a FrameBuffer for use by
263           // DecryptFrameBuffer() and TestValues()
264           FrameBuffer TmpWrapper;
265           TmpWrapper.SetData(ess_p, tmp_len);
266           TmpWrapper.Size(tmp_len);
267           TmpWrapper.SourceLength(SourceLength);
268           TmpWrapper.PlaintextOffset(PlaintextOffset);
269
270           result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
271           FrameBuf.FrameNumber(FrameNum);
272   
273           // detect and test integrity pack
274           if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC )
275             {
276               IntegrityPack IntPack;
277               result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, FrameNum + 1, HMAC);
278             }
279         }
280       else // return ciphertext to caller
281         {
282           if ( FrameBuf.Capacity() < tmp_len )
283             return RESULT_SMALLBUF;
284
285           memcpy(FrameBuf.Data(), ess_p, tmp_len);
286           FrameBuf.Size(tmp_len);
287           FrameBuf.SourceLength(SourceLength);
288           FrameBuf.PlaintextOffset(PlaintextOffset);
289         }
290     }
291   else if ( Key == EssenceUL )
292     { // read plaintext frame
293        if ( FrameBuf.Capacity() < PacketLength )
294         {
295           char intbuf[IntBufferLen];
296           DefaultLogSink().Error("FrameBuf.Capacity: %lu FrameLength: %s\n",
297                                  FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
298           return RESULT_SMALLBUF;
299         }
300
301       // read the data into the supplied buffer
302       ui32_t read_count;
303       result = m_File.Read(FrameBuf.Data(), PacketLength, &read_count);
304           
305       if ( ASDCP_FAILURE(result) )
306         return result;
307
308       if ( read_count != PacketLength )
309         {
310           char intbuf1[IntBufferLen];
311           char intbuf2[IntBufferLen];
312           DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
313                                  ui64sz(read_count, intbuf1),
314                                  ui64sz(PacketLength, intbuf2) );
315           
316           return RESULT_READFAIL;
317         }
318
319       FrameBuf.FrameNumber(FrameNum);
320       FrameBuf.Size(read_count);
321     }
322   else
323     {
324       char strbuf[IntBufferLen];
325       const MDDEntry* Entry = Dict::FindUL(Key.Value());
326       if ( Entry == 0 )
327         DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.ToString(strbuf));
328       else
329         DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
330       return RESULT_FORMAT;
331     }
332
333   return result;
334 }
335
336
337 //
338 // end h__Reader.cpp
339 //