shithub: libdvdcss

Download patch

ref: 7c90c435f3841bb11872f2e88024dcfccaa30816
parent: 88a0017365d6c15fc32021330d768bd52e520bc4
author: Håkan Hjort <d95hjort@dtek.chalmers.se>
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 <stef@via.ecp.fr>
  *         H�kan Hjort <d95hjort@dtek.chalmers.se>
@@ -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 <stef@via.ecp.fr>
  *
@@ -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 * );