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