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 ASDCP::h__Reader::h__Reader() : m_EssenceStart(0)
45 ASDCP::h__Reader::~h__Reader()
51 ASDCP::h__Reader::Close()
56 //------------------------------------------------------------------------------------------
61 ASDCP::h__Reader::InitInfo(WriterInfo& Info)
63 InterchangeObject* Object;
66 Result_t result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object);
68 if( ASDCP_SUCCESS(result) )
69 MD_to_WriterInfo((Identification*)Object, m_Info);
72 if( ASDCP_SUCCESS(result) )
73 result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object);
75 if( ASDCP_SUCCESS(result) )
77 SourcePackage* SP = (SourcePackage*)Object;
78 memcpy(Info.AssetUUID, SP->PackageUID.Data() + 16, UUIDlen);
81 // optional CryptographicContext
82 if( ASDCP_SUCCESS(result) )
84 Result_t cr_result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CryptographicContext), &Object);
86 if( ASDCP_SUCCESS(cr_result) )
87 MD_to_CryptoInfo((CryptographicContext*)Object, m_Info);
94 // standard method of opening an MXF file for read
96 ASDCP::h__Reader::OpenMXFRead(const char* filename)
98 Result_t result = m_File.OpenRead(filename);
100 if ( ASDCP_SUCCESS(result) )
101 result = m_HeaderPart.InitFromFile(m_File);
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();
107 if ( test_s < 2 || test_s > 3 )
109 DefaultLogSink().Error("RIP count is not 2 or 3: %lu\n", test_s);
110 return RESULT_FORMAT;
113 // it really OP-Atom?
114 // MDObject* OpPattern = GetMDObjectByType("OperationalPattern");
115 // TODO: check the label
117 // if this is a three partition file, go to the body
118 // partition and read off the partition pack
121 DefaultLogSink().Error("RIP count is 3: must write code...\n");
122 return RESULT_FORMAT;
124 // TODO: check the partition pack to make sure it is
125 // really a body with a single essence container
127 m_EssenceStart = m_File.Tell();
133 // standard method of populating the in-memory index
135 ASDCP::h__Reader::InitMXFIndex()
137 if ( ! m_File.IsOpen() )
140 Result_t result = m_File.Seek(m_HeaderPart.FooterPartition);
142 if ( ASDCP_SUCCESS(result) )
144 m_FooterPart.m_Lookup = &m_HeaderPart.m_Primer;
145 result = m_FooterPart.InitFromFile(m_File);
152 // standard method of reading a plaintext or encrypted frame
154 ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
155 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
157 // look up frame index node
158 IndexTableSegment::IndexEntry TmpEntry;
160 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
162 DefaultLogSink().Error("Frame value out of range: %lu\n", FrameNum);
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;
170 Result_t result = m_File.Seek(FilePosition);
172 if ( ASDCP_SUCCESS(result) )
173 result = Reader.ReadKLFromFile(m_File);
175 if ( ASDCP_FAILURE(result) )
178 UL Key(Reader.Key());
179 UL InteropRef(CryptEssenceUL_Data);
180 UL SMPTERef(CryptEssenceUL_Data);
181 ui64_t PacketLength = Reader.Length();
183 if ( Key == InteropRef || Key == SMPTERef )
185 if ( ! m_Info.EncryptedEssence )
187 DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
188 return RESULT_FORMAT;
191 // read encrypted triplet value into internal buffer
192 m_CtFrameBuf.Capacity(PacketLength);
194 result = m_File.Read(m_CtFrameBuf.Data(), PacketLength, &read_count);
196 if ( ASDCP_FAILURE(result) )
199 if ( read_count != PacketLength )
201 DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
202 return RESULT_FORMAT;
205 m_CtFrameBuf.Size(PacketLength);
207 // should be const but mxflib::ReadBER is not
208 byte_t* ess_p = m_CtFrameBuf.Data();
210 // read context ID length
211 if ( ! read_test_BER(&ess_p, UUIDlen) )
212 return RESULT_FORMAT;
214 // test the context ID
215 if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 )
217 DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
218 return RESULT_FORMAT;
222 // read PlaintextOffset length
223 if ( ! read_test_BER(&ess_p, sizeof(ui64_t)) )
224 return RESULT_FORMAT;
226 ui32_t PlaintextOffset = (ui32_t)ASDCP_i64_BE(cp2i<ui64_t>(ess_p));
227 ess_p += sizeof(ui64_t);
229 // read essence UL length
230 if ( ! read_test_BER(&ess_p, klv_key_size) )
231 return RESULT_FORMAT;
233 // TODO: test essence UL
234 ess_p += klv_key_size;
236 // read SourceLength length
237 if ( ! read_test_BER(&ess_p, sizeof(ui64_t)) )
238 return RESULT_FORMAT;
240 ui32_t SourceLength = (ui32_t)ASDCP_i64_BE(cp2i<ui64_t>(ess_p));
241 ess_p += sizeof(ui64_t);
242 assert(SourceLength);
244 if ( FrameBuf.Capacity() < SourceLength )
246 DefaultLogSink().Error("FrameBuf.Capacity: %lu SourceLength: %lu\n", FrameBuf.Capacity(), SourceLength);
247 return RESULT_SMALLBUF;
250 ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
253 if ( ! read_test_BER(&ess_p, esv_length) )
255 DefaultLogSink().Error("read_test_BER did not return %lu\n", esv_length);
256 return RESULT_FORMAT;
259 ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0);
261 if ( PacketLength < tmp_len )
263 DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
264 return RESULT_FORMAT;
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);
277 result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
278 FrameBuf.FrameNumber(FrameNum);
280 // detect and test integrity pack
281 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC )
283 IntegrityPack IntPack;
284 result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, FrameNum + 1, HMAC);
287 else // return ciphertext to caller
289 if ( FrameBuf.Capacity() < tmp_len )
290 return RESULT_SMALLBUF;
292 memcpy(FrameBuf.Data(), ess_p, tmp_len);
293 FrameBuf.Size(tmp_len);
294 FrameBuf.SourceLength(SourceLength);
295 FrameBuf.PlaintextOffset(PlaintextOffset);
298 else if ( Key == EssenceUL )
299 { // read plaintext frame
300 if ( FrameBuf.Capacity() < PacketLength )
302 char intbuf[IntBufferLen];
303 DefaultLogSink().Error("FrameBuf.Capacity: %lu FrameLength: %s\n",
304 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
305 return RESULT_SMALLBUF;
308 // read the data into the supplied buffer
310 result = m_File.Read(FrameBuf.Data(), PacketLength, &read_count);
312 if ( ASDCP_FAILURE(result) )
315 if ( read_count != PacketLength )
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) );
323 return RESULT_READFAIL;
326 FrameBuf.FrameNumber(FrameNum);
327 FrameBuf.Size(read_count);
331 DefaultLogSink().Error("Unexpected UL found.\n");
332 return RESULT_FORMAT;