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