ref: 7c90c435f3841bb11872f2e88024dcfccaa30816
parent: 88a0017365d6c15fc32021330d768bd52e520bc4
author: Håkan Hjort <[email protected]>
date: Sun Jan 20 12:04:54 EST 2002
Make CSSAuth more rubust, should now also work with drives that are not 100% compliant to the SFF-8090 standard. AGID invalidation should now work allowing us to recover from hung / failed authentications. Corrected CSSGetASF, it does not take an AGID argument. Move several data structures from the dvdcss handle to local variables in CSSAuth. Remove CSSAuth as a externaly visible function in css.c.
--- a/src/css.c
+++ b/src/css.c
@@ -2,7 +2,7 @@
* css.c: Functions for DVD authentification and unscrambling
*****************************************************************************
* Copyright (C) 1999-2001 VideoLAN
- * $Id: css.c,v 1.1 2001/12/22 00:08:13 sam Exp $
+ * $Id: css.c,v 1.2 2002/01/20 17:04:54 hjort Exp $
*
* Author: St�phane Borel <[email protected]>
* H�kan Hjort <[email protected]>
@@ -54,6 +54,7 @@
/*****************************************************************************
* Local prototypes
*****************************************************************************/
+static int CSSAuth ( dvdcss_handle dvdcss );
static int CSSGetASF ( dvdcss_handle dvdcss );
static void CSSCryptKey ( int i_key_type, int i_varient,
u8 const * p_challenge, u8* p_key );
@@ -86,55 +87,54 @@
}
/*****************************************************************************
- * CSSAuth : CSS Structure initialisation and DVD authentication.
+ * CSSAuth : DVD CSS 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.
+ * It simulates the mutual authentication between logical unit and host,
+ * and stops when a session key (called bus key) has been established.
+ * Always do the full auth sequence. Some drives seem to lie and always
+ * respond with ASF=1. For instance the old DVD roms on Compaq Armada says
+ * that ASF=1 from the start and then later fail with a 'read of scrambled
+ * block without authentication' error.
*****************************************************************************/
-int CSSAuth( dvdcss_handle dvdcss )
+static int CSSAuth( dvdcss_handle dvdcss )
{
- /* structures defined in cdrom.h or dvdio.h */
- unsigned char p_buffer[10];
- char psz_warning[48];
- int i_ret = -1;
- int i;
+ u8 p_buffer[10];
+ u8 p_challenge[2*KEY_SIZE];
+ dvd_key_t p_key1;
+ dvd_key_t p_key2;
+ dvd_key_t p_key_check;
+ u8 i_varient = 0;
+ char psz_warning[48];
+ int i_ret = -1;
+ int i;
- dvdcss->css.i_agid = 0;
-
- /* Test authentication success */
- switch( CSSGetASF( dvdcss ) )
+ /* So this isn't really necessary except for debuging. */
+ if( CSSGetASF( dvdcss ) < 0 )
{
- case -1:
- return -1;
-
- case 1:
- _dvdcss_debug( dvdcss, "already authenticated" );
- break;
-
- case 0:
- _dvdcss_debug( dvdcss, "need to authenticate" );
- break;
+ _dvdcss_error( dvdcss, "fatal error in CSSAuth" );
}
- /* Init sequence, request AGID */
- for( i = 1; i < 4 ; ++i )
- {
- snprintf( psz_warning, sizeof(psz_warning), "requesting AGID %d", i );
- _dvdcss_debug( dvdcss, psz_warning );
+ _dvdcss_debug( dvdcss, "requesting AGID" );
+ i_ret = ioctl_ReportAgid( dvdcss->i_fd, &dvdcss->css.i_agid );
- i_ret = ioctl_ReportAgid( dvdcss->i_fd, &dvdcss->css.i_agid );
-
- if( i_ret != -1 )
- {
- /* No error during ioctl: we know the device is authenticated */
- break;
- }
-
- _dvdcss_error( dvdcss, "ioctl_ReportAgid failed, invalidating" );
-
- dvdcss->css.i_agid = 0;
+ /* We might have to reset hung authentication processes in the drive
+ by invalidating the corresponding AGID'. As long as we haven't got
+ an AGID, invalidate one (in sequence) and try again. */
+ for( i = 0; i_ret == -1 && i < 4 ; ++i )
+ {
+ _dvdcss_debug( dvdcss, "ioctl_ReportAgid failed" );
+
+ sprintf( psz_warning, "invalidating AGID %d", i );
+ _dvdcss_debug( dvdcss, psz_warning );
+
+ /* This is really _not good_, should be handled by the OS.
+ Invalidating an AGID could make another process fail some
+ where in it's authentication process. */
+ dvdcss->css.i_agid = i;
ioctl_InvalidateAgid( dvdcss->i_fd, &dvdcss->css.i_agid );
+
+ _dvdcss_debug( dvdcss, "requesting AGID" );
+ i_ret = ioctl_ReportAgid( dvdcss->i_fd, &dvdcss->css.i_agid );
}
/* Unable to authenticate without AGID */
@@ -144,21 +144,24 @@
return -1;
}
+ /* Setup a challenge, any values should work */
for( i = 0 ; i < 10; ++i )
{
- dvdcss->css.disc.p_challenge[i] = i;
+ p_challenge[i] = i;
}
/* Get challenge from host */
for( i = 0 ; i < 10 ; ++i )
{
- p_buffer[9-i] = dvdcss->css.disc.p_challenge[i];
+ p_buffer[9-i] = p_challenge[i];
}
/* Send challenge to LU */
- if( ioctl_SendChallenge( dvdcss->i_fd, &dvdcss->css.i_agid, p_buffer ) < 0 )
+ if( ioctl_SendChallenge( dvdcss->i_fd,
+ &dvdcss->css.i_agid, p_buffer ) < 0 )
{
_dvdcss_error( dvdcss, "ioctl_SendChallenge failed" );
+ ioctl_InvalidateAgid( dvdcss->i_fd, &dvdcss->css.i_agid );
return -1;
}
@@ -166,6 +169,7 @@
if( ioctl_ReportKey1( dvdcss->i_fd, &dvdcss->css.i_agid, p_buffer ) < 0)
{
_dvdcss_error( dvdcss, "ioctl_ReportKey1 failed" );
+ ioctl_InvalidateAgid( dvdcss->i_fd, &dvdcss->css.i_agid );
return -1;
}
@@ -172,21 +176,19 @@
/* Send key1 to host */
for( i = 0 ; i < KEY_SIZE ; i++ )
{
- dvdcss->css.disc.p_key1[i] = p_buffer[4-i];
+ p_key1[i] = p_buffer[4-i];
}
for( i = 0 ; i < 32 ; ++i )
{
- CSSCryptKey( 0, i, dvdcss->css.disc.p_challenge,
- dvdcss->css.disc.p_key_check );
+ CSSCryptKey( 0, i, p_challenge, p_key_check );
- if( memcmp( dvdcss->css.disc.p_key_check,
- dvdcss->css.disc.p_key1, KEY_SIZE ) == 0 )
+ if( memcmp( p_key_check, p_key1, KEY_SIZE ) == 0 )
{
snprintf( psz_warning, sizeof(psz_warning),
- "drive authentic, using variant %d", i );
+ "drive authentic, using varient %d", i );
_dvdcss_debug( dvdcss, psz_warning );
- dvdcss->css.disc.i_varient = i;
+ i_varient = i;
break;
}
}
@@ -194,13 +196,16 @@
if( i == 32 )
{
_dvdcss_error( dvdcss, "drive would not authenticate" );
+ ioctl_InvalidateAgid( dvdcss->i_fd, &dvdcss->css.i_agid );
return -1;
}
/* Get challenge from LU */
- if( ioctl_ReportChallenge( dvdcss->i_fd, &dvdcss->css.i_agid, p_buffer ) < 0 )
+ if( ioctl_ReportChallenge( dvdcss->i_fd,
+ &dvdcss->css.i_agid, p_buffer ) < 0 )
{
_dvdcss_error( dvdcss, "ioctl_ReportKeyChallenge failed" );
+ ioctl_InvalidateAgid( dvdcss->i_fd, &dvdcss->css.i_agid );
return -1;
}
@@ -207,17 +212,15 @@
/* Send challenge to host */
for( i = 0 ; i < 10 ; ++i )
{
- dvdcss->css.disc.p_challenge[i] = p_buffer[9-i];
+ p_challenge[i] = p_buffer[9-i];
}
- CSSCryptKey( 1, dvdcss->css.disc.i_varient,
- dvdcss->css.disc.p_challenge,
- dvdcss->css.disc.p_key2 );
+ CSSCryptKey( 1, i_varient, p_challenge, p_key2 );
/* Get key2 from host */
for( i = 0 ; i < KEY_SIZE ; ++i )
{
- p_buffer[4-i] = dvdcss->css.disc.p_key2[i];
+ p_buffer[4-i] = p_key2[i];
}
/* Send key2 to LU */
@@ -224,43 +227,30 @@
if( ioctl_SendKey2( dvdcss->i_fd, &dvdcss->css.i_agid, p_buffer ) < 0 )
{
_dvdcss_error( dvdcss, "ioctl_SendKey2 failed" );
+ ioctl_InvalidateAgid( dvdcss->i_fd, &dvdcss->css.i_agid );
return -1;
}
+ /* The drive has accepted us as authentic. */
_dvdcss_debug( dvdcss, "authentication established" );
- memcpy( dvdcss->css.disc.p_challenge,
- dvdcss->css.disc.p_key1, KEY_SIZE );
- memcpy( dvdcss->css.disc.p_challenge+KEY_SIZE,
- dvdcss->css.disc.p_key2, KEY_SIZE );
+ memcpy( p_challenge, p_key1, KEY_SIZE );
+ memcpy( p_challenge + KEY_SIZE, p_key2, KEY_SIZE );
- CSSCryptKey( 2, dvdcss->css.disc.i_varient,
- dvdcss->css.disc.p_challenge,
- dvdcss->css.disc.p_key_check );
+ CSSCryptKey( 2, i_varient, p_challenge, dvdcss->css.p_bus_key );
- _dvdcss_debug( dvdcss, "received session key" );
+ return 0;
+}
- if( dvdcss->css.i_agid < 0 )
- {
- return -1;
- }
+/*****************************************************************************
+ * CSSPrintKey : debug function that dumps a key value
+ *****************************************************************************/static void CSSPrintKey( dvdcss_handle dvdcss, u8* data )
+{
+ char psz_output[80];
- /* Test authentication success */
- switch( CSSGetASF( dvdcss ) )
- {
- case -1:
- return -1;
-
- case 1:
- _dvdcss_debug( dvdcss, "already authenticated" );
- return 0;
-
- case 0:
- _dvdcss_debug( dvdcss, "need to get disc key" );
- return 0;
- }
-
- return -1;
+ sprintf( psz_output, "the key is %02x %02x %02x %02x %02x",
+ data[0], data[1], data[2], data[3], data[4] );
+ _dvdcss_debug( dvdcss, psz_output );
}
/*****************************************************************************
@@ -273,7 +263,7 @@
*****************************************************************************/
int CSSGetDiscKey( dvdcss_handle dvdcss )
{
- unsigned char p_buffer[2048 + 4 + 1];
+ unsigned char p_buffer[2048];
#ifdef HAVE_CSSKEYS
dvd_key_t disc_key;
dvd_key_t test_key;
@@ -295,9 +285,8 @@
/* Unencrypt disc key using bus key */
for( i = 0 ; i < 2048 ; i++ )
{
- p_buffer[ i ] ^= dvdcss->css.disc.p_key_check[ 4 - (i % KEY_SIZE) ];
+ p_buffer[ i ] ^= dvdcss->css.p_bus_key[ 4 - (i % KEY_SIZE) ];
}
- memcpy( dvdcss->css.disc.p_disc_key, p_buffer, 2048 );
switch( dvdcss->i_method )
{
@@ -310,14 +299,13 @@
{
/* Take encrypted disc key and decrypt it */
memcpy( disc_key,
- dvdcss->css.disc.p_disc_key
- + playerkeys[i].i_offset,
+ p_buffer + playerkeys[i].i_offset,
KEY_SIZE );
CSSDecryptKey( disc_key, playerkeys[i].p_key, 0 );
/* Encrypt disc key hash with disc key to
* check we have disc key */
- memcpy( test_key, dvdcss->css.disc.p_disc_key, KEY_SIZE );
+ memcpy( test_key, p_buffer, KEY_SIZE );
CSSDecryptKey( test_key, disc_key, 0);
i++;
@@ -325,8 +313,8 @@
} while( ( playerkeys[i].i_offset != -1 ) &&
( memcmp( test_key, disc_key, KEY_SIZE ) ) );
- /* The decrypted disk key will replace the disk key hash */
- memcpy( dvdcss->css.disc.p_disc_key, disc_key, KEY_SIZE );
+ /* Store decypted disk for use when decrypting title keys */
+ memcpy( dvdcss->css.p_disc_key, disc_key, KEY_SIZE );
break;
#else
dvdcss->i_method = DVDCSS_METHOD_DISC;
@@ -335,7 +323,8 @@
/* Crack Disc key to be able to use it */
_dvdcss_debug( dvdcss, "cracking disc key with key hash" );
_dvdcss_debug( dvdcss, "building 64MB table ... this will take some time" );
- CSSDiscCrack( dvdcss, dvdcss->css.disc.p_disc_key );
+ CSSDiscCrack( dvdcss, p_buffer );
+ memcpy( dvdcss->css.p_disc_key, p_buffer, KEY_SIZE );
break;
default:
@@ -342,6 +331,7 @@
_dvdcss_debug( dvdcss, "disc key won't be decrypted" );
}
+ CSSPrintKey( dvdcss, dvdcss->css.p_disc_key );
return 0;
}
@@ -443,6 +433,7 @@
if( b_stop_scanning )
{
memcpy( dvdcss->css.p_title_key, &p_key, sizeof(dvd_key_t) );
+ CSSPrintKey( dvdcss, dvdcss->css.p_title_key );
_dvdcss_debug( dvdcss, "vts key initialized" );
return 0;
}
@@ -463,9 +454,9 @@
*/
_dvdcss_debug( dvdcss, "decrypting title key with disc key" );
-
- /* We need to authenticate again for every key
- * (to get a new session key ?) */
+
+ /* We need to authenticate again for every title key,
+ * a new bus key is used each time. */
CSSAuth( dvdcss );
/* Get encrypted title key */
@@ -478,13 +469,23 @@
/* Unencrypt title key using bus key */
for( i = 0 ; i < KEY_SIZE ; i++ )
{
- p_key[ i ] ^= dvdcss->css.disc.p_key_check[ 4 - (i % KEY_SIZE) ];
+ p_key[ i ] ^= dvdcss->css.p_bus_key[ 4 - (i % KEY_SIZE) ];
}
+ /* If p_key is all zero then there realy wasn't any key pressent
+ * even though we got to read it without an error. */
+ if( !( p_key[0] | p_key[1] | p_key[2] | p_key[3] | p_key[4] ) )
+ {
+ memset( dvdcss->css.p_title_key, 0, sizeof(dvd_key_t) );
+ CSSPrintKey( dvdcss, dvdcss->css.p_title_key );
+ return 0;
+ }
+
/* Title key decryption needs one inversion 0xff */
- CSSDecryptKey( p_key, dvdcss->css.disc.p_disc_key, 0xff );
+ CSSDecryptKey( p_key, dvdcss->css.p_disc_key, 0xff );
memcpy( dvdcss->css.p_title_key, p_key, sizeof(dvd_key_t) );
+ CSSPrintKey( dvdcss, dvdcss->css.p_title_key );
return 0;
} // (dvdcss->i_method == DVDCSS_METHOD_TITLE) || (dvdcss->b_ioctls == 0)
@@ -546,29 +547,25 @@
*****************************************************************************/
static int CSSGetASF( dvdcss_handle dvdcss )
{
- int i_agid;
int i_asf = 0;
- for( i_agid = 0 ; i_agid < 4 ; i_agid++ )
+ if( ioctl_ReportASF( dvdcss->i_fd, NULL, &i_asf ) != 0 )
{
- if( ioctl_ReportASF( dvdcss->i_fd, &i_agid, &i_asf ) == 0 )
- {
- if( i_asf )
- {
- _dvdcss_debug( dvdcss, "GetASF authenticated" );
- }
- else
- {
- _dvdcss_debug( dvdcss, "GetASF not authenticated" );
- }
+ /* The ioctl process has failed */
+ _dvdcss_error( dvdcss, "GetASF fatal error" );
+ return -1;
+ }
- return i_asf;
- }
+ if( i_asf )
+ {
+ _dvdcss_debug( dvdcss, "GetASF authenticated (ASF=1)" );
}
+ else
+ {
+ _dvdcss_debug( dvdcss, "GetASF not authenticated (ASF=0)" );
+ }
- /* The ioctl process has failed */
- _dvdcss_error( dvdcss, "GetASF fatal error" );
- return -1;
+ return i_asf;
}
/*****************************************************************************
--- a/src/css.h
+++ b/src/css.h
@@ -2,7 +2,7 @@
* css.h: Structures for DVD authentification and unscrambling
*****************************************************************************
* Copyright (C) 1999-2001 VideoLAN
- * $Id: css.h,v 1.1 2001/12/22 00:08:13 sam Exp $
+ * $Id: css.h,v 1.2 2002/01/20 17:04:54 hjort Exp $
*
* Author: St�phane Borel <[email protected]>
*
@@ -30,16 +30,6 @@
typedef u8 dvd_key_t[KEY_SIZE];
-typedef struct disc_s
-{
- u8 p_challenge[2*KEY_SIZE];
- dvd_key_t p_key1;
- dvd_key_t p_key2;
- dvd_key_t p_key_check;
- u8 i_varient;
- u8 p_disc_key[2048];
-} disc_t;
-
typedef struct dvd_title_s
{
int i_startlb;
@@ -49,9 +39,10 @@
typedef struct css_s
{
- int i_agid;
- disc_t disc;
- dvd_key_t p_title_key;
+ int i_agid; /* Current Authenication Grant ID. */
+ dvd_key_t p_bus_key; /* Current session key. */
+ dvd_key_t p_disc_key; /* This DVD disc's key. */
+ dvd_key_t p_title_key; /* Current title key. */
} css_t;
/*****************************************************************************
@@ -58,7 +49,6 @@
* Prototypes in css.c
*****************************************************************************/
int CSSTest ( dvdcss_handle );
-int CSSAuth ( dvdcss_handle );
int CSSGetDiscKey ( dvdcss_handle );
int CSSGetTitleKey ( dvdcss_handle, int );
int CSSDescrambleSector ( u8 * , u8 * );