2 Copyright (c) 2004-2005, John Hurst
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
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.
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.
27 /*! \file h__Reader.cpp
29 \brief MXF file reader base class
32 #include "AS_DCP_internal.h"
37 using namespace ASDCP;
38 using namespace ASDCP::MXF;
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";
46 ASDCP::h__Reader::h__Reader() : m_EssenceStart(0)
50 ASDCP::h__Reader::~h__Reader()
56 ASDCP::h__Reader::Close()
61 //------------------------------------------------------------------------------------------
66 ASDCP::h__Reader::InitInfo(WriterInfo& Info)
68 InterchangeObject* Object;
71 Result_t result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object);
73 if( ASDCP_SUCCESS(result) )
74 MD_to_WriterInfo((Identification*)Object, m_Info);
77 if( ASDCP_SUCCESS(result) )
78 result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object);
80 if( ASDCP_SUCCESS(result) )
82 SourcePackage* SP = (SourcePackage*)Object;
83 memcpy(Info.AssetUUID, SP->PackageUID.Data() + 16, UUIDlen);
86 // optional CryptographicContext
87 if( ASDCP_SUCCESS(result) )
89 Result_t cr_result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CryptographicContext), &Object);
91 if( ASDCP_SUCCESS(cr_result) )
92 MD_to_CryptoInfo((CryptographicContext*)Object, m_Info);
99 // standard method of opening an MXF file for read
101 ASDCP::h__Reader::OpenMXFRead(const char* filename)
103 Result_t result = m_File.OpenRead(filename);
105 if ( ASDCP_SUCCESS(result) )
106 result = m_HeaderPart.InitFromFile(m_File);
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();
112 if ( test_s < 2 || test_s > 3 )
114 DefaultLogSink().Error("RIP count is not 2 or 3: %lu\n", test_s);
115 return RESULT_FORMAT;
118 // it really OP-Atom?
119 // MDObject* OpPattern = GetMDObjectByType("OperationalPattern");
120 // TODO: check the label
122 // if this is a three partition file, go to the body
123 // partition and read off the partition pack
126 DefaultLogSink().Error("RIP count is 3: must write code...\n");
127 return RESULT_FORMAT;
129 // TODO: check the partition pack to make sure it is
130 // really a body with a single essence container
132 m_EssenceStart = m_File.Tell();
138 // standard method of populating the in-memory index
140 ASDCP::h__Reader::InitMXFIndex()
142 if ( ! m_File.IsOpen() )
145 Result_t result = m_File.Seek(m_HeaderPart.FooterPartition);
147 if ( ASDCP_SUCCESS(result) )
149 m_FooterPart.m_Lookup = &m_HeaderPart.m_Primer;
150 result = m_FooterPart.InitFromFile(m_File);
157 // standard method of reading a plaintext or encrypted frame
159 ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
160 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
162 // look up frame index node
163 IndexTableSegment::IndexEntry TmpEntry;
165 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
167 DefaultLogSink().Error("Frame value out of range: %lu\n", FrameNum);
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;
175 Result_t result = m_File.Seek(FilePosition);
177 if ( ASDCP_SUCCESS(result) )
178 result = Reader.ReadKLFromFile(m_File);
180 if ( ASDCP_FAILURE(result) )
183 UL Key(Reader.Key());
184 UL InteropRef(CryptEssenceUL_Data);
185 UL SMPTERef(CryptEssenceUL_Data);
186 ui64_t PacketLength = Reader.Length();
188 if ( Key == InteropRef || Key == SMPTERef )
190 if ( ! m_Info.EncryptedEssence )
192 DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
193 return RESULT_FORMAT;
196 // read encrypted triplet value into internal buffer
197 m_CtFrameBuf.Capacity(PacketLength);
199 result = m_File.Read(m_CtFrameBuf.Data(), PacketLength, &read_count);
201 if ( ASDCP_FAILURE(result) )
204 if ( read_count != PacketLength )
206 DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
207 return RESULT_FORMAT;
210 m_CtFrameBuf.Size(PacketLength);
212 // should be const but mxflib::ReadBER is not
213 byte_t* ess_p = m_CtFrameBuf.Data();
215 // read context ID length
216 if ( ! read_test_BER(&ess_p, UUIDlen) )
217 return RESULT_FORMAT;
219 // test the context ID
220 if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 )
222 DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
223 return RESULT_FORMAT;
227 // read PlaintextOffset length
228 if ( ! read_test_BER(&ess_p, sizeof(ui64_t)) )
229 return RESULT_FORMAT;
231 ui32_t PlaintextOffset = (ui32_t)ASDCP_i64_BE(cp2i<ui64_t>(ess_p));
232 ess_p += sizeof(ui64_t);
234 // read essence UL length
235 if ( ! read_test_BER(&ess_p, klv_key_size) )
236 return RESULT_FORMAT;
238 // TODO: test essence UL
239 ess_p += klv_key_size;
241 // read SourceLength length
242 if ( ! read_test_BER(&ess_p, sizeof(ui64_t)) )
243 return RESULT_FORMAT;
245 ui32_t SourceLength = (ui32_t)ASDCP_i64_BE(cp2i<ui64_t>(ess_p));
246 ess_p += sizeof(ui64_t);
247 assert(SourceLength);
249 if ( FrameBuf.Capacity() < SourceLength )
251 DefaultLogSink().Error("FrameBuf.Capacity: %lu SourceLength: %lu\n", FrameBuf.Capacity(), SourceLength);
252 return RESULT_SMALLBUF;
255 ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
258 if ( ! read_test_BER(&ess_p, esv_length) )
260 DefaultLogSink().Error("read_test_BER did not return %lu\n", esv_length);
261 return RESULT_FORMAT;
264 ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0);
266 if ( PacketLength < tmp_len )
268 DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
269 return RESULT_FORMAT;
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);
282 result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
283 FrameBuf.FrameNumber(FrameNum);
285 // detect and test integrity pack
286 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC )
288 IntegrityPack IntPack;
289 result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, FrameNum + 1, HMAC);
292 else // return ciphertext to caller
294 if ( FrameBuf.Capacity() < tmp_len )
295 return RESULT_SMALLBUF;
297 memcpy(FrameBuf.Data(), ess_p, tmp_len);
298 FrameBuf.Size(tmp_len);
299 FrameBuf.SourceLength(SourceLength);
300 FrameBuf.PlaintextOffset(PlaintextOffset);
303 else if ( Key == EssenceUL )
304 { // read plaintext frame
305 if ( FrameBuf.Capacity() < PacketLength )
307 char intbuf[IntBufferLen];
308 DefaultLogSink().Error("FrameBuf.Capacity: %lu FrameLength: %s\n",
309 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
310 return RESULT_SMALLBUF;
313 // read the data into the supplied buffer
315 result = m_File.Read(FrameBuf.Data(), PacketLength, &read_count);
317 if ( ASDCP_FAILURE(result) )
320 if ( read_count != PacketLength )
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) );
328 return RESULT_READFAIL;
331 FrameBuf.FrameNumber(FrameNum);
332 FrameBuf.Size(read_count);
336 DefaultLogSink().Error("Unexpected UL found.\n");
337 return RESULT_FORMAT;