]> git.sesse.net Git - vlc/blob - modules/demux/mp4/drms.c
* src/libvlc.h, src/input/input.c:
[vlc] / modules / demux / mp4 / drms.c
1 /*****************************************************************************
2  * drms.c: DRMS
3  *****************************************************************************
4  * Copyright (C) 2004 VideoLAN
5  * $Id: drms.c,v 1.13 2004/02/17 13:13:31 gbazin Exp $
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8  *          Sam Hocevar <sam@zoy.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 #include <stdlib.h>                                      /* malloc(), free() */
26
27 #ifdef WIN32
28 #   include <io.h>
29 #else
30 #   include <stdio.h>
31 #endif
32
33 #include <vlc/vlc.h>
34
35 #ifdef HAVE_ERRNO_H
36 #   include <errno.h>
37 #endif
38
39 #ifdef WIN32
40 #   include <tchar.h>
41 #   include <shlobj.h>
42 #   include <windows.h>
43 #endif
44
45 #ifdef HAVE_SYS_STAT_H
46 #   include <sys/stat.h>
47 #endif
48 #ifdef HAVE_SYS_TYPES_H
49 #   include <sys/types.h>
50 #endif
51
52 /* In Solaris (and perhaps others) PATH_MAX is in limits.h. */
53 #ifdef HAVE_LIMITS_H
54 #   include <limits.h>
55 #endif
56
57 #ifdef SYS_DARWIN
58 #   include <mach/mach.h>
59 #   include <IOKit/IOKitLib.h>
60 #   include <CoreFoundation/CFNumber.h>
61 #endif
62
63 #ifdef HAVE_SYSFS_LIBSYSFS_H
64 #   include <sysfs/libsysfs.h>
65 #endif
66
67 #include "drms.h"
68 #include "drmstables.h"
69
70 #include "libmp4.h"
71
72 /*****************************************************************************
73  * aes_s: AES keys structure
74  *****************************************************************************
75  * This structure stores a set of keys usable for encryption and decryption
76  * with the AES/Rijndael algorithm.
77  *****************************************************************************/
78 struct aes_s
79 {
80     uint32_t pp_enc_keys[ AES_KEY_COUNT + 1 ][ 4 ];
81     uint32_t pp_dec_keys[ AES_KEY_COUNT + 1 ][ 4 ];
82 };
83
84 /*****************************************************************************
85  * md5_s: MD5 message structure
86  *****************************************************************************
87  * This structure stores the static information needed to compute an MD5
88  * hash. It has an extra data buffer to allow non-aligned writes.
89  *****************************************************************************/
90 struct md5_s
91 {
92     uint64_t i_bits;      /* Total written bits */
93     uint32_t p_digest[4]; /* The MD5 digest */
94     uint32_t p_data[16];  /* Buffer to cache non-aligned writes */
95 };
96
97 /*****************************************************************************
98  * shuffle_s: shuffle structure
99  *****************************************************************************
100  * This structure stores the static information needed to shuffle data using
101  * a custom algorithm.
102  *****************************************************************************/
103 struct shuffle_s
104 {
105     uint32_t p_commands[ 20 ];
106     uint32_t p_bordel[ 16 ];
107 };
108
109 /*****************************************************************************
110  * drms_s: DRMS structure
111  *****************************************************************************
112  * This structure stores the static information needed to decrypt DRMS data.
113  *****************************************************************************/
114 struct drms_s
115 {
116     uint32_t i_user;
117     uint32_t i_key;
118     uint8_t  p_iviv[ 16 ];
119     uint8_t *p_name;
120
121     uint32_t p_key[ 4 ];
122     struct aes_s aes;
123
124     char     psz_homedir[ PATH_MAX ];
125 };
126
127 /*****************************************************************************
128  * Local prototypes
129  *****************************************************************************/
130 static void InitAES       ( struct aes_s *, uint32_t * );
131 static void DecryptAES    ( struct aes_s *, uint32_t *, const uint32_t * );
132
133 static void InitMD5       ( struct md5_s * );
134 static void AddMD5        ( struct md5_s *, const uint8_t *, uint32_t );
135 static void EndMD5        ( struct md5_s * );
136 static void Digest        ( struct md5_s *, uint32_t * );
137
138 static void InitShuffle   ( struct shuffle_s *, uint32_t * );
139 static void DoShuffle     ( struct shuffle_s *, uint32_t *, uint32_t );
140
141 static int GetSystemKey   ( uint32_t *, vlc_bool_t );
142 static int WriteUserKey   ( void *, uint32_t * );
143 static int ReadUserKey    ( void *, uint32_t * );
144 static int GetUserKey     ( void *, uint32_t * );
145
146 static int GetSCIData     ( char *, uint32_t **, uint32_t * );
147 static int HashSystemInfo ( uint32_t * );
148 static int GetiPodID      ( int64_t * );
149
150 #ifdef WORDS_BIGENDIAN
151 /*****************************************************************************
152  * Reverse: reverse byte order
153  *****************************************************************************/
154 static inline void Reverse( uint32_t *p_buffer, int n )
155 {
156     int i;
157
158     for( i = 0; i < n; i++ )
159     {
160         p_buffer[ i ] = GetDWLE(&p_buffer[ i ]);
161     }
162 }
163 #    define REVERSE( p, n ) Reverse( p, n )
164 #else
165 #    define REVERSE( p, n )
166 #endif
167
168 /*****************************************************************************
169  * BlockXOR: XOR two 128 bit blocks
170  *****************************************************************************/
171 static inline void BlockXOR( uint32_t *p_dest, uint32_t *p_s1, uint32_t *p_s2 )
172 {
173     int i;
174
175     for( i = 0; i < 4; i++ )
176     {
177         p_dest[ i ] = p_s1[ i ] ^ p_s2[ i ];
178     }
179 }
180
181 /*****************************************************************************
182  * drms_alloc: allocate a DRMS structure
183  *****************************************************************************/
184 void *drms_alloc( char *psz_homedir )
185 {
186     struct drms_s *p_drms;
187
188     p_drms = malloc( sizeof(struct drms_s) );
189
190     if( p_drms == NULL )
191     {
192         return NULL;
193     }
194
195     memset( p_drms, 0, sizeof(struct drms_s) );
196
197     strncpy( p_drms->psz_homedir, psz_homedir, PATH_MAX );
198     p_drms->psz_homedir[ PATH_MAX - 1 ] = '\0';
199
200     return (void *)p_drms;
201 }
202
203 /*****************************************************************************
204  * drms_free: free a previously allocated DRMS structure
205  *****************************************************************************/
206 void drms_free( void *_p_drms )
207 {
208     struct drms_s *p_drms = (struct drms_s *)_p_drms;
209
210     if( p_drms->p_name != NULL )
211     {
212         free( (void *)p_drms->p_name );
213     }
214
215     free( p_drms );
216 }
217
218 /*****************************************************************************
219  * drms_decrypt: unscramble a chunk of data
220  *****************************************************************************/
221 void drms_decrypt( void *_p_drms, uint32_t *p_buffer, uint32_t i_bytes )
222 {
223     struct drms_s *p_drms = (struct drms_s *)_p_drms;
224     uint32_t p_key[ 4 ];
225     unsigned int i_blocks;
226
227     /* AES is a block cypher, round down the byte count */
228     i_blocks = i_bytes / 16;
229     i_bytes = i_blocks * 16;
230
231     /* Initialise the key */
232     memcpy( p_key, p_drms->p_key, 16 );
233
234     /* Unscramble */
235     while( i_blocks-- )
236     {
237         uint32_t p_tmp[ 4 ];
238
239         REVERSE( p_buffer, 4 );
240         DecryptAES( &p_drms->aes, p_tmp, p_buffer );
241         BlockXOR( p_tmp, p_key, p_tmp );
242
243         /* Use the previous scrambled data as the key for next block */
244         memcpy( p_key, p_buffer, 16 );
245
246         /* Copy unscrambled data back to the buffer */
247         memcpy( p_buffer, p_tmp, 16 );
248         REVERSE( p_buffer, 4 );
249
250         p_buffer += 4;
251     }
252 }
253
254 /*****************************************************************************
255  * drms_init: initialise a DRMS structure
256  *****************************************************************************/
257 int drms_init( void *_p_drms, uint32_t i_type,
258                uint8_t *p_info, uint32_t i_len )
259 {
260     struct drms_s *p_drms = (struct drms_s *)_p_drms;
261     int i_ret = 0;
262
263     switch( i_type )
264     {
265         case FOURCC_user:
266             if( i_len < sizeof(p_drms->i_user) )
267             {
268                 i_ret = -1;
269                 break;
270             }
271
272             p_drms->i_user = U32_AT( p_info );
273             break;
274
275         case FOURCC_key:
276             if( i_len < sizeof(p_drms->i_key) )
277             {
278                 i_ret = -1;
279                 break;
280             }
281
282             p_drms->i_key = U32_AT( p_info );
283             break;
284
285         case FOURCC_iviv:
286             if( i_len < sizeof(p_drms->p_key) )
287             {
288                 i_ret = -1;
289                 break;
290             }
291
292             memcpy( p_drms->p_iviv, p_info, 16 );
293             break;
294
295         case FOURCC_name:
296             p_drms->p_name = strdup( p_info );
297
298             if( p_drms->p_name == NULL )
299             {
300                 i_ret = -1;
301             }
302             break;
303
304         case FOURCC_priv:
305         {
306             uint32_t p_priv[ 64 ];
307             struct md5_s md5;
308
309             if( i_len < 64 )
310             {
311                 i_ret = -1;
312                 break;
313             }
314
315             InitMD5( &md5 );
316             AddMD5( &md5, p_drms->p_name, strlen( p_drms->p_name ) );
317             AddMD5( &md5, p_drms->p_iviv, 16 );
318             EndMD5( &md5 );
319
320             if( GetUserKey( p_drms, p_drms->p_key ) )
321             {
322                 i_ret = -1;
323                 break;
324             }
325
326             InitAES( &p_drms->aes, p_drms->p_key );
327
328             memcpy( p_priv, p_info, 64 );
329             memcpy( p_drms->p_key, md5.p_digest, 16 );
330             drms_decrypt( p_drms, p_priv, 64 );
331             REVERSE( p_priv, 64 );
332
333             if( p_priv[ 0 ] != 0x6e757469 ) /* itun */
334             {
335                 i_ret = -1;
336                 break;
337             }
338
339             InitAES( &p_drms->aes, p_priv + 6 );
340             memcpy( p_drms->p_key, p_priv + 12, 16 );
341
342             free( (void *)p_drms->p_name );
343             p_drms->p_name = NULL;
344         }
345         break;
346     }
347
348     return i_ret;
349 }
350
351 /* The following functions are local */
352
353 /*****************************************************************************
354  * InitAES: initialise AES/Rijndael encryption/decryption tables
355  *****************************************************************************
356  * The Advanced Encryption Standard (AES) is described in RFC 3268
357  *****************************************************************************/
358 static void InitAES( struct aes_s *p_aes, uint32_t *p_key )
359 {
360     unsigned int i, t;
361     uint32_t i_key, i_seed;
362
363     memset( p_aes->pp_enc_keys[1], 0, 16 );
364     memcpy( p_aes->pp_enc_keys[0], p_key, 16 );
365
366     /* Generate the key tables */
367     i_seed = p_aes->pp_enc_keys[ 0 ][ 3 ];
368
369     for( i_key = 0; i_key < AES_KEY_COUNT; i_key++ )
370     {
371         uint32_t j;
372
373         i_seed = AES_ROR( i_seed, 8 );
374
375         j = p_aes_table[ i_key ];
376
377         j ^= p_aes_encrypt[ (i_seed >> 24) & 0xff ]
378               ^ AES_ROR( p_aes_encrypt[ (i_seed >> 16) & 0xff ], 8 )
379               ^ AES_ROR( p_aes_encrypt[ (i_seed >> 8) & 0xff ], 16 )
380               ^ AES_ROR( p_aes_encrypt[ i_seed & 0xff ], 24 );
381
382         j ^= p_aes->pp_enc_keys[ i_key ][ 0 ];
383         p_aes->pp_enc_keys[ i_key + 1 ][ 0 ] = j;
384         j ^= p_aes->pp_enc_keys[ i_key ][ 1 ];
385         p_aes->pp_enc_keys[ i_key + 1 ][ 1 ] = j;
386         j ^= p_aes->pp_enc_keys[ i_key ][ 2 ];
387         p_aes->pp_enc_keys[ i_key + 1 ][ 2 ] = j;
388         j ^= p_aes->pp_enc_keys[ i_key ][ 3 ];
389         p_aes->pp_enc_keys[ i_key + 1 ][ 3 ] = j;
390
391         i_seed = j;
392     }
393
394     memcpy( p_aes->pp_dec_keys[ 0 ],
395             p_aes->pp_enc_keys[ 0 ], 16 );
396
397     for( i = 1; i < AES_KEY_COUNT; i++ )
398     {
399         for( t = 0; t < 4; t++ )
400         {
401             uint32_t j, k, l, m, n;
402
403             j = p_aes->pp_enc_keys[ i ][ t ];
404
405             k = (((j >> 7) & 0x01010101) * 27) ^ ((j & 0xff7f7f7f) << 1);
406             l = (((k >> 7) & 0x01010101) * 27) ^ ((k & 0xff7f7f7f) << 1);
407             m = (((l >> 7) & 0x01010101) * 27) ^ ((l & 0xff7f7f7f) << 1);
408
409             j ^= m;
410
411             n = AES_ROR( l ^ j, 16 ) ^ AES_ROR( k ^ j, 8 ) ^ AES_ROR( j, 24 );
412
413             p_aes->pp_dec_keys[ i ][ t ] = k ^ l ^ m ^ n;
414         }
415     }
416 }
417
418 /*****************************************************************************
419  * DecryptAES: decrypt an AES/Rijndael 128 bit block
420  *****************************************************************************/
421 static void DecryptAES( struct aes_s *p_aes,
422                         uint32_t *p_dest, const uint32_t *p_src )
423 {
424     uint32_t p_wtxt[ 4 ]; /* Working cyphertext */
425     uint32_t p_tmp[ 4 ];
426     unsigned int i_round, t;
427
428     for( t = 0; t < 4; t++ )
429     {
430         /* FIXME: are there any endianness issues here? */
431         p_wtxt[ t ] = p_src[ t ] ^ p_aes->pp_enc_keys[ AES_KEY_COUNT ][ t ];
432     }
433
434     /* Rounds 0 - 8 */
435     for( i_round = 0; i_round < (AES_KEY_COUNT - 1); i_round++ )
436     {
437         for( t = 0; t < 4; t++ )
438         {
439             p_tmp[ t ] = AES_XOR_ROR( p_aes_itable, p_wtxt );
440         }
441
442         for( t = 0; t < 4; t++ )
443         {
444             p_wtxt[ t ] = p_tmp[ t ]
445                     ^ p_aes->pp_dec_keys[ (AES_KEY_COUNT - 1) - i_round ][ t ];
446         }
447     }
448
449     /* Final round (9) */
450     for( t = 0; t < 4; t++ )
451     {
452         p_dest[ t ] = AES_XOR_ROR( p_aes_decrypt, p_wtxt );
453         p_dest[ t ] ^= p_aes->pp_dec_keys[ 0 ][ t ];
454     }
455 }
456
457 /*****************************************************************************
458  * InitMD5: initialise an MD5 message
459  *****************************************************************************
460  * The MD5 message-digest algorithm is described in RFC 1321
461  *****************************************************************************/
462 static void InitMD5( struct md5_s *p_md5 )
463 {
464     p_md5->p_digest[ 0 ] = 0x67452301;
465     p_md5->p_digest[ 1 ] = 0xefcdab89;
466     p_md5->p_digest[ 2 ] = 0x98badcfe;
467     p_md5->p_digest[ 3 ] = 0x10325476;
468
469     memset( p_md5->p_data, 0, 64 );
470     p_md5->i_bits = 0;
471 }
472
473 /*****************************************************************************
474  * AddMD5: add i_len bytes to an MD5 message
475  *****************************************************************************/
476 static void AddMD5( struct md5_s *p_md5, const uint8_t *p_src, uint32_t i_len )
477 {
478     unsigned int i_current; /* Current bytes in the spare buffer */
479     unsigned int i_offset = 0;
480
481     i_current = (p_md5->i_bits / 8) & 63;
482
483     p_md5->i_bits += 8 * i_len;
484
485     /* If we can complete our spare buffer to 64 bytes, do it and add the
486      * resulting buffer to the MD5 message */
487     if( i_len >= (64 - i_current) )
488     {
489         memcpy( ((uint8_t *)p_md5->p_data) + i_current, p_src,
490                 (64 - i_current) );
491         Digest( p_md5, p_md5->p_data );
492
493         i_offset += (64 - i_current);
494         i_len -= (64 - i_current);
495         i_current = 0;
496     }
497
498     /* Add as many entire 64 bytes blocks as we can to the MD5 message */
499     while( i_len >= 64 )
500     {
501         uint32_t p_tmp[ 16 ];
502         memcpy( p_tmp, p_src + i_offset, 64 );
503         Digest( p_md5, p_tmp );
504         i_offset += 64;
505         i_len -= 64;
506     }
507
508     /* Copy our remaining data to the message's spare buffer */
509     memcpy( ((uint8_t *)p_md5->p_data) + i_current, p_src + i_offset, i_len );
510 }
511
512 /*****************************************************************************
513  * EndMD5: finish an MD5 message
514  *****************************************************************************
515  * This function adds adequate padding to the end of the message, and appends
516  * the bit count so that we end at a block boundary.
517  *****************************************************************************/
518 static void EndMD5( struct md5_s *p_md5 )
519 {
520     unsigned int i_current;
521
522     i_current = (p_md5->i_bits / 8) & 63;
523
524     /* Append 0x80 to our buffer. No boundary check because the temporary
525      * buffer cannot be full, otherwise AddMD5 would have emptied it. */
526     ((uint8_t *)p_md5->p_data)[ i_current++ ] = 0x80;
527
528     /* If less than 8 bytes are available at the end of the block, complete
529      * this 64 bytes block with zeros and add it to the message. We'll add
530      * our length at the end of the next block. */
531     if( i_current > 56 )
532     {
533         memset( ((uint8_t *)p_md5->p_data) + i_current, 0, (64 - i_current) );
534         Digest( p_md5, p_md5->p_data );
535         i_current = 0;
536     }
537
538     /* Fill the unused space in our last block with zeroes and put the
539      * message length at the end. */
540     memset( ((uint8_t *)p_md5->p_data) + i_current, 0, (56 - i_current) );
541     p_md5->p_data[ 14 ] = p_md5->i_bits & 0xffffffff;
542     p_md5->p_data[ 15 ] = (p_md5->i_bits >> 32);
543     REVERSE( &p_md5->p_data[ 14 ], 2 );
544
545     Digest( p_md5, p_md5->p_data );
546 }
547
548 #define F1( x, y, z ) ((z) ^ ((x) & ((y) ^ (z))))
549 #define F2( x, y, z ) F1((z), (x), (y))
550 #define F3( x, y, z ) ((x) ^ (y) ^ (z))
551 #define F4( x, y, z ) ((y) ^ ((x) | ~(z)))
552
553 #define MD5_DO( f, w, x, y, z, data, s ) \
554     ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
555
556 /*****************************************************************************
557  * Digest: update the MD5 digest with 64 bytes of data
558  *****************************************************************************/
559 static void Digest( struct md5_s *p_md5, uint32_t *p_input )
560 {
561     uint32_t a, b, c, d;
562
563     REVERSE( p_input, 16 );
564
565     a = p_md5->p_digest[ 0 ];
566     b = p_md5->p_digest[ 1 ];
567     c = p_md5->p_digest[ 2 ];
568     d = p_md5->p_digest[ 3 ];
569
570     MD5_DO( F1, a, b, c, d, p_input[  0 ] + 0xd76aa478,  7 );
571     MD5_DO( F1, d, a, b, c, p_input[  1 ] + 0xe8c7b756, 12 );
572     MD5_DO( F1, c, d, a, b, p_input[  2 ] + 0x242070db, 17 );
573     MD5_DO( F1, b, c, d, a, p_input[  3 ] + 0xc1bdceee, 22 );
574     MD5_DO( F1, a, b, c, d, p_input[  4 ] + 0xf57c0faf,  7 );
575     MD5_DO( F1, d, a, b, c, p_input[  5 ] + 0x4787c62a, 12 );
576     MD5_DO( F1, c, d, a, b, p_input[  6 ] + 0xa8304613, 17 );
577     MD5_DO( F1, b, c, d, a, p_input[  7 ] + 0xfd469501, 22 );
578     MD5_DO( F1, a, b, c, d, p_input[  8 ] + 0x698098d8,  7 );
579     MD5_DO( F1, d, a, b, c, p_input[  9 ] + 0x8b44f7af, 12 );
580     MD5_DO( F1, c, d, a, b, p_input[ 10 ] + 0xffff5bb1, 17 );
581     MD5_DO( F1, b, c, d, a, p_input[ 11 ] + 0x895cd7be, 22 );
582     MD5_DO( F1, a, b, c, d, p_input[ 12 ] + 0x6b901122,  7 );
583     MD5_DO( F1, d, a, b, c, p_input[ 13 ] + 0xfd987193, 12 );
584     MD5_DO( F1, c, d, a, b, p_input[ 14 ] + 0xa679438e, 17 );
585     MD5_DO( F1, b, c, d, a, p_input[ 15 ] + 0x49b40821, 22 );
586
587     MD5_DO( F2, a, b, c, d, p_input[  1 ] + 0xf61e2562,  5 );
588     MD5_DO( F2, d, a, b, c, p_input[  6 ] + 0xc040b340,  9 );
589     MD5_DO( F2, c, d, a, b, p_input[ 11 ] + 0x265e5a51, 14 );
590     MD5_DO( F2, b, c, d, a, p_input[  0 ] + 0xe9b6c7aa, 20 );
591     MD5_DO( F2, a, b, c, d, p_input[  5 ] + 0xd62f105d,  5 );
592     MD5_DO( F2, d, a, b, c, p_input[ 10 ] + 0x02441453,  9 );
593     MD5_DO( F2, c, d, a, b, p_input[ 15 ] + 0xd8a1e681, 14 );
594     MD5_DO( F2, b, c, d, a, p_input[  4 ] + 0xe7d3fbc8, 20 );
595     MD5_DO( F2, a, b, c, d, p_input[  9 ] + 0x21e1cde6,  5 );
596     MD5_DO( F2, d, a, b, c, p_input[ 14 ] + 0xc33707d6,  9 );
597     MD5_DO( F2, c, d, a, b, p_input[  3 ] + 0xf4d50d87, 14 );
598     MD5_DO( F2, b, c, d, a, p_input[  8 ] + 0x455a14ed, 20 );
599     MD5_DO( F2, a, b, c, d, p_input[ 13 ] + 0xa9e3e905,  5 );
600     MD5_DO( F2, d, a, b, c, p_input[  2 ] + 0xfcefa3f8,  9 );
601     MD5_DO( F2, c, d, a, b, p_input[  7 ] + 0x676f02d9, 14 );
602     MD5_DO( F2, b, c, d, a, p_input[ 12 ] + 0x8d2a4c8a, 20 );
603
604     MD5_DO( F3, a, b, c, d, p_input[  5 ] + 0xfffa3942,  4 );
605     MD5_DO( F3, d, a, b, c, p_input[  8 ] + 0x8771f681, 11 );
606     MD5_DO( F3, c, d, a, b, p_input[ 11 ] + 0x6d9d6122, 16 );
607     MD5_DO( F3, b, c, d, a, p_input[ 14 ] + 0xfde5380c, 23 );
608     MD5_DO( F3, a, b, c, d, p_input[  1 ] + 0xa4beea44,  4 );
609     MD5_DO( F3, d, a, b, c, p_input[  4 ] + 0x4bdecfa9, 11 );
610     MD5_DO( F3, c, d, a, b, p_input[  7 ] + 0xf6bb4b60, 16 );
611     MD5_DO( F3, b, c, d, a, p_input[ 10 ] + 0xbebfbc70, 23 );
612     MD5_DO( F3, a, b, c, d, p_input[ 13 ] + 0x289b7ec6,  4 );
613     MD5_DO( F3, d, a, b, c, p_input[  0 ] + 0xeaa127fa, 11 );
614     MD5_DO( F3, c, d, a, b, p_input[  3 ] + 0xd4ef3085, 16 );
615     MD5_DO( F3, b, c, d, a, p_input[  6 ] + 0x04881d05, 23 );
616     MD5_DO( F3, a, b, c, d, p_input[  9 ] + 0xd9d4d039,  4 );
617     MD5_DO( F3, d, a, b, c, p_input[ 12 ] + 0xe6db99e5, 11 );
618     MD5_DO( F3, c, d, a, b, p_input[ 15 ] + 0x1fa27cf8, 16 );
619     MD5_DO( F3, b, c, d, a, p_input[  2 ] + 0xc4ac5665, 23 );
620
621     MD5_DO( F4, a, b, c, d, p_input[  0 ] + 0xf4292244,  6 );
622     MD5_DO( F4, d, a, b, c, p_input[  7 ] + 0x432aff97, 10 );
623     MD5_DO( F4, c, d, a, b, p_input[ 14 ] + 0xab9423a7, 15 );
624     MD5_DO( F4, b, c, d, a, p_input[  5 ] + 0xfc93a039, 21 );
625     MD5_DO( F4, a, b, c, d, p_input[ 12 ] + 0x655b59c3,  6 );
626     MD5_DO( F4, d, a, b, c, p_input[  3 ] + 0x8f0ccc92, 10 );
627     MD5_DO( F4, c, d, a, b, p_input[ 10 ] + 0xffeff47d, 15 );
628     MD5_DO( F4, b, c, d, a, p_input[  1 ] + 0x85845dd1, 21 );
629     MD5_DO( F4, a, b, c, d, p_input[  8 ] + 0x6fa87e4f,  6 );
630     MD5_DO( F4, d, a, b, c, p_input[ 15 ] + 0xfe2ce6e0, 10 );
631     MD5_DO( F4, c, d, a, b, p_input[  6 ] + 0xa3014314, 15 );
632     MD5_DO( F4, b, c, d, a, p_input[ 13 ] + 0x4e0811a1, 21 );
633     MD5_DO( F4, a, b, c, d, p_input[  4 ] + 0xf7537e82,  6 );
634     MD5_DO( F4, d, a, b, c, p_input[ 11 ] + 0xbd3af235, 10 );
635     MD5_DO( F4, c, d, a, b, p_input[  2 ] + 0x2ad7d2bb, 15 );
636     MD5_DO( F4, b, c, d, a, p_input[  9 ] + 0xeb86d391, 21 );
637
638     p_md5->p_digest[ 0 ] += a;
639     p_md5->p_digest[ 1 ] += b;
640     p_md5->p_digest[ 2 ] += c;
641     p_md5->p_digest[ 3 ] += d;
642 }
643
644 /*****************************************************************************
645  * InitShuffle: initialise a shuffle structure
646  *****************************************************************************
647  * This function initialises tables in the p_shuffle structure that will be
648  * used later by DoShuffle. The only external parameter is p_sys_key.
649  *****************************************************************************/
650 static void InitShuffle( struct shuffle_s *p_shuffle, uint32_t *p_sys_key )
651 {
652     char p_secret1[] = "Tv!*";
653     static char const p_secret2[] = "v8rhvsaAvOKMFfUH%798=[;."
654                                     "f8677680a634ba87fnOIf)(*";
655     unsigned int i;
656
657     /* Fill p_commands using the key and a secret seed */
658     for( i = 0; i < 20; i++ )
659     {
660         struct md5_s md5;
661         int32_t i_hash;
662
663         InitMD5( &md5 );
664         AddMD5( &md5, (uint8_t *)p_sys_key, 16 );
665         AddMD5( &md5, (uint8_t *)p_secret1, 4 );
666         EndMD5( &md5 );
667
668         p_secret1[ 3 ]++;
669
670         REVERSE( md5.p_digest, 1 );
671         i_hash = ((int32_t)U32_AT(md5.p_digest)) % 1024;
672
673         p_shuffle->p_commands[ i ] = i_hash < 0 ? i_hash * -1 : i_hash;
674     }
675
676     /* Fill p_bordel with completely meaningless initial values. */
677     for( i = 0; i < 4; i++ )
678     {
679         p_shuffle->p_bordel[ 4 * i ] = U32_AT(p_sys_key + i);
680         memcpy( p_shuffle->p_bordel + 4 * i + 1, p_secret2 + 12 * i, 12 );
681         REVERSE( p_shuffle->p_bordel + 4 * i + 1, 3 );
682     }
683 }
684
685 /*****************************************************************************
686  * DoShuffle: shuffle buffer
687  *****************************************************************************
688  * This is so ugly and uses so many MD5 checksums that it is most certainly
689  * one-way, though why it needs to be so complicated is beyond me.
690  *****************************************************************************/
691 static void DoShuffle( struct shuffle_s *p_shuffle,
692                        uint32_t *p_buffer, uint32_t i_size )
693 {
694     struct md5_s md5;
695     uint32_t p_big_bordel[ 16 ];
696     uint32_t *p_bordel = p_shuffle->p_bordel;
697     unsigned int i;
698
699     /* Using the MD5 hash of a memory block is probably not one-way enough
700      * for the iTunes people. This function randomises p_bordel depending on
701      * the values in p_commands to make things even more messy in p_bordel. */
702     for( i = 0; i < 20; i++ )
703     {
704         uint8_t i_command, i_index;
705
706         if( !p_shuffle->p_commands[ i ] )
707         {
708             continue;
709         }
710
711         i_command = (p_shuffle->p_commands[ i ] & 0x300) >> 8;
712         i_index = p_shuffle->p_commands[ i ] & 0xff;
713
714         switch( i_command )
715         {
716         case 0x3:
717             p_bordel[ i_index & 0xf ] = p_bordel[ i_index >> 4 ]
718                                       + p_bordel[ ((i_index + 0x10) >> 4) & 0xf ];
719             break;
720         case 0x2:
721             p_bordel[ i_index >> 4 ] ^= p_shuffle_xor[ 0xff - i_index ];
722             break;
723         case 0x1:
724             p_bordel[ i_index >> 4 ] -= p_shuffle_sub[ 0xff - i_index ];
725             break;
726         default:
727             p_bordel[ i_index >> 4 ] += p_shuffle_add[ 0xff - i_index ];
728             break;
729         }
730     }
731
732     /* Convert our newly randomised p_bordel to big endianness and take
733      * its MD5 hash. */
734     InitMD5( &md5 );
735     for( i = 0; i < 16; i++ )
736     {
737         p_big_bordel[ i ] = U32_AT(p_bordel + i);
738     }
739     AddMD5( &md5, (uint8_t *)p_big_bordel, 64 );
740     EndMD5( &md5 );
741
742     /* XOR our buffer with the computed checksum */
743     for( i = 0; i < i_size; i++ )
744     {
745         p_buffer[ i ] ^= md5.p_digest[ i ];
746     }
747 }
748
749 /*****************************************************************************
750  * GetSystemKey: get the system key
751  *****************************************************************************
752  * Compute the system key from various system information, see HashSystemInfo.
753  *****************************************************************************/
754 static int GetSystemKey( uint32_t *p_sys_key, vlc_bool_t b_ipod )
755 {
756     static char const p_secret1[ 8 ] = "YuaFlafu";
757     static char const p_secret2[ 8 ] = "zPif98ga";
758     struct md5_s md5;
759     int64_t i_ipod_id;
760     uint32_t p_system_hash[ 4 ];
761
762     /* Compute the MD5 hash of our system info */
763     if( ( !b_ipod && HashSystemInfo( p_system_hash ) ) ||
764         (  b_ipod && GetiPodID( &i_ipod_id ) ) )
765     {
766         return -1;
767     }
768
769     /* Combine our system info hash with additional secret data. The resulting
770      * MD5 hash will be our system key. */
771     InitMD5( &md5 );
772     AddMD5( &md5, p_secret1, 8 );
773
774     if( !b_ipod )
775     {
776         AddMD5( &md5, (uint8_t *)p_system_hash, 6 );
777         AddMD5( &md5, (uint8_t *)p_system_hash, 6 );
778         AddMD5( &md5, (uint8_t *)p_system_hash, 6 );
779         AddMD5( &md5, p_secret2, 8 );
780     }
781     else
782     {
783         i_ipod_id = U64_AT(&i_ipod_id);
784         AddMD5( &md5, (uint8_t *)&i_ipod_id, sizeof(i_ipod_id) );
785         AddMD5( &md5, (uint8_t *)&i_ipod_id, sizeof(i_ipod_id) );
786         AddMD5( &md5, (uint8_t *)&i_ipod_id, sizeof(i_ipod_id) );
787     }
788
789     EndMD5( &md5 );
790
791     memcpy( p_sys_key, md5.p_digest, 16 );
792
793     return 0;
794 }
795
796 #ifdef WIN32
797 #   define DRMS_DIRNAME "drms"
798 #else
799 #   define DRMS_DIRNAME ".drms"
800 #endif
801
802 /*****************************************************************************
803  * WriteUserKey: write the user key to hard disk
804  *****************************************************************************
805  * Write the user key to the hard disk so that it can be reused later or used
806  * on operating systems other than Win32.
807  *****************************************************************************/
808 static int WriteUserKey( void *_p_drms, uint32_t *p_user_key )
809 {
810     struct drms_s *p_drms = (struct drms_s *)_p_drms;
811     FILE *file;
812     int i_ret = -1;
813     char psz_path[ PATH_MAX ];
814
815     snprintf( psz_path, PATH_MAX - 1,
816               "%s/" DRMS_DIRNAME, p_drms->psz_homedir );
817
818 #if defined( HAVE_ERRNO_H )
819 #   if defined( WIN32 )
820     if( !mkdir( psz_path ) || errno == EEXIST )
821 #   else
822     if( !mkdir( psz_path, 0755 ) || errno == EEXIST )
823 #   endif
824 #else
825     if( !mkdir( psz_path ) )
826 #endif
827     {
828         snprintf( psz_path, PATH_MAX - 1, "%s/" DRMS_DIRNAME "/%08X.%03d",
829                   p_drms->psz_homedir, p_drms->i_user, p_drms->i_key );
830
831         file = fopen( psz_path, "w" );
832         if( file != NULL )
833         {
834             i_ret = fwrite( p_user_key, sizeof(uint32_t),
835                             4, file ) == 4 ? 0 : -1;
836             fclose( file );
837         }
838     }
839
840     return i_ret;
841 }
842
843 /*****************************************************************************
844  * ReadUserKey: read the user key from hard disk
845  *****************************************************************************
846  * Retrieve the user key from the hard disk if available.
847  *****************************************************************************/
848 static int ReadUserKey( void *_p_drms, uint32_t *p_user_key )
849 {
850     struct drms_s *p_drms = (struct drms_s *)_p_drms;
851     FILE *file;
852     int i_ret = -1;
853     char psz_path[ PATH_MAX ];
854
855     snprintf( psz_path, PATH_MAX - 1,
856               "%s/" DRMS_DIRNAME "/%08X.%03d", p_drms->psz_homedir,
857               p_drms->i_user, p_drms->i_key );
858
859     file = fopen( psz_path, "r" );
860     if( file != NULL )
861     {
862         i_ret = fread( p_user_key, sizeof(uint32_t),
863                        4, file ) == 4 ? 0 : -1;
864         fclose( file );
865     }
866
867     return i_ret;
868 }
869
870 /*****************************************************************************
871  * GetUserKey: get the user key
872  *****************************************************************************
873  * Retrieve the user key from the hard disk if available, otherwise generate
874  * it from the system key. If the key could be successfully generated, write
875  * it to the hard disk for future use.
876  *****************************************************************************/
877 static int GetUserKey( void *_p_drms, uint32_t *p_user_key )
878 {
879     static char const p_secret[] = "mUfnpognadfgf873";
880     struct drms_s *p_drms = (struct drms_s *)_p_drms;
881     struct aes_s aes;
882     struct shuffle_s shuffle;
883     uint32_t i, y;
884     uint32_t *p_sci_data;
885     uint32_t i_user, i_key;
886     uint32_t p_sys_key[ 4 ];
887     uint32_t i_sci_size, i_blocks, i_remaining;
888     uint32_t *p_sci0, *p_sci1, *p_buffer;
889     uint32_t p_sci_key[ 4 ];
890     char *psz_ipod;
891     int i_ret = -1;
892
893     if( !ReadUserKey( p_drms, p_user_key ) )
894     {
895         REVERSE( p_user_key, 4 );
896         return 0;
897     }
898
899     psz_ipod = getenv( "IPOD" );
900
901     if( GetSystemKey( p_sys_key, psz_ipod ? VLC_TRUE : VLC_FALSE ) )
902     {
903         return -1;
904     }
905
906     if( GetSCIData( psz_ipod, &p_sci_data, &i_sci_size ) )
907     {
908         return -1;
909     }
910
911     /* Phase 1: unscramble the SCI data using the system key and shuffle
912      *          it using DoShuffle(). */
913
914     /* Skip the first 4 bytes (some sort of header). Decrypt the rest. */
915     i_blocks = (i_sci_size - 4) / 16;
916     i_remaining = (i_sci_size - 4) - (i_blocks * 16);
917     p_buffer = p_sci_data + 1;
918
919     /* Decrypt and shuffle our data at the same time */
920     InitAES( &aes, p_sys_key );
921     REVERSE( p_sys_key, 4 );
922     InitShuffle( &shuffle, p_sys_key );
923
924     memcpy( p_sci_key, p_secret, 16 );
925     REVERSE( p_sci_key, 4 );
926
927     while( i_blocks-- )
928     {
929         uint32_t p_tmp[ 4 ];
930
931         REVERSE( p_buffer, 4 );
932         DecryptAES( &aes, p_tmp, p_buffer );
933         BlockXOR( p_tmp, p_sci_key, p_tmp );
934
935         /* Use the previous scrambled data as the key for next block */
936         memcpy( p_sci_key, p_buffer, 16 );
937
938         /* Shuffle the decrypted data using a custom routine */
939         DoShuffle( &shuffle, p_tmp, 4 );
940
941         /* Copy this block back to p_buffer */
942         memcpy( p_buffer, p_tmp, 16 );
943
944         p_buffer += 4;
945     }
946
947     if( i_remaining >= 4 )
948     {
949         i_remaining /= 4;
950         REVERSE( p_buffer, i_remaining );
951         DoShuffle( &shuffle, p_buffer, i_remaining );
952     }
953
954     /* Phase 2: look for the user key in the generated data. I must admit I
955      *          do not understand what is going on here, because it almost
956      *          looks like we are browsing data that makes sense, even though
957      *          the DoShuffle() part made it completely meaningless. */
958
959     y = 0;
960     REVERSE( p_sci_data + 5, 1 );
961     i = U32_AT( p_sci_data + 5 );
962     i_sci_size -= 22 * sizeof(uint32_t);
963     p_sci1 = p_sci_data + 22;
964     p_sci0 = NULL;
965
966     while( i_sci_size >= 20 && i > 0 )
967     {
968         if( p_sci0 == NULL )
969         {
970             i_sci_size -= 18 * sizeof(uint32_t);
971             if( i_sci_size < 20 )
972             {
973                 break;
974             }
975
976             p_sci0 = p_sci1;
977             REVERSE( p_sci1 + 17, 1 );
978             y = U32_AT( p_sci1 + 17 );
979             p_sci1 += 18;
980         }
981
982         if( !y )
983         {
984             i--;
985             p_sci0 = NULL;
986             continue;
987         }
988
989         i_user = U32_AT( p_sci0 );
990         i_key = U32_AT( p_sci1 );
991         REVERSE( &i_user, 1 );
992         REVERSE( &i_key, 1 );
993         if( i_user == p_drms->i_user && ( ( i_key == p_drms->i_key ) ||
994             ( !p_drms->i_key && ( p_sci1 == (p_sci0 + 18) ) ) ) )
995         {
996             memcpy( p_user_key, p_sci1 + 1, 16 );
997             REVERSE( p_sci1 + 1, 4 );
998             WriteUserKey( p_drms, p_sci1 + 1 );
999             i_ret = 0;
1000             break;
1001         }
1002
1003         y--;
1004         p_sci1 += 5;
1005         i_sci_size -= 5 * sizeof(uint32_t);
1006     }
1007
1008     free( p_sci_data );
1009
1010     return i_ret;
1011 }
1012
1013 /*****************************************************************************
1014  * GetSCIData: get SCI data from "SC Info.sidb"
1015  *****************************************************************************
1016  * Read SCI data from "\Apple Computer\iTunes\SC Info\SC Info.sidb"
1017  *****************************************************************************/
1018 static int GetSCIData( char *psz_ipod, uint32_t **pp_sci,
1019                        uint32_t *pi_sci_size )
1020 {
1021     FILE *file;
1022     char *psz_path = NULL;
1023     char p_tmp[ PATH_MAX ];
1024     int i_ret = -1;
1025
1026     if( psz_ipod == NULL )
1027     {
1028 #ifdef WIN32
1029         char *p_filename = "\\Apple Computer\\iTunes\\SC Info\\SC Info.sidb";
1030         typedef HRESULT (WINAPI *SHGETFOLDERPATH)( HWND, int, HANDLE, DWORD,
1031                                                    LPSTR );
1032         HINSTANCE shfolder_dll = NULL;
1033         SHGETFOLDERPATH dSHGetFolderPath = NULL;
1034
1035         if( ( shfolder_dll = LoadLibrary( _T("SHFolder.dll") ) ) != NULL )
1036         {
1037             dSHGetFolderPath =
1038                 (SHGETFOLDERPATH)GetProcAddress( shfolder_dll,
1039                                                  _T("SHGetFolderPathA") );
1040         }
1041
1042         if( dSHGetFolderPath != NULL &&
1043             SUCCEEDED( dSHGetFolderPath( NULL, CSIDL_COMMON_APPDATA,
1044                                          NULL, 0, p_tmp ) ) )
1045         {
1046             strncat( p_tmp, p_filename, min( strlen( p_filename ),
1047                      (sizeof(p_tmp)/sizeof(p_tmp[0]) - 1) -
1048                      strlen( p_tmp ) ) );
1049             psz_path = p_tmp;
1050         }
1051
1052         if( shfolder_dll != NULL )
1053         {
1054             FreeLibrary( shfolder_dll );
1055         }
1056 #endif
1057     }
1058     else
1059     {
1060 #define ISCINFO "iSCInfo"
1061         if( strstr( psz_ipod, ISCINFO ) == NULL )
1062         {
1063             snprintf( p_tmp, sizeof(p_tmp)/sizeof(p_tmp[0]) - 1,
1064                       "%s/iPod_Control/iTunes/" ISCINFO, psz_ipod );
1065             psz_path = p_tmp;
1066         }
1067         else
1068         {
1069             psz_path = psz_ipod;
1070         }
1071     }
1072
1073     if( psz_path == NULL )
1074     {
1075         return -1;
1076     }
1077
1078     file = fopen( psz_path, "r" );
1079     if( file != NULL )
1080     {
1081         struct stat st;
1082
1083         if( !fstat( fileno( file ), &st ) )
1084         {
1085             *pp_sci = malloc( st.st_size );
1086             if( *pp_sci != NULL )
1087             {
1088                 if( fread( *pp_sci, 1, st.st_size,
1089                            file ) == (size_t)st.st_size )
1090                 {
1091                     *pi_sci_size = st.st_size;
1092                     i_ret = 0;
1093                 }
1094                 else
1095                 {
1096                     free( (void *)*pp_sci );
1097                     *pp_sci = NULL;
1098                 }
1099             }
1100         }
1101
1102         fclose( file );
1103     }
1104
1105     return i_ret;
1106 }
1107
1108 /*****************************************************************************
1109  * HashSystemInfo: hash system information
1110  *****************************************************************************
1111  * This function computes the MD5 hash of the C: hard drive serial number,
1112  * BIOS version, CPU type and Windows version.
1113  *****************************************************************************/
1114 static int HashSystemInfo( uint32_t *p_system_hash )
1115 {
1116     struct md5_s md5;
1117     int i_ret = 0;
1118
1119 #ifdef WIN32
1120     HKEY i_key;
1121     unsigned int i;
1122     DWORD i_size;
1123     DWORD i_serial;
1124     LPBYTE p_reg_buf;
1125
1126     static LPCTSTR p_reg_keys[ 3 ][ 2 ] =
1127     {
1128         {
1129             _T("HARDWARE\\DESCRIPTION\\System"),
1130             _T("SystemBiosVersion")
1131         },
1132
1133         {
1134             _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"),
1135             _T("ProcessorNameString")
1136         },
1137
1138         {
1139             _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion"),
1140             _T("ProductId")
1141         }
1142     };
1143
1144     InitMD5( &md5 );
1145
1146     AddMD5( &md5, "cache-control", 13 );
1147     AddMD5( &md5, "Ethernet", 8 );
1148
1149     GetVolumeInformation( _T("C:\\"), NULL, 0, &i_serial,
1150                           NULL, NULL, NULL, 0 );
1151     AddMD5( &md5, (uint8_t *)&i_serial, 4 );
1152
1153     for( i = 0; i < sizeof(p_reg_keys) / sizeof(p_reg_keys[ 0 ]); i++ )
1154     {
1155         if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, p_reg_keys[ i ][ 0 ],
1156                           0, KEY_READ, &i_key ) != ERROR_SUCCESS )
1157         {
1158             continue;
1159         }
1160
1161         if( RegQueryValueEx( i_key, p_reg_keys[ i ][ 1 ],
1162                              NULL, NULL, NULL, &i_size ) != ERROR_SUCCESS )
1163         {
1164             RegCloseKey( i_key );
1165             continue;
1166         }
1167
1168         p_reg_buf = malloc( i_size );
1169
1170         if( p_reg_buf != NULL )
1171         {
1172             if( RegQueryValueEx( i_key, p_reg_keys[ i ][ 1 ],
1173                                  NULL, NULL, p_reg_buf,
1174                                  &i_size ) == ERROR_SUCCESS )
1175             {
1176                 AddMD5( &md5, (uint8_t *)p_reg_buf, i_size );
1177             }
1178
1179             free( p_reg_buf );
1180         }
1181
1182         RegCloseKey( i_key );
1183     }
1184
1185 #else
1186     InitMD5( &md5 );
1187     i_ret = -1;
1188 #endif
1189
1190     EndMD5( &md5 );
1191     memcpy( p_system_hash, md5.p_digest, 16 );
1192
1193     return i_ret;
1194 }
1195
1196 /*****************************************************************************
1197  * GetiPodID: Get iPod ID
1198  *****************************************************************************
1199  * This function gets the iPod ID.
1200  *****************************************************************************/
1201 static int GetiPodID( int64_t *p_ipod_id )
1202 {
1203     int i_ret = -1;
1204
1205 #define PROD_NAME   "iPod"
1206 #define VENDOR_NAME "Apple Computer, Inc."
1207
1208     char *psz_ipod_id = getenv( "IPODID" );
1209     if( psz_ipod_id != NULL )
1210     {
1211         *p_ipod_id = strtoll( psz_ipod_id, NULL, 16 );
1212         return 0;
1213     }
1214
1215 #ifdef SYS_DARWIN
1216     CFTypeRef value;
1217     mach_port_t port;
1218     io_object_t device;
1219     io_iterator_t iterator;
1220     CFMutableDictionaryRef matching_dic;
1221
1222     if( IOMasterPort( MACH_PORT_NULL, &port ) == KERN_SUCCESS )
1223     {
1224         if( ( matching_dic = IOServiceMatching( "IOFireWireUnit" ) ) != NULL )
1225         {
1226             CFDictionarySetValue( matching_dic,
1227                                   CFSTR("FireWire Vendor Name"),
1228                                   CFSTR(VENDOR_NAME) );
1229             CFDictionarySetValue( matching_dic,
1230                                   CFSTR("FireWire Product Name"),
1231                                   CFSTR(PROD_NAME) );
1232
1233             if( IOServiceGetMatchingServices( port, matching_dic,
1234                                               &iterator ) == KERN_SUCCESS )
1235             {
1236                 while( ( device = IOIteratorNext( iterator ) ) != NULL )
1237                 {
1238                     value = IORegistryEntryCreateCFProperty( device,
1239                         CFSTR("GUID"), kCFAllocatorDefault, kNilOptions );
1240
1241                     if( value != NULL )
1242                     {
1243                         if( CFGetTypeID( value ) == CFNumberGetTypeID() )
1244                         {
1245                             int64_t i_ipod_id;
1246                             CFNumberGetValue( (CFNumberRef)value,
1247                                               kCFNumberLongLongType,
1248                                               &i_ipod_id );
1249                             *p_ipod_id = i_ipod_id;
1250                             i_ret = 0;
1251                         }
1252
1253                         CFRelease( value );
1254                     }
1255
1256                     IOObjectRelease( device );
1257
1258                     if( !i_ret ) break;
1259                 }
1260
1261                 IOObjectRelease( iterator );
1262             }
1263         }
1264
1265         mach_port_deallocate( mach_task_self(), port );
1266     }
1267
1268 #elif HAVE_SYSFS_LIBSYSFS_H
1269     struct sysfs_bus *bus = NULL;
1270     struct dlist *devlist = NULL;
1271     struct dlist *attributes = NULL;
1272     struct sysfs_device *curdev = NULL;
1273     struct sysfs_attribute *curattr = NULL;
1274
1275     bus = sysfs_open_bus( "ieee1394" );
1276     if( bus != NULL )
1277     {
1278         devlist = sysfs_get_bus_devices( bus );
1279         if( devlist != NULL )
1280         {
1281             dlist_for_each_data( devlist, curdev, struct sysfs_device )
1282             {
1283                 attributes = sysfs_get_device_attributes( curdev );
1284                 if( attributes != NULL )
1285                 {
1286                     dlist_for_each_data( attributes, curattr,
1287                                          struct sysfs_attribute )
1288                     {
1289                         if( ( strcmp( curattr->name, "model_name" ) == 0 ) &&
1290                             ( strncmp( curattr->value, PROD_NAME,
1291                                        sizeof(PROD_NAME) ) == 0 ) )
1292                         {
1293                             *p_ipod_id = strtoll( curdev->name, NULL, 16 );
1294                             i_ret = 0;
1295                             break;
1296                         }
1297                     }
1298                 }
1299
1300                 if( !i_ret ) break;
1301             }
1302         }
1303
1304         sysfs_close_bus( bus );
1305     }
1306 #endif
1307
1308     return i_ret;
1309 }
1310