wheee!
[asdcplib.git] / src / AS_DCP.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    AS_DCP.cpp
28     \version $Id$       
29     \brief   AS-DCP library, misc classes and subroutines
30 */
31
32 #include <AS_DCP_system.h>
33 #include "hex_utils.h"
34 #include <assert.h>
35
36
37 static const ui32_t s_MessageCount = 27;
38 static const char* s_ErrorMessages[] =
39 {
40   "An undefined error was detected.",
41   "An unexpected NULL pointer was given.",
42   "An unexpected empty string was given.",
43   "The given frame buffer is too small.",
44   "The object is not yet initialized.",
45
46   "The requested file does not exist on the system.",
47   "Insufficient privilege exists to perform the operation.",
48   "File open error.",
49   "The file contains errors or is not OP-Atom/AS-DCP.",
50   "An invalid file location was requested.",
51
52   "File read error.",
53   "File write error.",
54   "Unknown raw essence file type.",
55   "Raw essence format invalid.",
56   "Object state error.",
57
58   "Attempt to read past end of file.",
59   "Invalid configuration option detected.",
60   "Frame number out of range.",
61   "AESEncContext required when writing to encrypted file",
62   "Plaintext offset exceeds frame buffer size",
63
64   "Error allocating memory",
65   "Cannot resize externally allocated memory",
66   "The check value did not decrypt correctly",
67   "HMAC authentication failure",
68   "HMAC context required",
69
70   "Error initializing block cipher context",
71   "Attempted to write an empty frame buffer"
72 };
73
74
75 //------------------------------------------------------------------------------------------
76
77 //
78 class StderrLogSink : public ASDCP::ILogSink
79 {
80 public:
81   bool show_info;
82   bool show_debug;
83
84   StderrLogSink() : show_info(false), show_debug(false) {}
85   ~StderrLogSink() {}
86
87   void Error(const char* fmt, ...) {
88     va_list args;
89     va_start(args, fmt);
90     vLogf(LOG_ERROR, fmt, &args);
91     va_end(args);
92   }
93
94   void Warn(const char* fmt, ...) {
95     va_list args;
96     va_start(args, fmt);
97     vLogf(LOG_WARN, fmt, &args);
98     va_end(args);
99   }
100
101   void Info(const char* fmt, ...) {
102     va_list args;
103     va_start(args, fmt);
104     vLogf(LOG_INFO, fmt, &args);
105     va_end(args);
106   }
107
108   void Debug(const char* fmt, ...) {
109     va_list args;
110     va_start(args, fmt);
111     vLogf(LOG_DEBUG, fmt, &args);
112     va_end(args);
113   }
114
115   void Logf(ASDCP::ILogSink::LogType_t type, const char* fmt, ...) {
116     va_list args;
117     va_start(args, fmt);
118     vLogf(type, fmt, &args);
119     va_end(args);
120   }
121
122   void vLogf(ASDCP::ILogSink::LogType_t type, const char* fmt, va_list* list) {
123     FILE* stream = stderr;
124
125     switch ( type )
126       {
127       case LOG_ERROR: fputs("Error: ", stream); break;
128       case LOG_WARN:  fputs("Warning: ", stream); break;
129       case LOG_INFO:
130         if ( ! show_info ) return;
131         fputs("Info: ", stream);
132         break;
133       case LOG_DEBUG:
134         if ( ! show_debug ) return;
135         fputs("Debug: ", stream);
136         break;
137       }
138     
139     vfprintf(stream, fmt, *list);
140   }
141
142 } s_StderrLogSink;
143
144 //
145 static ASDCP::ILogSink* s_DefaultLogSink = 0;
146
147 //
148 void
149 ASDCP::SetDefaultLogSink(ILogSink* Sink)
150 {
151     s_DefaultLogSink = Sink;
152 }
153
154 // bootleg entry for debug enthusiasts
155 void
156 set_debug_mode(bool info_mode, bool debug_mode)
157 {
158   s_StderrLogSink.show_info = info_mode;
159   s_StderrLogSink.show_debug = debug_mode;
160 }
161
162 // Returns the internal default sink.
163 ASDCP::ILogSink&
164 ASDCP::DefaultLogSink()
165 {
166   if ( s_DefaultLogSink == 0 )
167     s_DefaultLogSink = &s_StderrLogSink;
168
169   return *s_DefaultLogSink;
170 }
171
172 const char*
173 ASDCP::Version()
174 {
175   static char ver[16];
176   sprintf(ver, "%lu.%lu.%lu", VERSION_MAJOR, VERSION_APIMINOR, VERSION_IMPMINOR);
177   return ver;
178 }
179
180
181 // Returns a pointer to an English language string describing the given result code.
182 // If the result code is not a valid member of the Result_t enum, the string
183 // "**UNKNOWN**" will be returned.
184 const char*
185 ASDCP::GetResultString(Result_t result)
186 {
187   if ( result >= 0 )
188     return "No error.";
189
190   ui32_t idx = (- result);
191
192   if ( idx > s_MessageCount )
193     return "**UNKNOWN**";
194
195   return s_ErrorMessages[--idx];
196 }
197
198
199 // convert utf-8 hext string to bin
200 i32_t
201 ASDCP::hex2bin(const char* str, byte_t* buf, ui32_t buf_len, ui32_t* conv_size)
202 {
203   ASDCP_TEST_NULL(str);
204   ASDCP_TEST_NULL(buf);
205   ASDCP_TEST_NULL(conv_size);
206
207   *conv_size = 0;
208
209   if ( str[0] == 0 ) // nothing to convert
210     return 0;
211
212   for ( int j = 0; str[j]; j++ )
213     {
214       if ( isxdigit(str[j]) )
215         (*conv_size)++;
216     }
217
218   if ( *conv_size & 0x01 ) (*conv_size)++;
219   *conv_size /= 2;
220
221   if ( *conv_size > buf_len )// maximum possible data size
222     return -1;
223
224   *conv_size = 0;
225
226   int phase = 0; // track high/low nybble
227
228   // for each character, fill in the high nybble then the low
229   for ( int i = 0; str[i]; i++ )
230     {
231       if ( ! isxdigit(str[i]) )
232         continue;
233
234       byte_t val = str[i] - ( isdigit(str[i]) ? 0x30 : ( isupper(str[i]) ? 0x37 : 0x57 ) );
235
236       if ( phase == 0 )
237         {
238           buf[*conv_size] = val << 4;
239           phase++;
240         }
241       else
242         {
243           buf[*conv_size] |= val;
244           phase = 0;
245           (*conv_size)++;
246         }
247     }
248
249   return 0;
250 }
251
252
253 // convert a memory region to a NULL-terminated hexadecimal string
254 //
255 const char*
256 ASDCP::bin2hex(const byte_t* bin_buf, ui32_t bin_len, char* str_buf, ui32_t str_len)
257 {
258   if ( bin_buf == 0
259        || str_buf == 0
260        || ((bin_len * 2) + 1) > str_len )
261     return 0;
262
263   char* p = str_buf;
264
265   for ( ui32_t i = 0; i < bin_len; i++ )
266     {
267       *p = (bin_buf[i] >> 4) & 0x0f;
268       *p += *p < 10 ? 0x30 : 0x61 - 10;
269       p++;
270
271       *p = bin_buf[i] & 0x0f;
272       *p += *p < 10 ? 0x30 : 0x61 - 10;
273       p++;
274     }
275
276   *p = '\0';
277   return str_buf;
278 }
279
280
281 // spew a range of bin data as hex
282 void
283 ASDCP::hexdump(const byte_t* buf, ui32_t dump_len, FILE* stream)
284 {
285   if ( buf == 0 )
286     return;
287
288   if ( stream == 0 )
289     stream = stderr;
290
291   static ui32_t row_len = 16;
292   const byte_t* p = buf;
293   const byte_t* end_p = p + dump_len;
294
295   for ( ui32_t line = 0; p < end_p; line++ )
296     {
297       fprintf(stream, "  %06x: ", line);
298       ui32_t i;
299       const byte_t* pp;
300
301       for ( pp = p, i = 0; i < row_len && pp < end_p; i++, pp++ )
302         fprintf(stream, "%02x ", *pp);
303
304       while ( i++ < row_len )
305         fputs("   ", stream);
306
307       for ( pp = p, i = 0; i < row_len && pp < end_p; i++, pp++ )
308         fputc((isprint(*pp) ? *pp : '.'), stream);
309
310       fputc('\n', stream);
311       p += row_len;
312     }
313 }
314
315 //------------------------------------------------------------------------------------------
316
317 // read a ber value from the buffer and compare with test value.
318 // Advances buffer to first character after BER value.
319 //
320 bool
321 ASDCP::read_test_BER(byte_t **buf, ui64_t test_value)
322 {
323   if ( buf == 0 )
324     return false;
325
326   if ( ( **buf & 0x80 ) == 0 )
327     return false;
328
329   ui64_t val = 0;
330   ui8_t ber_size = ( **buf & 0x0f ) + 1;
331
332   if ( ber_size > 9 )
333     return false;
334
335   for ( ui8_t i = 1; i < ber_size; i++ )
336     {
337       if ( (*buf)[i] > 0 )
338         val |= (ui64_t)((*buf)[i]) << ( ( ( ber_size - 1 ) - i ) * 8 );
339     }
340
341   *buf += ber_size;
342   return ( val == test_value );
343 }
344
345
346 //
347 bool
348 ASDCP::read_BER(const byte_t* buf, ui64_t* val)
349 {
350   ui8_t ber_size, i;
351   
352   if ( buf == 0 || val == 0 )
353     return false;
354
355   if ( ( *buf & 0x80 ) == 0 )
356     return false;
357
358   *val = 0;
359   ber_size = ( *buf & 0x0f ) + 1;
360
361   if ( ber_size > 9 )
362     return false;
363
364   for ( i = 1; i < ber_size; i++ )
365     {
366       if ( buf[i] > 0 )
367         *val |= (ui64_t)buf[i] << ( ( ( ber_size - 1 ) - i ) * 8 );
368     }
369
370   return true;
371 }
372
373
374 static const ui64_t ber_masks[9] =
375   { ui64_C(0xffffffffffffffff), ui64_C(0xffffffffffffff00), 
376     ui64_C(0xffffffffffff0000), ui64_C(0xffffffffff000000),
377     ui64_C(0xffffffff00000000), ui64_C(0xffffff0000000000),
378     ui64_C(0xffff000000000000), ui64_C(0xff00000000000000),
379     0
380   };
381
382
383 //
384 bool
385 ASDCP::write_BER(byte_t* buf, ui64_t val, ui32_t ber_len)
386 {
387   if ( buf == 0 )
388     return false;
389
390   if ( ber_len == 0 )
391     { // calculate default length
392       if ( val < 0x01000000L )
393         ber_len = 4;
394       else if ( val < ui64_C(0x0100000000000000) )
395         ber_len = 8;
396       else
397         ber_len = 9;
398     }
399   else
400     { // sanity check BER length
401       if ( ber_len > 9 )
402         {
403           DefaultLogSink().Error("BER size %lu exceeds maximum size of 9\n", ber_len);
404           return false;
405         }
406       
407       if ( val & ber_masks[ber_len - 1] )
408         {
409           char intbuf[IntBufferLen];
410           DefaultLogSink().Error("BER size %lu too small for value %s\n",
411                                  ber_len, ui64sz(val, intbuf));
412           return false;
413         }
414     }
415
416   buf[0] = 0x80 + ( ber_len - 1 );
417
418   for ( ui32_t i = ber_len - 1; i > 0; i-- )
419     {
420       buf[i] = (ui8_t)(val & 0xff);
421       val >>= 8;
422     }
423
424   return true;
425 }
426
427 //------------------------------------------------------------------------------------------
428 //
429 // frame buffer base class implementation
430
431 ASDCP::FrameBuffer::FrameBuffer() :
432   m_Data(0), m_Capacity(0), m_OwnMem(false), m_Size(0),
433   m_FrameNumber(0), m_SourceLength(0), m_PlaintextOffset(0)
434 {
435 }
436
437 ASDCP::FrameBuffer::~FrameBuffer()
438 {
439   if ( m_OwnMem && m_Data != 0 )
440     free(m_Data);
441 }
442
443 // Instructs the object to use an externally allocated buffer. The external
444 // buffer will not be cleaned up by the frame buffer when it is destroyed.
445 // Call with (0,0) to revert to internally allocated buffer.
446 // Returns error if the buf_addr argument is NULL and either buf_size is
447 // non-zero or internally allocated memory is in use.
448 ASDCP::Result_t
449 ASDCP::FrameBuffer::SetData(byte_t* buf_addr, ui32_t buf_size)
450 {
451   // if buf_addr is null and we have an external memory reference,
452   // drop the reference and place the object in the initialized-
453   // but-no-buffer-allocated state
454   if ( buf_addr == 0 )
455     {
456       if ( buf_size > 0 || m_OwnMem )
457         return RESULT_PTR;
458
459       m_OwnMem = false;
460       m_Capacity = m_Size = 0;
461       m_Data = 0;
462       return RESULT_OK;
463     }
464
465   if ( m_OwnMem && m_Data != 0 )
466     free(m_Data);
467
468   m_OwnMem = false;
469   m_Capacity = buf_size;
470   m_Data = buf_addr;
471   m_Size = 0;
472
473   return RESULT_OK;
474 }
475
476 // Sets the size of the internally allocate buffer. Returns RESULT_CAPEXTMEM
477 // if the object is using an externally allocated buffer via SetData();
478 // Resets content size to zero.
479 ASDCP::Result_t
480 ASDCP::FrameBuffer::Capacity(ui32_t cap_size)
481 {
482   if ( ! m_OwnMem && m_Data != 0 )
483     return RESULT_CAPEXTMEM; // cannot resize external memory
484
485   if ( m_Capacity < cap_size )
486     {
487       if ( m_Data != 0 )
488         {
489           assert(m_OwnMem);
490           free(m_Data);
491         }
492
493       m_Data = (byte_t*)malloc(cap_size);
494
495       if ( m_Data == 0 )
496         return RESULT_ALLOC;
497
498       m_Capacity = cap_size;
499       m_OwnMem = true;
500       m_Size = 0;
501     }
502
503   return RESULT_OK;
504 }
505
506
507 //
508 // end AS_DCP.cpp
509 //