dvd_css.c 29.8 KB
Newer Older
1 2 3 4
/*****************************************************************************
 * dvd_css.c: Functions for DVD authentification and unscrambling
 *****************************************************************************
 * Copyright (C) 1999-2001 VideoLAN
Sam Hocevar's avatar
 
Sam Hocevar committed
5
 * $Id: dvd_css.c,v 1.18 2001/03/03 11:01:07 sam Exp $
6 7 8
 *
 * Author: Stphane Borel <stef@via.ecp.fr>
 *
9 10 11
 * based on:
 *  - css-auth by Derek Fawcus <derek@spider.com>
 *  - DVD CSS ioctls example program by Andrew T. Veliath <andrewtv@usa.net>
Sam Hocevar's avatar
 
Sam Hocevar committed
12
 *  - The Divide and conquer attack by Frank A. Stevenson <frank@funcom.com>
13 14 15 16
 *  - DeCSSPlus by Ethan Hawke
 *  - DecVOB
 *  see http://www.lemuria.org/DeCSS/ by Tom Vogt for more information.
 * 
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
35 36
#include "defs.h"

37
#include <stdio.h>
38
#include <stdlib.h>
39 40 41 42
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <netinet/in.h>
43
#ifdef HAVE_SYS_IOCTL_H
44 45
# include <sys/ioctl.h>
#endif
46 47 48 49 50 51
#ifdef HAVE_SYS_DVDIO_H
# include <sys/dvdio.h>
#endif
#ifdef LINUX_DVD
# include <linux/cdrom.h>
#endif
52 53

#include "common.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
54

55
#include "intf_msg.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
56

57
#include "dvd_css.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
58 59 60
#ifdef HAVE_CSS
#include "dvd_csstables.h"
#endif /* HAVE_CSS */
Sam Hocevar's avatar
 
Sam Hocevar committed
61
#include "dvd_ioctl.h"
62
#include "dvd_ifo.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
63

64
#include "input_dvd.h"
65 66

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
67
 * Local prototypes
68
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
69 70 71 72 73 74 75 76
#ifdef HAVE_CSS
static int  CSSGetASF    ( int i_fd );
static void CSSCryptKey  ( int i_key_type, int i_varient,
                           u8 const * pi_challenge, u8* pi_key );
static int  CSSCracker   ( int i_start, unsigned char * p_crypted,
                           unsigned char * p_decrypted,
                           dvd_key_t * p_sector_key, dvd_key_t * p_key );
#endif /* HAVE_CSS */
77 78

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
79
 * CSSTest : check if the disc is encrypted or not
80
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
81
int CSSTest( int i_fd )
82
{
Sam Hocevar's avatar
 
Sam Hocevar committed
83
    dvd_struct dvd;
84

Sam Hocevar's avatar
 
Sam Hocevar committed
85 86
    dvd.type = DVD_STRUCT_COPYRIGHT;
    dvd.copyright.layer_num = 0;
87

Sam Hocevar's avatar
 
Sam Hocevar committed
88
    if( dvd_ioctl( i_fd, DVD_READ_STRUCT, &dvd ) < 0 )
89
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
90 91
        intf_ErrMsg( "css error: DVD ioctl failed" );
        return -1;
92
    }
93

Sam Hocevar's avatar
 
Sam Hocevar committed
94
    return dvd.copyright.cpst;
95 96 97
}

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
98 99 100 101 102
 * CSSInit : CSS Structure initialisation and DVD authentication.
 *****************************************************************************
 * It simulates the mutual authentication between logical unit and host.
 * Since we don't need the disc key to find the title key, we just run the
 * basic unavoidable commands to authenticate device and disc.
103
 *****************************************************************************/
104
css_t * CSSInit( int i_fd )
105
{
Sam Hocevar's avatar
 
Sam Hocevar committed
106 107 108 109
#ifdef HAVE_CSS
    /* structures defined in cdrom.h or dvdio.h */
    dvd_struct      dvd;
    dvd_authinfo    auth_info;
Sam Hocevar's avatar
 
Sam Hocevar committed
110
    css_t *         p_css;
111

Sam Hocevar's avatar
 
Sam Hocevar committed
112 113
    int             i_error = -1;
    int             i;
114

115 116 117 118 119 120 121
    p_css = malloc( sizeof(css_t) );
    if( p_css == NULL )
    {
        return NULL;
    }

    p_css->i_fd = i_fd;
122

Sam Hocevar's avatar
 
Sam Hocevar committed
123
    memset( &auth_info, 0, sizeof(auth_info) );
124

Sam Hocevar's avatar
 
Sam Hocevar committed
125 126
    /* Test authentication success */
    switch( CSSGetASF( i_fd ) )
127
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
128
    case -1:
129 130
        free( p_css );
        return NULL;
Sam Hocevar's avatar
 
Sam Hocevar committed
131
    case 1:
132
        return p_css;
Sam Hocevar's avatar
 
Sam Hocevar committed
133 134
    case 0:
        intf_WarnMsg( 3, "css info: authenticating" );
135 136
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
137 138
    /* Init sequence, request AGID */
    for( i = 1; i < 4 ; ++i )
139
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
140 141 142 143 144
        intf_WarnMsg( 3, "css info: request AGID %d", i );
        auth_info.type = DVD_LU_SEND_AGID;
        auth_info.lsa.agid = 0;
        i_error =  dvd_ioctl( i_fd, DVD_AUTH, &auth_info );
        if( i_error != -1 )
145
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
146 147 148
            /* No error during ioctl: we know if device
             * is authenticated */
            break;
149
        }
150

Sam Hocevar's avatar
 
Sam Hocevar committed
151 152 153 154 155
        intf_ErrMsg( "css error: AGID N/A, invalidating" );
        auth_info.type = DVD_INVALIDATE_AGID;
        auth_info.lsa.agid = 0;
        dvd_ioctl( i_fd, DVD_AUTH, &auth_info );
    }
156

Sam Hocevar's avatar
 
Sam Hocevar committed
157 158
    /* Unable to authenticate without AGID */
    if( i_error == -1 )
159
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
160
        intf_ErrMsg( "css error: could not get AGID" );
161 162
        free( p_css );
        return NULL;
163
    }
164

Sam Hocevar's avatar
 
Sam Hocevar committed
165
    for( i = 0 ; i < 10; ++i )
166
    {
167
        p_css->disc.pi_challenge[i] = i;
168
    }
169

Sam Hocevar's avatar
 
Sam Hocevar committed
170 171
    /* Send AGID to host */
    auth_info.type = DVD_HOST_SEND_CHALLENGE;
172

Sam Hocevar's avatar
 
Sam Hocevar committed
173 174 175
    /* Get challenge from host */
    for( i = 0 ; i < 10 ; ++i )
    {
176
        auth_info.hsc.chal[9-i] = p_css->disc.pi_challenge[i];
177
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
178
    /* Returning data, let LU change state */
179
    p_css->i_agid = auth_info.lsa.agid;
180

Sam Hocevar's avatar
 
Sam Hocevar committed
181 182
    /* Send challenge to LU */
    if( dvd_ioctl( i_fd, DVD_AUTH, &auth_info )<0 )
183
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
184
        intf_ErrMsg( "css error: failed sending challenge to LU" );
185 186
        free( p_css );
        return NULL;
187
    }
188

Sam Hocevar's avatar
 
Sam Hocevar committed
189 190
    /* Get key1 from LU */
    if( dvd_ioctl( i_fd, DVD_AUTH, &auth_info ) < 0)
191
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
192
        intf_ErrMsg( "css error: failed getting key1 from LU" );
193 194
        free( p_css );
        return NULL;
195
    }
196

Sam Hocevar's avatar
 
Sam Hocevar committed
197 198
    /* Send key1 to host */
    for( i = 0 ; i < KEY_SIZE ; i++ )
199
    {
200
        p_css->disc.pi_key1[i] = auth_info.lsk.key[4-i];
201 202 203 204
    }

    for( i = 0 ; i < 32 ; ++i )
    {
205 206
        CSSCryptKey( 0, i, p_css->disc.pi_challenge,
                           p_css->disc.pi_key_check );
207

208 209
        if( memcmp( p_css->disc.pi_key_check,
                    p_css->disc.pi_key1, KEY_SIZE ) == 0 )
210
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
211
            intf_WarnMsg( 3, "css info: drive authentic, using variant %d", i);
212
            p_css->disc.i_varient = i;
213 214 215 216 217 218 219
            auth_info.type = DVD_LU_SEND_CHALLENGE;
            break;
        }
    }

    if( i == 32 )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
220
        intf_ErrMsg( "css error: drive would not authenticate" );
221
        auth_info.type = DVD_AUTH_FAILURE;
222 223
        free( p_css );
        return NULL;
224
    }
225 226

    /* Get challenge from LU */
Sam Hocevar's avatar
 
Sam Hocevar committed
227
    if( dvd_ioctl( i_fd, DVD_AUTH, &auth_info ) < 0 )
228
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
229
        intf_ErrMsg( "css error: failed getting challenge from LU" );
230 231
        free( p_css );
        return NULL;
232 233
    }

234
    /* Send challenge to host */
235
    for( i = 0 ; i < 10 ; ++i )
236
    {
237
        p_css->disc.pi_challenge[i] = auth_info.hsc.chal[9-i];
238 239
    }

240 241
    CSSCryptKey( 1, p_css->disc.i_varient, p_css->disc.pi_challenge,
                                                    p_css->disc.pi_key2 );
242
    auth_info.type = DVD_HOST_SEND_KEY2;
243

244
    /* Get key2 from host */
245
    for( i = 0 ; i < KEY_SIZE ; ++i )
246
    {
247
        auth_info.hsk.key[4-i] = p_css->disc.pi_key2[i];
248
    }
249
    /* Returning data, let LU change state */
250

251
    /* Send key2 to LU */
Sam Hocevar's avatar
 
Sam Hocevar committed
252
    if( dvd_ioctl( i_fd, DVD_AUTH, &auth_info ) < 0 )
253
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
254
        intf_ErrMsg( "css error: failed sending key2 to LU (expected)" );
255 256
        free( p_css );
        return NULL;
257
    }
258

259
    if( auth_info.type == DVD_AUTH_ESTABLISHED )
260
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
261
        intf_WarnMsg( 3, "css info: authentication established" );
262
    }
263
    else if( auth_info.type == DVD_AUTH_FAILURE )
264
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
265
        intf_ErrMsg( "css error: DVD authentication failed" );
266 267
        free( p_css );
        return NULL;
268 269
    }

270 271 272 273 274
    memcpy( p_css->disc.pi_challenge, p_css->disc.pi_key1, KEY_SIZE );
    memcpy( p_css->disc.pi_challenge+KEY_SIZE, p_css->disc.pi_key2, KEY_SIZE );
    CSSCryptKey( 2, p_css->disc.i_varient,
                    p_css->disc.pi_challenge,
                    p_css->disc.pi_key_check );
275

Sam Hocevar's avatar
 
Sam Hocevar committed
276
    intf_WarnMsg( 1, "css info: received Session Key" );
277

278
    if( p_css->i_agid < 0 )
279
    {
280 281
        free( p_css );
        return NULL;
282
    }
283

284 285 286 287
    /* Test authentication success */
    switch( CSSGetASF( i_fd ) )
    {
    case -1:
288 289
        free( p_css );
        return NULL;
290
    case 1:
291
        return p_css;
292
    case 0:
Sam Hocevar's avatar
 
Sam Hocevar committed
293
        intf_WarnMsg( 3, "css info: getting disc key" );
294
    }
295

296
    /* Get encrypted disc key */
297
    dvd.type = DVD_STRUCT_DISCKEY;
298
    dvd.disckey.agid = p_css->i_agid;
299
    memset( dvd.disckey.value, 0, 2048 );
300

Sam Hocevar's avatar
 
Sam Hocevar committed
301
    if( dvd_ioctl( i_fd, DVD_READ_STRUCT, &dvd ) < 0 )
302
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
303
        intf_ErrMsg( "css error: could not read Disc Key" );
304 305
        free( p_css );
        return NULL;
306
    }
307
#if 1
308 309
    /* Unencrypt disc key using bus key */
    for( i = 0 ; i < sizeof(dvd.disckey.value) ; i++ )
310
    {
311
        dvd.disckey.value[i] ^= p_css->disc.pi_key_check[4 - (i % KEY_SIZE)];
312
    }
313
    memcpy( p_css->disc.pi_key_check, dvd.disckey.value, 2048 );
314 315 316 317 318 319
#endif
    /* Test authentication success */
    switch( CSSGetASF( i_fd ) )
    {
    case -1:
    case 0:
320 321
        free( p_css );
        return NULL;
322
    case 1:
323
        return p_css;
324
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
325 326 327

    return p_css;

Sam Hocevar's avatar
 
Sam Hocevar committed
328 329 330
#else /* HAVE_CSS */
    intf_ErrMsg( "css error: CSS decryption is disabled in this module" );

Sam Hocevar's avatar
 
Sam Hocevar committed
331
    return NULL;
Sam Hocevar's avatar
 
Sam Hocevar committed
332
#endif /* HAVE_CSS */
333 334 335 336 337 338 339 340 341
}

/*****************************************************************************
 * CSSEnd : frees css structure
 *****************************************************************************/
void CSSEnd( css_t * p_css )
{
#ifdef HAVE_CSS
    free( p_css );
Sam Hocevar's avatar
 
Sam Hocevar committed
342 343 344
#else /* HAVE_CSS */
    ;
#endif /* HAVE_CSS */
345 346 347
}

/*****************************************************************************
Stéphane Borel's avatar
 
Stéphane Borel committed
348
 * CSSGetKey : get title key.
Sam Hocevar's avatar
 
Sam Hocevar committed
349
 *****************************************************************************
350
 * The DVD should have been opened and authenticated before.
351
 *****************************************************************************/
Stéphane Borel's avatar
 
Stéphane Borel committed
352
int CSSGetKey( css_t * p_css )
353
{
Sam Hocevar's avatar
 
Sam Hocevar committed
354
#ifdef HAVE_CSS
355 356 357 358
    /*
     * Title key cracking method from Ethan Hawke,
     * with Frank A. Stevenson algorithm.
     * Does not use any player key table and ioctls.
359
     */
Stéphane Borel's avatar
 
Stéphane Borel committed
360 361 362
    u8          pi_buf[0x800];
    dvd_key_t   pi_key;
    title_key_t p_title_key[10];
363 364 365
    off_t       i_pos;
    boolean_t   b_encrypted;
    boolean_t   b_stop_scanning;
366
    int         i_title;
367 368 369
    int         i_bytes_read;
    int         i_best_plen;
    int         i_best_p;
370 371 372
    int         i_registered_keys;
    int         i_total_keys_found;
    int         i_highest;
373
    int         i,j,k;
374

Stéphane Borel's avatar
 
Stéphane Borel committed
375 376 377 378 379 380 381 382 383 384 385
    memset( p_title_key, 0, 10 );
    memset( &pi_key, 0, 10 );
    b_encrypted = 0;
    b_stop_scanning = 0;
    i_registered_keys = 0 ;
    i_total_keys_found = 0 ;
    i_highest = 0;

    /* Position of the title on the disc */
    i_title = p_css->i_title;
    i_pos = p_css->i_title_pos;
386

387 388
//fprintf( stderr, "CSS %d start pos: %lld\n", i_title, i_pos );

Stéphane Borel's avatar
 
Stéphane Borel committed
389 390 391
    do {
    i_pos = lseek( p_css->i_fd, i_pos, SEEK_SET );
    i_bytes_read = read( p_css->i_fd, pi_buf, 0x800 );
392

Stéphane Borel's avatar
 
Stéphane Borel committed
393 394 395 396 397 398
    /* PES_scrambling_control */
    if( pi_buf[0x14] & 0x30 )
    {
        b_encrypted = 1;
        i_best_plen = 0;
        i_best_p = 0;
399

Stéphane Borel's avatar
 
Stéphane Borel committed
400 401 402 403
        for( i = 2 ; i < 0x30 ; i++ )
        {
            for( j = i ; ( j < 0x80 ) &&
                   ( pi_buf[0x7F - (j%i)] == pi_buf[0x7F-j] ) ; j++ );
404
            {
Stéphane Borel's avatar
 
Stéphane Borel committed
405
                if( ( j > i_best_plen ) && ( j > i ) )
406
                {
Stéphane Borel's avatar
 
Stéphane Borel committed
407 408
                    i_best_plen = j;
                    i_best_p = i;
409
                }
410
            }
Stéphane Borel's avatar
 
Stéphane Borel committed
411
        }
412

Stéphane Borel's avatar
 
Stéphane Borel committed
413 414 415 416 417 418 419
        if( ( i_best_plen > 20 ) && ( i_best_plen / i_best_p >= 2) )
        {
            i = CSSCracker( 0,  &pi_buf[0x80],
                    &pi_buf[0x80 - ( i_best_plen / i_best_p) *i_best_p],
                    (dvd_key_t*)&pi_buf[0x54],
                    &pi_key );
            while( i>=0 )
420
            {
Stéphane Borel's avatar
 
Stéphane Borel committed
421 422
                k = 0;
                for( j=0 ; j<i_registered_keys ; j++ )
423
                {
Stéphane Borel's avatar
 
Stéphane Borel committed
424 425
                    if( memcmp( &(p_title_key[j].pi_key),
                                &pi_key, sizeof(dvd_key_t) ) == 0 )
426
                    {
Stéphane Borel's avatar
 
Stéphane Borel committed
427
                        p_title_key[j].i_occ++;
428
                        i_total_keys_found++;
Stéphane Borel's avatar
 
Stéphane Borel committed
429
                        k = 1;
430
                    }
431 432
                }

Sam Hocevar's avatar
 
Sam Hocevar committed
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742
                if( k == 0 )
                {
                    memcpy( &(p_title_key[i_registered_keys].pi_key),
                                            &pi_key, sizeof(dvd_key_t) );
                    p_title_key[i_registered_keys++].i_occ = 1;
                    i_total_keys_found++;
                }
                i = CSSCracker( i, &pi_buf[0x80],
                    &pi_buf[0x80 - ( i_best_plen / i_best_p) *i_best_p],
                    (dvd_key_t*)&pi_buf[0x54], &pi_key);
            }

            /* Stop search if we find one occurance of the key 
             * I have never found a DVD for which it is not enough
             * but we should take care of that */
            if( i_registered_keys == 1 && p_title_key[0].i_occ >= 1 )
            {
                b_stop_scanning = 1;
            }
        }
    }

    i_pos += i_bytes_read;
    } while( i_bytes_read == 0x800 && !b_stop_scanning);

    if( b_stop_scanning)
    {
        intf_WarnMsg( 1,
            "css info: found enough occurencies of the same key." );
    }

    if( !b_encrypted )
    {
        intf_WarnMsg( 3, "css warning: this file was _NOT_ encrypted!" );
        return(0);
    }

    if( b_encrypted && i_registered_keys == 0 )
    {
        intf_ErrMsg( "css error: unable to determine keys from file" );
        return(1);
    }

    for( i = 0 ; i < i_registered_keys - 1 ; i++ )
    {
        for( j = i + 1 ; j < i_registered_keys ; j++ )
        {
            if( p_title_key[j].i_occ > p_title_key[i].i_occ )
            {
                memcpy( &pi_key, &(p_title_key[j].pi_key), sizeof(dvd_key_t) );
                k = p_title_key[j].i_occ;

                memcpy( &(p_title_key[j].pi_key),
                        &(p_title_key[i].pi_key), sizeof(dvd_key_t) );
                p_title_key[j].i_occ = p_title_key[i].i_occ;

                memcpy( &(p_title_key[i].pi_key),&pi_key, sizeof(dvd_key_t) );
                p_title_key[i].i_occ = k;
            }
        }
    }

#ifdef STATS
    intf_WarnMsg( 1, "css info: key(s) & key probability" );
    intf_WarnMsg( 1, "----------------------------------" );
#endif
    for( i=0 ; i<i_registered_keys ; i++ )
    {
#ifdef STATS
        intf_WarnMsg( 1, "%d) %02X %02X %02X %02X %02X - %3.2f%%", i,
                      p_title_key[i].pi_key[0], p_title_key[i].pi_key[1],
                      p_title_key[i].pi_key[2], p_title_key[i].pi_key[3],
                      p_title_key[i].pi_key[4],
                      p_title_key[i].i_occ * 100.0 / i_total_keys_found );
#endif
        if( p_title_key[i_highest].i_occ * 100.0 / i_total_keys_found
                           <= p_title_key[i].i_occ*100.0 / i_total_keys_found )
        {
            i_highest = i;
        }
    }


    /* The "find the key with the highest probability" code
     * is untested, as I haven't been able to find a VOB that
     * produces multiple keys (RT)
     */
    intf_WarnMsg( 3, "css info: title %d, key %02X %02X %02X %02X %02X",
                  i_title, p_title_key[i_highest].pi_key[0],
                           p_title_key[i_highest].pi_key[1],
                           p_title_key[i_highest].pi_key[2],
                           p_title_key[i_highest].pi_key[3],
                           p_title_key[i_highest].pi_key[4] );

    memcpy( p_css->pi_title_key,
            p_title_key[i_highest].pi_key, KEY_SIZE );

    return 0;
#else /* HAVE_CSS */
    return 1;
#endif /* HAVE_CSS */
}

/*****************************************************************************
 * CSSDescrambleSector
 *****************************************************************************
 * sec : sector to descramble
 * key : title key for this sector
 *****************************************************************************/
int CSSDescrambleSector( dvd_key_t pi_key, u8* pi_sec )
{
#ifdef HAVE_CSS
    unsigned int    i_t1, i_t2, i_t3, i_t4, i_t5, i_t6;
    u8*             pi_end = pi_sec + 0x800;

    /* PES_scrambling_control */
    if( pi_sec[0x14] & 0x30)
    {
        i_t1 = ((pi_key)[0] ^ pi_sec[0x54]) | 0x100;
        i_t2 = (pi_key)[1] ^ pi_sec[0x55];
        i_t3 = (((pi_key)[2]) | ((pi_key)[3] << 8) |
               ((pi_key)[4] << 16)) ^ ((pi_sec[0x56]) |
               (pi_sec[0x57] << 8) | (pi_sec[0x58] << 16));
        i_t4 = i_t3 & 7;
        i_t3 = i_t3 * 2 + 8 - i_t4;
        pi_sec += 0x80;
        i_t5 = 0;

        while( pi_sec != pi_end )
        {
            i_t4 = pi_css_tab2[i_t2] ^ pi_css_tab3[i_t1];
            i_t2 = i_t1>>1;
            i_t1 = ( ( i_t1 & 1 ) << 8 ) ^ i_t4;
            i_t4 = pi_css_tab5[i_t4];
            i_t6 = ((((((( i_t3 >> 3 ) ^ i_t3 ) >> 1 ) ^
                                         i_t3 ) >> 8 ) ^ i_t3 ) >> 5) & 0xff;
            i_t3 = (i_t3 << 8 ) | i_t6;
            i_t6 = pi_css_tab4[i_t6];
            i_t5 += i_t6 + i_t4;
            *pi_sec++ = pi_css_tab1[*pi_sec] ^( i_t5 & 0xff );
            i_t5 >>= 8;
        }
    }

    return 0;
#else /* HAVE_CSS */
    return 1;
#endif /* HAVE_CSS */
}

#ifdef HAVE_CSS
/*
 * Following functions are local
 */

/*****************************************************************************
 * CSSGetASF : Get Authentification success flag
 *****************************************************************************
 * Returns :
 *  -1 on ioctl error,
 *  0 if the device needs to be authenticated,
 *  1 either.
 *****************************************************************************/
static int CSSGetASF( int i_fd )
{
    dvd_authinfo    auth_info;

    auth_info.type = DVD_LU_SEND_ASF;
    auth_info.lsasf.asf = 0;

    for( auth_info.lsasf.agid = 0 ; auth_info.lsasf.agid < 4 ;
                                                    auth_info.lsasf.agid++ )
    {
        if( !( dvd_ioctl( i_fd, DVD_AUTH, &auth_info ) ) )
        {
            intf_WarnMsg( 3, "css info: %sauthenticated",
                          auth_info.lsasf.asf ? "" : "not " );
            return auth_info.lsasf.asf;
        }
    }

    /* The ioctl process has failed */
    intf_ErrMsg( "css error: GetASF fatal error" );
    return -1;
}

/*****************************************************************************
 * CSSCryptKey : shuffles bits and unencrypt keys.
 *****************************************************************************
 * Used during authentication and disc key negociation in CSSInit.
 * i_key_type : 0->key1, 1->key2, 2->buskey.
 * i_varient : between 0 and 31.
 *****************************************************************************/
static void CSSCryptKey( int i_key_type, int i_varient,
                         u8 const * pi_challenge, u8* pi_key )
{
    /* Permutation table for challenge */
    u8      ppi_perm_challenge[3][10] =
            { { 1, 3, 0, 7, 5, 2, 9, 6, 4, 8 },
              { 6, 1, 9, 3, 8, 5, 7, 4, 0, 2 },
              { 4, 0, 3, 5, 7, 2, 8, 6, 1, 9 } };

    /* Permutation table for varient table for key2 and buskey */
    u8      ppi_perm_varient[2][32] =
            { { 0x0a, 0x08, 0x0e, 0x0c, 0x0b, 0x09, 0x0f, 0x0d,
                0x1a, 0x18, 0x1e, 0x1c, 0x1b, 0x19, 0x1f, 0x1d,
                0x02, 0x00, 0x06, 0x04, 0x03, 0x01, 0x07, 0x05,
                0x12, 0x10, 0x16, 0x14, 0x13, 0x11, 0x17, 0x15 },
              { 0x12, 0x1a, 0x16, 0x1e, 0x02, 0x0a, 0x06, 0x0e,
                0x10, 0x18, 0x14, 0x1c, 0x00, 0x08, 0x04, 0x0c,
                0x13, 0x1b, 0x17, 0x1f, 0x03, 0x0b, 0x07, 0x0f,
                0x11, 0x19, 0x15, 0x1d, 0x01, 0x09, 0x05, 0x0d } };

    u8      pi_varients[32] =
            {   0xB7, 0x74, 0x85, 0xD0, 0xCC, 0xDB, 0xCA, 0x73,
                0x03, 0xFE, 0x31, 0x03, 0x52, 0xE0, 0xB7, 0x42,
                0x63, 0x16, 0xF2, 0x2A, 0x79, 0x52, 0xFF, 0x1B,
                0x7A, 0x11, 0xCA, 0x1A, 0x9B, 0x40, 0xAD, 0x01 };

    /* The "secret" key */
    u8      pi_secret[5] = { 0x55, 0xD6, 0xC4, 0xC5, 0x28 };

    u8      pi_bits[30];
    u8      pi_scratch[10];
    u8      pi_tmp1[5];
    u8      pi_tmp2[5];
    u8      i_lfsr0_o;  /* 1 bit used */
    u8      i_lfsr1_o;  /* 1 bit used */
    u32     i_lfsr0;
    u32     i_lfsr1;
    u8      i_css_varient;
    u8      i_cse;
    u8      i_index;
    u8      i_combined;
    u8      i_carry;
    u8      i_val = 0;
    int     i_term = 0;
    int     i_bit;
    int     i;

    for (i = 9; i >= 0; --i)
        pi_scratch[i] = pi_challenge[ppi_perm_challenge[i_key_type][i]];

    i_css_varient = ( i_key_type == 0 ) ? i_varient :
                    ppi_perm_varient[i_key_type-1][i_varient];

    /*
     * This encryption engine implements one of 32 variations
     * one the same theme depending upon the choice in the
     * varient parameter (0 - 31).
     *
     * The algorithm itself manipulates a 40 bit input into
     * a 40 bit output.
     * The parameter 'input' is 80 bits.  It consists of
     * the 40 bit input value that is to be encrypted followed
     * by a 40 bit seed value for the pseudo random number
     * generators.
     */

    /* Feed the secret into the input values such that
     * we alter the seed to the LFSR's used above,  then
     * generate the bits to play with.
     */
    for( i = 5 ; --i >= 0 ; )
    {
        pi_tmp1[i] = pi_scratch[5 + i] ^ pi_secret[i] ^ pi_crypt_tab2[i];
    }

    /*
     * We use two LFSR's (seeded from some of the input data bytes) to
     * generate two streams of pseudo-random bits.  These two bit streams
     * are then combined by simply adding with carry to generate a final
     * sequence of pseudo-random bits which is stored in the buffer that
     * 'output' points to the end of - len is the size of this buffer.
     *
     * The first LFSR is of degree 25,  and has a polynomial of:
     * x^13 + x^5 + x^4 + x^1 + 1
     *
     * The second LSFR is of degree 17,  and has a (primitive) polynomial of:
     * x^15 + x^1 + 1
     *
     * I don't know if these polynomials are primitive modulo 2,  and thus
     * represent maximal-period LFSR's.
     *
     *
     * Note that we take the output of each LFSR from the new shifted in
     * bit,  not the old shifted out bit.  Thus for ease of use the LFSR's
     * are implemented in bit reversed order.
     *
     */
    
    /* In order to ensure that the LFSR works we need to ensure that the
     * initial values are non-zero.  Thus when we initialise them from
     * the seed,  we ensure that a bit is set.
     */
    i_lfsr0 = ( pi_tmp1[0] << 17 ) | ( pi_tmp1[1] << 9 ) |
              (( pi_tmp1[2] & ~7 ) << 1 ) | 8 | ( pi_tmp1[2] & 7 );
    i_lfsr1 = ( pi_tmp1[3] << 9 ) | 0x100 | pi_tmp1[4];

    i_index = sizeof(pi_bits);
    i_carry = 0;

    do
    {
        for( i_bit = 0, i_val = 0 ; i_bit < 8 ; ++i_bit )
        {

            i_lfsr0_o = ( ( i_lfsr0 >> 24 ) ^ ( i_lfsr0 >> 21 ) ^
                        ( i_lfsr0 >> 20 ) ^ ( i_lfsr0 >> 12 ) ) & 1;
            i_lfsr0 = ( i_lfsr0 << 1 ) | i_lfsr0_o;
Stéphane Borel's avatar
 
Stéphane Borel committed
743

Sam Hocevar's avatar
 
Sam Hocevar committed
744 745 746 747 748 749 750
            i_lfsr1_o = ( ( i_lfsr1 >> 16 ) ^ ( i_lfsr1 >> 2 ) ) & 1;
            i_lfsr1 = ( i_lfsr1 << 1 ) | i_lfsr1_o;

            i_combined = !i_lfsr1_o + i_carry + !i_lfsr0_o;
            /* taking bit 1 */
            i_carry = ( i_combined >> 1 ) & 1;
            i_val |= ( i_combined & 1 ) << i_bit;
751
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
752 753 754
    
        pi_bits[--i_index] = i_val;
    } while( i_index > 0 );
755

Sam Hocevar's avatar
 
Sam Hocevar committed
756 757 758 759 760
    /* This term is used throughout the following to
     * select one of 32 different variations on the
     * algorithm.
     */
    i_cse = pi_varients[i_css_varient] ^ pi_crypt_tab2[i_css_varient];
761

Sam Hocevar's avatar
 
Sam Hocevar committed
762 763 764 765 766 767
    /* Now the actual blocks doing the encryption.  Each
     * of these works on 40 bits at a time and are quite
     * similar.
     */
    i_index = 0;
    for( i = 5, i_term = 0 ; --i >= 0 ; i_term = pi_scratch[i] )
Stéphane Borel's avatar
 
Stéphane Borel committed
768
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
769 770 771 772
        i_index = pi_bits[25 + i] ^ pi_scratch[i];
        i_index = pi_crypt_tab1[i_index] ^ ~pi_crypt_tab2[i_index] ^ i_cse;

        pi_tmp1[i] = pi_crypt_tab2[i_index] ^ pi_crypt_tab3[i_index] ^ i_term;
Stéphane Borel's avatar
 
Stéphane Borel committed
773
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
774
    pi_tmp1[4] ^= pi_tmp1[0];
775

Sam Hocevar's avatar
 
Sam Hocevar committed
776
    for( i = 5, i_term = 0 ; --i >= 0 ; i_term = pi_tmp1[i] )
Stéphane Borel's avatar
 
Stéphane Borel committed
777
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
778 779 780 781
        i_index = pi_bits[20 + i] ^ pi_tmp1[i];
        i_index = pi_crypt_tab1[i_index] ^ ~pi_crypt_tab2[i_index] ^ i_cse;

        pi_tmp2[i] = pi_crypt_tab2[i_index] ^ pi_crypt_tab3[i_index] ^ i_term;
Stéphane Borel's avatar
 
Stéphane Borel committed
782
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
783
    pi_tmp2[4] ^= pi_tmp2[0];
784

Sam Hocevar's avatar
 
Sam Hocevar committed
785
    for( i = 5, i_term = 0 ; --i >= 0 ; i_term = pi_tmp2[i] )
Stéphane Borel's avatar
 
Stéphane Borel committed
786
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
787 788 789 790 791
        i_index = pi_bits[15 + i] ^ pi_tmp2[i];
        i_index = pi_crypt_tab1[i_index] ^ ~pi_crypt_tab2[i_index] ^ i_cse;
        i_index = pi_crypt_tab2[i_index] ^ pi_crypt_tab3[i_index] ^ i_term;

        pi_tmp1[i] = pi_crypt_tab0[i_index] ^ pi_crypt_tab2[i_index];
Stéphane Borel's avatar
 
Stéphane Borel committed
792
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
793
    pi_tmp1[4] ^= pi_tmp1[0];
794

Sam Hocevar's avatar
 
Sam Hocevar committed
795
    for( i = 5, i_term = 0 ; --i >= 0 ; i_term = pi_tmp1[i] )
Stéphane Borel's avatar
 
Stéphane Borel committed
796
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
797 798
        i_index = pi_bits[10 + i] ^ pi_tmp1[i];
        i_index = pi_crypt_tab1[i_index] ^ ~pi_crypt_tab2[i_index] ^ i_cse;
799

Sam Hocevar's avatar
 
Sam Hocevar committed
800
        i_index = pi_crypt_tab2[i_index] ^ pi_crypt_tab3[i_index] ^ i_term;
801

Sam Hocevar's avatar
 
Sam Hocevar committed
802
        pi_tmp2[i] = pi_crypt_tab0[i_index] ^ pi_crypt_tab2[i_index];
Stéphane Borel's avatar
 
Stéphane Borel committed
803
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
804
    pi_tmp2[4] ^= pi_tmp2[0];
805

Sam Hocevar's avatar
 
Sam Hocevar committed
806
    for( i = 5, i_term = 0 ; --i >= 0 ; i_term = pi_tmp2[i] )
Stéphane Borel's avatar
 
Stéphane Borel committed
807
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
808 809
        i_index = pi_bits[5 + i] ^ pi_tmp2[i];
        i_index = pi_crypt_tab1[i_index] ^ ~pi_crypt_tab2[i_index] ^ i_cse;
810

Sam Hocevar's avatar
 
Sam Hocevar committed
811 812 813
        pi_tmp1[i] = pi_crypt_tab2[i_index] ^ pi_crypt_tab3[i_index] ^ i_term;
    }
    pi_tmp1[4] ^= pi_tmp1[0];
814

Sam Hocevar's avatar
 
Sam Hocevar committed
815 816 817 818
    for(i = 5, i_term = 0 ; --i >= 0 ; i_term = pi_tmp1[i] )
    {
        i_index = pi_bits[i] ^ pi_tmp1[i];
        i_index = pi_crypt_tab1[i_index] ^ ~pi_crypt_tab2[i_index] ^ i_cse;
Stéphane Borel's avatar
 
Stéphane Borel committed
819

Sam Hocevar's avatar
 
Sam Hocevar committed
820 821
        pi_key[i] = pi_crypt_tab2[i_index] ^ pi_crypt_tab3[i_index] ^ i_term;
    }
822

Sam Hocevar's avatar
 
Sam Hocevar committed
823
    return;
824 825 826
}

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
827 828 829
 * CSSCracker : title key decryption by cracking
 *****************************************************************************
 * This function is called by CSSGetKeys to find a key
830
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
831 832 833 834 835
static int CSSCracker( int i_start,
                       unsigned char * p_crypted,
                       unsigned char * p_decrypted,
                       dvd_key_t * p_sector_key,
                       dvd_key_t * p_key )
836
{
Sam Hocevar's avatar
 
Sam Hocevar committed
837 838 839 840 841 842
    unsigned char pi_buffer[10];
    unsigned int i_t1, i_t2, i_t3, i_t4, i_t5, i_t6;
    unsigned int i_try;
    unsigned int i_candidate;
    unsigned int i, j;
    int i_exit = -1;
843

Sam Hocevar's avatar
 
Sam Hocevar committed
844 845

    for( i = 0 ; i < 10 ; i++ )
846
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
847 848 849 850 851 852 853 854
        pi_buffer[i] = pi_css_tab1[p_crypted[i]] ^ p_decrypted[i];
    }

    for( i_try = i_start ; i_try < 0x10000 ; i_try++ )
    {
        i_t1 = i_try >> 8 | 0x100;
        i_t2 = i_try & 0xff;
        i_t3 = 0;               /* not needed */
855 856
        i_t5 = 0;

Sam Hocevar's avatar
 
Sam Hocevar committed
857 858
        /* iterate cipher 4 times to reconstruct LFSR2 */
        for( i = 0 ; i < 4 ; i++ )
859
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
860
            /* advance LFSR1 normaly */
861
            i_t4 = pi_css_tab2[i_t2] ^ pi_css_tab3[i_t1];
Sam Hocevar's avatar
 
Sam Hocevar committed
862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889
            i_t2 = i_t1 >> 1;
            i_t1 = ( ( i_t1 & 1 ) << 8 ) ^ i_t4;
            i_t4 = pi_css_tab5[i_t4];
            /* deduce i_t6 & i_t5 */
            i_t6 = pi_buffer[i];
            if( i_t5 )
            {
                i_t6 = ( i_t6 + 0xff ) & 0x0ff;
            }
            if( i_t6 < i_t4 )
            {
                i_t6 += 0x100;
            }
            i_t6 -= i_t4;
            i_t5 += i_t6 + i_t4;
            i_t6 = pi_css_tab4[ i_t6 ];
            /* feed / advance i_t3 / i_t5 */
            i_t3 = ( i_t3 << 8 ) | i_t6;
            i_t5 >>= 8;
        }

        i_candidate = i_t3;

        /* iterate 6 more times to validate candidate key */
        for( ; i < 10 ; i++ )
        {
            i_t4 = pi_css_tab2[i_t2] ^ pi_css_tab3[i_t1];
            i_t2 = i_t1 >> 1;
890 891 892
            i_t1 = ( ( i_t1 & 1 ) << 8 ) ^ i_t4;
            i_t4 = pi_css_tab5[i_t4];
            i_t6 = ((((((( i_t3 >> 3 ) ^ i_t3 ) >> 1 ) ^
Sam Hocevar's avatar
 
Sam Hocevar committed
893 894
                                         i_t3 ) >> 8 ) ^ i_t3 ) >> 5 ) & 0xff;
            i_t3 = ( i_t3 << 8 ) | i_t6;
895 896
            i_t6 = pi_css_tab4[i_t6];
            i_t5 += i_t6 + i_t4;
Sam Hocevar's avatar
 
Sam Hocevar committed
897 898 899 900 901
            if( ( i_t5 & 0xff ) != pi_buffer[i] )
            {
                break;
            }

902
            i_t5 >>= 8;
903
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950

        if( i == 10 )
        {
            /* Do 4 backwards steps of iterating t3 to deduce initial state */
            i_t3 = i_candidate;
            for( i = 0 ; i < 4 ; i++ )
            {
                i_t1 = i_t3 & 0xff;
                i_t3 = ( i_t3 >> 8 );
                /* easy to code, and fast enough bruteforce
                 * search for byte shifted in */
                for( j = 0 ; j < 256 ; j++ )
                {
                    i_t3 = ( i_t3 & 0x1ffff) | ( j << 17 );
                    i_t6 = ((((((( i_t3 >> 3 ) ^ i_t3 ) >> 1 ) ^
                                   i_t3 ) >> 8 ) ^ i_t3 ) >> 5 ) & 0xff;
                    if( i_t6 == i_t1 )
                    {
                        break;
                    }
                }
            }

            i_t4 = ( i_t3 >> 1 ) - 4;
            for( i_t5 = 0 ; i_t5 < 8; i_t5++ )
            {
                if( ( ( i_t4 + i_t5 ) * 2 + 8 - ( (i_t4 + i_t5 ) & 7 ) )
                                                                      == i_t3 )
                {
                    (*p_key)[0] = i_try>>8;
                    (*p_key)[1] = i_try & 0xFF;
                    (*p_key)[2] = ( ( i_t4 + i_t5 ) >> 0) & 0xFF;
                    (*p_key)[3] = ( ( i_t4 + i_t5 ) >> 8) & 0xFF;
                    (*p_key)[4] = ( ( i_t4 + i_t5 ) >> 16) & 0xFF;
                    i_exit = i_try + 1;
                }
            }
        }
    }

    if( i_exit >= 0 )
    {
        (*p_key)[0] ^= (*p_sector_key)[0];
        (*p_key)[1] ^= (*p_sector_key)[1];
        (*p_key)[2] ^= (*p_sector_key)[2];
        (*p_key)[3] ^= (*p_sector_key)[3];
        (*p_key)[4] ^= (*p_sector_key)[4];
951 952
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
953
    return i_exit;
954
}
Sam Hocevar's avatar
 
Sam Hocevar committed
955
#endif /* HAVE_CSS */
Sam Hocevar's avatar
 
Sam Hocevar committed
956