dvd_css.c 25.1 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.31 2001/05/31 03:12:49 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>
Sam Hocevar's avatar
 
Sam Hocevar committed
39 40

#ifdef HAVE_UNISTD_H
Sam Hocevar's avatar
 
Sam Hocevar committed
41
#   include <unistd.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
42
#elif defined( _MSC_VER ) && defined( _WIN32 )
Sam Hocevar's avatar
 
Sam Hocevar committed
43
#   include <io.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
44 45
#endif

46 47 48
#include <string.h>

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

50
#include "intf_msg.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
51

52
#include "dvd_css.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
53

Sam Hocevar's avatar
 
Sam Hocevar committed
54
#ifdef HAVE_CSS
Sam Hocevar's avatar
 
Sam Hocevar committed
55
#   include "dvd_csstables.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
56
#endif /* HAVE_CSS */
Sam Hocevar's avatar
 
Sam Hocevar committed
57

Sam Hocevar's avatar
 
Sam Hocevar committed
58 59
#include "dvd_ioctl.h"

60
#include "input_dvd.h"
61 62

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
63
 * Local prototypes
64
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
65
#ifdef HAVE_CSS
Sam Hocevar's avatar
 
Sam Hocevar committed
66
static int  CSSGetASF    ( int i_fd );
Sam Hocevar's avatar
 
Sam Hocevar committed
67 68 69 70 71 72
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 */
73 74

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
75
 * CSSTest : check if the disc is encrypted or not
76
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
77
int CSSTest( int i_fd )
78
{
Sam Hocevar's avatar
 
Sam Hocevar committed
79
    int i_ret, i_copyright;
80

Sam Hocevar's avatar
 
Sam Hocevar committed
81
    i_ret = ioctl_ReadCopyright( i_fd, 0 /* i_layer */, &i_copyright );
82

Sam Hocevar's avatar
 
Sam Hocevar committed
83
    if( i_ret < 0 )
84
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
85 86 87 88
        /* Since it's the first ioctl we try to issue, we add a notice */
        intf_ErrMsg( "css error: ioctl_ReadCopyright failed, "
                     "make sure DVD ioctls were compiled in" );

Sam Hocevar's avatar
 
Sam Hocevar committed
89
        return i_ret;
90
    }
91

Sam Hocevar's avatar
 
Sam Hocevar committed
92
    return i_copyright;
93 94 95
}

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
96 97 98 99 100
 * 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.
101
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
102
int CSSInit( int i_fd, css_t * p_css )
103
{
Sam Hocevar's avatar
 
Sam Hocevar committed
104 105
#ifdef HAVE_CSS
    /* structures defined in cdrom.h or dvdio.h */
Sam Hocevar's avatar
 
Sam Hocevar committed
106
    char p_buffer[2048 + 4 + 1];
Sam Hocevar's avatar
 
Sam Hocevar committed
107
    int  i_agid = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
108 109
    int  i_ret = -1;
    int  i;
110

Sam Hocevar's avatar
 
Sam Hocevar committed
111
    /* Test authentication success */
Sam Hocevar's avatar
 
Sam Hocevar committed
112
    switch( CSSGetASF( i_fd ) )
113
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
114 115
        case -1:
            return -1;
116

Sam Hocevar's avatar
 
Sam Hocevar committed
117
        case 1:
118
            intf_WarnMsg( 2, "css info: already authenticated" );
Sam Hocevar's avatar
 
Sam Hocevar committed
119
            return 0;
120

Sam Hocevar's avatar
 
Sam Hocevar committed
121
        case 0:
122
            intf_WarnMsg( 2, "css info: need to authenticate" );
123 124
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
125 126
    /* Init sequence, request AGID */
    for( i = 1; i < 4 ; ++i )
127
    {
128
        intf_WarnMsg( 2, "css info: requesting AGID %d", i );
Sam Hocevar's avatar
 
Sam Hocevar committed
129

Sam Hocevar's avatar
 
Sam Hocevar committed
130
        i_ret = ioctl_ReportAgid( i_fd, &i_agid );
Sam Hocevar's avatar
 
Sam Hocevar committed
131 132

        if( i_ret != -1 )
133
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
134
            /* No error during ioctl: we know the device is authenticated */
Sam Hocevar's avatar
 
Sam Hocevar committed
135
            break;
136
        }
137

Sam Hocevar's avatar
 
Sam Hocevar committed
138
        intf_ErrMsg( "css error: ioctl_ReportAgid failed, invalidating" );
Sam Hocevar's avatar
 
Sam Hocevar committed
139

Sam Hocevar's avatar
 
Sam Hocevar committed
140 141
        i_agid = 0;
        ioctl_InvalidateAgid( i_fd, &i_agid );
Sam Hocevar's avatar
 
Sam Hocevar committed
142
    }
143

Sam Hocevar's avatar
 
Sam Hocevar committed
144
    /* Unable to authenticate without AGID */
Sam Hocevar's avatar
 
Sam Hocevar committed
145
    if( i_ret == -1 )
146
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
147
        intf_ErrMsg( "css error: ioctl_ReportAgid failed, fatal" );
Sam Hocevar's avatar
 
Sam Hocevar committed
148
        return -1;
149
    }
150

Sam Hocevar's avatar
 
Sam Hocevar committed
151
    for( i = 0 ; i < 10; ++i )
152
    {
153
        p_css->disc.pi_challenge[i] = i;
154
    }
155

Sam Hocevar's avatar
 
Sam Hocevar committed
156 157 158
    /* Get challenge from host */
    for( i = 0 ; i < 10 ; ++i )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
159
        p_buffer[9-i] = p_css->disc.pi_challenge[i];
160
    }
161

Sam Hocevar's avatar
 
Sam Hocevar committed
162
    /* Send challenge to LU */
Sam Hocevar's avatar
 
Sam Hocevar committed
163
    if( ioctl_SendChallenge( i_fd, &i_agid, p_buffer ) < 0 )
164
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
165
        intf_ErrMsg( "css error: ioctl_SendChallenge failed" );
Sam Hocevar's avatar
 
Sam Hocevar committed
166
        return -1;
167
    }
168

Sam Hocevar's avatar
 
Sam Hocevar committed
169
    /* Get key1 from LU */
Sam Hocevar's avatar
 
Sam Hocevar committed
170
    if( ioctl_ReportKey1( i_fd, &i_agid, p_buffer ) < 0)
171
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
172
        intf_ErrMsg( "css error: ioctl_ReportKey1 failed" );
Sam Hocevar's avatar
 
Sam Hocevar committed
173
        return -1;
174
    }
175

Sam Hocevar's avatar
 
Sam Hocevar committed
176 177
    /* Send key1 to host */
    for( i = 0 ; i < KEY_SIZE ; i++ )
178
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
179
        p_css->disc.pi_key1[i] = p_buffer[4-i];
180 181 182 183
    }

    for( i = 0 ; i < 32 ; ++i )
    {
184 185
        CSSCryptKey( 0, i, p_css->disc.pi_challenge,
                           p_css->disc.pi_key_check );
186

187 188
        if( memcmp( p_css->disc.pi_key_check,
                    p_css->disc.pi_key1, KEY_SIZE ) == 0 )
189
        {
190
            intf_WarnMsg( 2, "css info: drive authentic, using variant %d", i);
191
            p_css->disc.i_varient = i;
192 193 194 195 196 197
            break;
        }
    }

    if( i == 32 )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
198
        intf_ErrMsg( "css error: drive would not authenticate" );
Sam Hocevar's avatar
 
Sam Hocevar committed
199
        return -1;
200
    }
201 202

    /* Get challenge from LU */
Sam Hocevar's avatar
 
Sam Hocevar committed
203
    if( ioctl_ReportChallenge( i_fd, &i_agid, p_buffer ) < 0 )
204
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
205
        intf_ErrMsg( "css error: ioctl_ReportKeyChallenge failed" );
Sam Hocevar's avatar
 
Sam Hocevar committed
206
        return -1;
207 208
    }

209
    /* Send challenge to host */
210
    for( i = 0 ; i < 10 ; ++i )
211
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
212
        p_css->disc.pi_challenge[i] = p_buffer[9-i];
213 214
    }

215
    CSSCryptKey( 1, p_css->disc.i_varient, p_css->disc.pi_challenge,
Sam Hocevar's avatar
 
Sam Hocevar committed
216
                                               p_css->disc.pi_key2 );
217

218
    /* Get key2 from host */
219
    for( i = 0 ; i < KEY_SIZE ; ++i )
220
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
221
        p_buffer[4-i] = p_css->disc.pi_key2[i];
222
    }
223

224
    /* Send key2 to LU */
Sam Hocevar's avatar
 
Sam Hocevar committed
225
    if( ioctl_SendKey2( i_fd, &i_agid, p_buffer ) < 0 )
226
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
227
        intf_ErrMsg( "css error: ioctl_SendKey2 failed" );
Sam Hocevar's avatar
 
Sam Hocevar committed
228
        return -1;
229
    }
230

231
    intf_WarnMsg( 2, "css info: authentication established" );
232

233 234
    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 );
Sam Hocevar's avatar
 
Sam Hocevar committed
235

Sam Hocevar's avatar
 
Sam Hocevar committed
236
    CSSCryptKey( 2, p_css->disc.i_varient, p_css->disc.pi_challenge,
Sam Hocevar's avatar
 
Sam Hocevar committed
237
                                           p_css->disc.pi_key_check );
238

239
    intf_WarnMsg( 2, "css info: received session key" );
240

Sam Hocevar's avatar
 
Sam Hocevar committed
241
    if( i_agid < 0 )
242
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
243
        return -1;
244
    }
245

246
    /* Test authentication success */
Sam Hocevar's avatar
 
Sam Hocevar committed
247
    switch( CSSGetASF( i_fd ) )
248
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
249 250 251 252
        case -1:
            return -1;

        case 1:
253
            intf_WarnMsg( 2, "css info: already authenticated" );
Sam Hocevar's avatar
 
Sam Hocevar committed
254 255 256
            return 0;

        case 0:
257
            intf_WarnMsg( 2, "css info: need to get disc key" );
258
    }
259

260
    /* Get encrypted disc key */
Sam Hocevar's avatar
 
Sam Hocevar committed
261
    if( ioctl_ReadKey( i_fd, &i_agid, p_buffer ) < 0 )
262
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
263
        intf_ErrMsg( "css error: ioctl_ReadKey failed" );
Sam Hocevar's avatar
 
Sam Hocevar committed
264
        return -1;
265
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
266

267
    /* Unencrypt disc key using bus key */
Sam Hocevar's avatar
 
Sam Hocevar committed
268
    for( i = 0 ; i < 2048 ; i++ )
269
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
270
        p_buffer[ i ] ^= p_css->disc.pi_key_check[ 4 - (i % KEY_SIZE) ];
271
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
272 273
    memcpy( p_css->disc.pi_key_check, p_buffer, 2048 );

274 275 276 277 278 279
    /* initialize title key to know it empty */
    for( i = 0 ; i < KEY_SIZE ; i++ )
    {
        p_css->pi_title_key[i] = 0;
    }

280
    /* Test authentication success */
Sam Hocevar's avatar
 
Sam Hocevar committed
281
    switch( CSSGetASF( i_fd ) )
282
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
283 284 285 286
        case -1:
            return -1;

        case 1:
287
            intf_WarnMsg( 2, "css info: successfully authenticated" );
Sam Hocevar's avatar
 
Sam Hocevar committed
288
            return 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
289

Sam Hocevar's avatar
 
Sam Hocevar committed
290
        case 0:
291 292
            intf_ErrMsg( "css error: no way to authenticate" );
            return -1;
Sam Hocevar's avatar
 
Sam Hocevar committed
293
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
294

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

Sam Hocevar's avatar
 
Sam Hocevar committed
298
#endif /* HAVE_CSS */
Sam Hocevar's avatar
 
Sam Hocevar committed
299 300
    return -1;

301 302
}

303
/*****************************************************************************
Stéphane Borel's avatar
 
Stéphane Borel committed
304
 * CSSGetKey : get title key.
Sam Hocevar's avatar
 
Sam Hocevar committed
305
 *****************************************************************************
306
 * The DVD should have been opened and authenticated before.
307
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
308
int CSSGetKey( int i_fd, css_t * p_css )
309
{
Sam Hocevar's avatar
 
Sam Hocevar committed
310
#ifdef HAVE_CSS
311 312 313 314
    /*
     * Title key cracking method from Ethan Hawke,
     * with Frank A. Stevenson algorithm.
     * Does not use any player key table and ioctls.
315
     */
Stéphane Borel's avatar
 
Stéphane Borel committed
316 317
    u8          pi_buf[0x800];
    dvd_key_t   pi_key;
318 319 320
    off_t       i_pos;
    boolean_t   b_encrypted;
    boolean_t   b_stop_scanning;
321 322 323
    int         i_bytes_read;
    int         i_best_plen;
    int         i_best_p;
324 325 326 327 328 329
    int         i,j;

    for( i = 0 ; i < KEY_SIZE ; i++ )
    {
        pi_key[i] = 0;
    }
330

Stéphane Borel's avatar
 
Stéphane Borel committed
331 332 333 334 335
    b_encrypted = 0;
    b_stop_scanning = 0;

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

Stéphane Borel's avatar
 
Stéphane Borel committed
337
    do {
Sam Hocevar's avatar
 
Sam Hocevar committed
338
#if !defined( WIN32 )
Sam Hocevar's avatar
 
Sam Hocevar committed
339 340
    i_pos = lseek( i_fd, i_pos, SEEK_SET );
    i_bytes_read = read( i_fd, pi_buf, 0x800 );
Sam Hocevar's avatar
 
Sam Hocevar committed
341 342 343 344
#else
    i_pos = SetFilePointer( (HANDLE) i_fd, i_pos, 0, FILE_BEGIN );
    ReadFile( (HANDLE) i_fd, pi_buf, 0x800, &i_bytes_read, NULL );
#endif
345

Stéphane Borel's avatar
 
Stéphane Borel committed
346 347 348 349 350 351
    /* PES_scrambling_control */
    if( pi_buf[0x14] & 0x30 )
    {
        b_encrypted = 1;
        i_best_plen = 0;
        i_best_p = 0;
352

Stéphane Borel's avatar
 
Stéphane Borel committed
353 354 355 356
        for( i = 2 ; i < 0x30 ; i++ )
        {
            for( j = i ; ( j < 0x80 ) &&
                   ( pi_buf[0x7F - (j%i)] == pi_buf[0x7F-j] ) ; j++ );
357
            {
Stéphane Borel's avatar
 
Stéphane Borel committed
358
                if( ( j > i_best_plen ) && ( j > i ) )
359
                {
Stéphane Borel's avatar
 
Stéphane Borel committed
360 361
                    i_best_plen = j;
                    i_best_p = i;
362
                }
363
            }
Stéphane Borel's avatar
 
Stéphane Borel committed
364
        }
365

Stéphane Borel's avatar
 
Stéphane Borel committed
366 367 368 369 370 371
        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 );
372
            b_stop_scanning = ( i >= 0 );
Sam Hocevar's avatar
 
Sam Hocevar committed
373 374 375 376 377 378 379 380
        }
    }

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

    if( b_stop_scanning)
    {
381 382 383 384
            memcpy( p_css->pi_title_key,
                    &pi_key, sizeof(dvd_key_t) );
        intf_WarnMsg( 2, "css info: vts key initialized" );
        return 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
385 386 387 388
    }

    if( !b_encrypted )
    {
389
        intf_WarnMsg( 2, "css warning: this file was _NOT_ encrypted!" );
390
        return 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
391 392
    }

393
    return -1;
Sam Hocevar's avatar
 
Sam Hocevar committed
394

Sam Hocevar's avatar
 
Sam Hocevar committed
395
#else /* HAVE_CSS */
396 397
    intf_ErrMsg( "css error: css decryption unavailable" );
    return -1;
Sam Hocevar's avatar
 
Sam Hocevar committed
398

Sam Hocevar's avatar
 
Sam Hocevar committed
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
#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;
Sam Hocevar's avatar
 
Sam Hocevar committed
438 439
            *pi_sec = pi_css_tab1[*pi_sec] ^( i_t5 & 0xff );
            pi_sec++;
Sam Hocevar's avatar
 
Sam Hocevar committed
440 441 442 443 444
            i_t5 >>= 8;
        }
    }

    return 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
445

Sam Hocevar's avatar
 
Sam Hocevar committed
446 447
#else /* HAVE_CSS */
    return 1;
Sam Hocevar's avatar
 
Sam Hocevar committed
448

Sam Hocevar's avatar
 
Sam Hocevar committed
449 450 451 452
#endif /* HAVE_CSS */
}

#ifdef HAVE_CSS
Sam Hocevar's avatar
 
Sam Hocevar committed
453 454

/* Following functions are local */
Sam Hocevar's avatar
 
Sam Hocevar committed
455 456 457 458 459 460 461 462 463

/*****************************************************************************
 * CSSGetASF : Get Authentification success flag
 *****************************************************************************
 * Returns :
 *  -1 on ioctl error,
 *  0 if the device needs to be authenticated,
 *  1 either.
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
464
static int CSSGetASF( int i_fd )
Sam Hocevar's avatar
 
Sam Hocevar committed
465
{
Sam Hocevar's avatar
 
Sam Hocevar committed
466 467
    int i_agid;
    int i_asf = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
468

Sam Hocevar's avatar
 
Sam Hocevar committed
469
    for( i_agid = 0 ; i_agid < 4 ; i_agid++ )
Sam Hocevar's avatar
 
Sam Hocevar committed
470
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
471
        if( ioctl_ReportASF( i_fd, &i_agid, &i_asf ) == 0 )
Sam Hocevar's avatar
 
Sam Hocevar committed
472
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
473 474
            intf_WarnMsg( 3, "css info: GetASF %sauthenticated",
                          i_asf ? "":"not " );
Sam Hocevar's avatar
 
Sam Hocevar committed
475 476

            return i_asf;
Sam Hocevar's avatar
 
Sam Hocevar committed
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
        }
    }

    /* 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
609

Sam Hocevar's avatar
 
Sam Hocevar committed
610 611 612 613 614 615 616
            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;
617
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
618 619 620
    
        pi_bits[--i_index] = i_val;
    } while( i_index > 0 );
621

Sam Hocevar's avatar
 
Sam Hocevar committed
622 623 624 625 626
    /* 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];
627

Sam Hocevar's avatar
 
Sam Hocevar committed
628 629 630 631 632 633
    /* 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
634
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
635 636 637 638
        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
639
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
640
    pi_tmp1[4] ^= pi_tmp1[0];
641

Sam Hocevar's avatar
 
Sam Hocevar committed
642
    for( i = 5, i_term = 0 ; --i >= 0 ; i_term = pi_tmp1[i] )
Stéphane Borel's avatar
 
Stéphane Borel committed
643
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
644 645 646 647
        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
648
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
649
    pi_tmp2[4] ^= pi_tmp2[0];
650

Sam Hocevar's avatar
 
Sam Hocevar committed
651
    for( i = 5, i_term = 0 ; --i >= 0 ; i_term = pi_tmp2[i] )
Stéphane Borel's avatar
 
Stéphane Borel committed
652
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
653 654 655 656 657
        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
658
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
659
    pi_tmp1[4] ^= pi_tmp1[0];
660

Sam Hocevar's avatar
 
Sam Hocevar committed
661
    for( i = 5, i_term = 0 ; --i >= 0 ; i_term = pi_tmp1[i] )
Stéphane Borel's avatar
 
Stéphane Borel committed
662
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
663 664
        i_index = pi_bits[10 + i] ^ pi_tmp1[i];
        i_index = pi_crypt_tab1[i_index] ^ ~pi_crypt_tab2[i_index] ^ i_cse;
665

Sam Hocevar's avatar
 
Sam Hocevar committed
666
        i_index = pi_crypt_tab2[i_index] ^ pi_crypt_tab3[i_index] ^ i_term;
667

Sam Hocevar's avatar
 
Sam Hocevar committed
668
        pi_tmp2[i] = pi_crypt_tab0[i_index] ^ pi_crypt_tab2[i_index];
Stéphane Borel's avatar
 
Stéphane Borel committed
669
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
670
    pi_tmp2[4] ^= pi_tmp2[0];
671

Sam Hocevar's avatar
 
Sam Hocevar committed
672
    for( i = 5, i_term = 0 ; --i >= 0 ; i_term = pi_tmp2[i] )
Stéphane Borel's avatar
 
Stéphane Borel committed
673
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
674 675
        i_index = pi_bits[5 + i] ^ pi_tmp2[i];
        i_index = pi_crypt_tab1[i_index] ^ ~pi_crypt_tab2[i_index] ^ i_cse;
676

Sam Hocevar's avatar
 
Sam Hocevar committed
677 678 679
        pi_tmp1[i] = pi_crypt_tab2[i_index] ^ pi_crypt_tab3[i_index] ^ i_term;
    }
    pi_tmp1[4] ^= pi_tmp1[0];
680

Sam Hocevar's avatar
 
Sam Hocevar committed
681 682 683 684
    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
685

Sam Hocevar's avatar
 
Sam Hocevar committed
686 687
        pi_key[i] = pi_crypt_tab2[i_index] ^ pi_crypt_tab3[i_index] ^ i_term;
    }
688

Sam Hocevar's avatar
 
Sam Hocevar committed
689
    return;
690 691 692
}

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
693 694 695
 * CSSCracker : title key decryption by cracking
 *****************************************************************************
 * This function is called by CSSGetKeys to find a key
696
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
697 698 699 700 701
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 )
702
{
Sam Hocevar's avatar
 
Sam Hocevar committed
703 704 705 706 707 708
    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;
709

Sam Hocevar's avatar
 
Sam Hocevar committed
710 711

    for( i = 0 ; i < 10 ; i++ )
712
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
713 714 715 716 717 718 719 720
        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 */
721 722
        i_t5 = 0;

Sam Hocevar's avatar
 
Sam Hocevar committed
723 724
        /* iterate cipher 4 times to reconstruct LFSR2 */
        for( i = 0 ; i < 4 ; i++ )
725
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
726
            /* advance LFSR1 normaly */
727
            i_t4 = pi_css_tab2[i_t2] ^ pi_css_tab3[i_t1];
Sam Hocevar's avatar
 
Sam Hocevar committed
728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
            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;
756 757 758
            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
759 760
                                         i_t3 ) >> 8 ) ^ i_t3 ) >> 5 ) & 0xff;
            i_t3 = ( i_t3 << 8 ) | i_t6;
761 762
            i_t6 = pi_css_tab4[i_t6];
            i_t5 += i_t6 + i_t4;
Sam Hocevar's avatar
 
Sam Hocevar committed
763 764 765 766 767
            if( ( i_t5 & 0xff ) != pi_buffer[i] )
            {
                break;
            }

768
            i_t5 >>= 8;
769
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816

        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];
817 818
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
819
    return i_exit;
820
}
Sam Hocevar's avatar
 
Sam Hocevar committed
821

Sam Hocevar's avatar
 
Sam Hocevar committed
822
#endif /* HAVE_CSS */
Sam Hocevar's avatar
 
Sam Hocevar committed
823