fixed encryption for timed text
[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()
60 {
61   InterchangeObject* Object;
62
63   m_Info.LabelSetType = LS_MXF_UNKNOWN;
64   UL OPAtomUL(Dict::ul(MDD_OPAtom));
65   UL Interop_OPAtomUL(Dict::ul(MDD_MXFInterop_OPAtom));
66
67   if ( m_HeaderPart.OperationalPattern == Interop_OPAtomUL )
68     m_Info.LabelSetType = LS_MXF_INTEROP;
69   else if ( m_HeaderPart.OperationalPattern == OPAtomUL )
70     m_Info.LabelSetType = LS_MXF_SMPTE;
71
72   // Identification
73   Result_t result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object);
74
75   if( ASDCP_SUCCESS(result) )
76     MD_to_WriterInfo((Identification*)Object, m_Info);
77
78   // SourcePackage
79   if( ASDCP_SUCCESS(result) )
80     result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object);
81
82   if( ASDCP_SUCCESS(result) )
83     {
84       SourcePackage* SP = (SourcePackage*)Object;
85       memcpy(m_Info.AssetUUID, SP->PackageUID.Value() + 16, UUIDlen);
86     }
87
88   // optional CryptographicContext
89   if( ASDCP_SUCCESS(result) )
90     {
91       Result_t cr_result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CryptographicContext), &Object);
92
93       if( ASDCP_SUCCESS(cr_result) )
94         MD_to_CryptoInfo((CryptographicContext*)Object, m_Info);
95     }
96
97   return result;
98 }
99
100
101 // standard method of opening an MXF file for read
102 Result_t
103 ASDCP::h__Reader::OpenMXFRead(const char* filename)
104 {
105   m_LastPosition = 0;
106   Result_t result = m_File.OpenRead(filename);
107
108   if ( ASDCP_SUCCESS(result) )
109     result = m_HeaderPart.InitFromFile(m_File);
110
111   if ( ASDCP_SUCCESS(result) )
112     {
113       // if this is a three partition file, go to the body
114       // partition and read the partition pack
115       if ( m_HeaderPart.m_RIP.PairArray.size() > 2 )
116         {
117           Array<RIP::Pair>::iterator r_i = m_HeaderPart.m_RIP.PairArray.begin();
118           r_i++;
119           m_File.Seek((*r_i).ByteOffset);
120
121           result = m_BodyPart.InitFromFile(m_File);
122         }
123
124       m_EssenceStart = m_File.Tell();
125     }
126
127   return result;
128 }
129
130
131 // standard method of populating the in-memory index
132 Result_t
133 ASDCP::h__Reader::InitMXFIndex()
134 {
135   if ( ! m_File.IsOpen() )
136     return RESULT_INIT;
137
138   Result_t result = m_File.Seek(m_HeaderPart.FooterPartition);
139
140   if ( ASDCP_SUCCESS(result) )
141     {
142       m_FooterPart.m_Lookup = &m_HeaderPart.m_Primer;
143       result = m_FooterPart.InitFromFile(m_File);
144     }
145
146   if ( ASDCP_SUCCESS(result) )
147     m_File.Seek(m_EssenceStart);
148
149   return result;
150 }
151
152 //
153 class KLReader : public ASDCP::KLVPacket
154 {
155   ASDCP_NO_COPY_CONSTRUCT(KLReader);
156   byte_t m_KeyBuf[32];
157
158 public:
159   KLReader() {}
160   ~KLReader() {}
161
162   inline const byte_t* Key() { return m_KeyBuf; }
163   inline const ui64_t  Length() { return m_ValueLength; }
164   inline const ui64_t  KLLength() { return m_KLLength; }
165
166   Result_t ReadKLFromFile(Kumu::FileReader& Reader)
167   {
168     ui32_t read_count;
169     ui32_t header_length = SMPTE_UL_LENGTH + MXF_BER_LENGTH;
170     Result_t result = Reader.Read(m_KeyBuf, header_length, &read_count);
171
172     if ( read_count != header_length )
173       return RESULT_READFAIL;
174   
175     if ( ASDCP_SUCCESS(result) )
176       result = InitFromBuffer(m_KeyBuf, header_length);
177
178     return result;
179   }
180 };
181
182
183 // standard method of reading a plaintext or encrypted frame
184 Result_t
185 ASDCP::h__Reader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
186                                 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
187 {
188   // look up frame index node
189   IndexTableSegment::IndexEntry TmpEntry;
190
191   if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
192     {
193       DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
194       return RESULT_RANGE;
195     }
196
197   // get frame position and go read the frame's key and length
198   Kumu::fpos_t FilePosition = m_EssenceStart + TmpEntry.StreamOffset;
199   Result_t result = RESULT_OK;
200
201   if ( FilePosition != m_LastPosition )
202     {
203       m_LastPosition = FilePosition;
204       result = m_File.Seek(FilePosition);
205     }
206
207   if( ASDCP_SUCCESS(result) )
208     result = ReadEKLVPacket(FrameNum, FrameBuf, EssenceUL, Ctx, HMAC);
209
210   return result;
211 }
212
213
214 Result_t
215 ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
216                                  const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
217 {
218   KLReader Reader;
219   Result_t result = Reader.ReadKLFromFile(m_File);
220
221   if ( ASDCP_FAILURE(result) )
222     return result;
223
224   UL Key(Reader.Key());
225   ui64_t PacketLength = Reader.Length();
226   m_LastPosition = m_LastPosition + Reader.KLLength() + PacketLength;
227
228   if ( memcmp(Key.Value(), Dict::ul(MDD_CryptEssence), Key.Size() - 1) == 0  // ignore the stream numbers
229        || memcmp(Key.Value(), Dict::ul(MDD_MXFInterop_CryptEssence), Key.Size() - 1) == 0 )
230     {
231       if ( ! m_Info.EncryptedEssence )
232         {
233           DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
234           return RESULT_FORMAT;
235         }
236
237       // read encrypted triplet value into internal buffer
238       m_CtFrameBuf.Capacity(PacketLength);
239       ui32_t read_count;
240       result = m_File.Read(m_CtFrameBuf.Data(), 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       m_CtFrameBuf.Size(PacketLength);
252
253       // should be const but mxflib::ReadBER is not
254       byte_t* ess_p = m_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, m_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 ( memcmp(ess_p, EssenceUL, SMPTE_UL_LENGTH - 1) != 0 ) // 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 + (m_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) && m_Info.UsesHMAC && HMAC )
338             {
339               IntegrityPack IntPack;
340               result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, FrameNum + 1, 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.SourceLength(SourceLength);
356           FrameBuf.PlaintextOffset(PlaintextOffset);
357         }
358     }
359   else if ( memcmp(Key.Value(), EssenceUL, Key.Size() - 1) == 0 ) // ignore the stream number
360     { // read plaintext frame
361        if ( FrameBuf.Capacity() < PacketLength )
362         {
363           char intbuf[IntBufferLen];
364           DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
365                                  FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
366           return RESULT_SMALLBUF;
367         }
368
369       // read the data into the supplied buffer
370       ui32_t read_count;
371       result = m_File.Read(FrameBuf.Data(), PacketLength, &read_count);
372           
373       if ( ASDCP_FAILURE(result) )
374         return result;
375
376       if ( read_count != PacketLength )
377         {
378           char intbuf1[IntBufferLen];
379           char intbuf2[IntBufferLen];
380           DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
381                                  ui64sz(read_count, intbuf1),
382                                  ui64sz(PacketLength, intbuf2) );
383           
384           return RESULT_READFAIL;
385         }
386
387       FrameBuf.FrameNumber(FrameNum);
388       FrameBuf.Size(read_count);
389     }
390   else
391     {
392       char strbuf[IntBufferLen];
393       const MDDEntry* Entry = Dict::FindUL(Key.Value());
394       if ( Entry == 0 )
395         DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
396       else
397         DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
398       return RESULT_FORMAT;
399     }
400
401   return result;
402 }
403
404
405 //
406 // end h__Reader.cpp
407 //