📄 css.c
字号:
/*****************************************************************************
* css.c: Functions for DVD authentication and descrambling
*****************************************************************************
* Copyright (C) 1999-2001 VideoLAN
* $Id: css.c,v 1.2 2004/11/20 20:19:30 jbors Exp $
*
* Author: St閜hane Borel <stef@via.ecp.fr>
* H錵an Hjort <d95hjort@dtek.chalmers.se>
*
* based on:
* - css-auth by Derek Fawcus <derek@spider.com>
* - DVD CSS ioctls example program by Andrew T. Veliath <andrewtv@usa.net>
* - The Divide and conquer attack by Frank A. Stevenson <frank@funcom.com>
* - DeCSSPlus by Ethan Hawke
* - DecVOB
* see http://www.lemuria.org/DeCSS/ by Tom Vogt for more information.
*
* 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
*****************************************************************************/
//#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif
#if !defined( WIN32 )
# include <unistd.h>
#endif
#include <fcntl.h>
#ifdef HAVE_LIMITS_H
# include <limits.h>
#endif
#include "dvdcss/dvdcss.h"
#include "common.h"
#include "css.h"
#include "libdvdcss.h"
#include "csstables.h"
#include "ioctl.h"
#include "device.h"
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int GetBusKey ( dvdcss_t );
static int GetASF ( dvdcss_t );
static void CryptKey ( int, int, uint8_t const *, uint8_t * );
static void DecryptKey ( uint8_t,
uint8_t const *, uint8_t const *, uint8_t * );
static int DecryptDiscKey ( uint8_t const *, dvd_key_t );
static int CrackDiscKey ( dvdcss_t, uint8_t * );
static void DecryptTitleKey ( dvd_key_t, dvd_key_t );
static int RecoverTitleKey ( int, uint8_t const *,
uint8_t const *, uint8_t const *, uint8_t * );
static int CrackTitleKey ( dvdcss_t, int, int, dvd_key_t );
static int AttackPattern ( uint8_t const[], int, uint8_t * );
#if 0
static int AttackPadding ( uint8_t const[], int, uint8_t * );
#endif
/*****************************************************************************
* _dvdcss_test: check if the disc is encrypted or not
*****************************************************************************/
int _dvdcss_test( dvdcss_t dvdcss )
{
int i_ret, i_copyright;
i_ret = ioctl_ReadCopyright( dvdcss->i_fd, 0 /* i_layer */, &i_copyright );
#ifdef WIN32
if( i_ret < 0 )
{
/* Maybe we didn't have enough priviledges to read the copyright
* (see ioctl_ReadCopyright comments).
* Apparently, on unencrypted DVDs _dvdcss_disckey() always fails, so
* we can check this as a work-around. */
i_ret = 0;
if( _dvdcss_disckey( dvdcss ) < 0 )
i_copyright = 0;
else
i_copyright = 1;
}
#endif
if( i_ret < 0 )
{
/* Since it's the first ioctl we try to issue, we add a notice */
_dvdcss_error( dvdcss, "css error: ioctl_ReadCopyright failed, "
"make sure there is a DVD in the drive, and that "
"you have used the correct device node." );
return i_ret;
}
return i_copyright;
}
/*****************************************************************************
* GetBusKey : Go through the CSS Authentication process
*****************************************************************************
* 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.
*****************************************************************************/
static int GetBusKey( dvdcss_t dvdcss )
{
uint8_t p_buffer[10];
uint8_t p_challenge[2*KEY_SIZE];
dvd_key_t p_key1;
dvd_key_t p_key2;
dvd_key_t p_key_check;
uint8_t i_variant = 0;
char psz_warning[80];
int i_ret = -1;
int i;
_dvdcss_debug( dvdcss, "requesting AGID" );
i_ret = ioctl_ReportAgid( dvdcss->i_fd, &dvdcss->css.i_agid );
/* 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 )
{
sprintf( psz_warning,
"ioctl ReportAgid failed, 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 */
if( i_ret == -1 )
{
_dvdcss_error( dvdcss, "ioctl ReportAgid failed, fatal" );
return -1;
}
/* Setup a challenge, any values should work */
for( i = 0 ; i < 10; ++i )
{
p_challenge[i] = i;
}
/* Get challenge from host */
for( i = 0 ; i < 10 ; ++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 )
{
_dvdcss_error( dvdcss, "ioctl SendChallenge failed" );
ioctl_InvalidateAgid( dvdcss->i_fd, &dvdcss->css.i_agid );
return -1;
}
/* Get key1 from LU */
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;
}
/* Send key1 to host */
for( i = 0 ; i < KEY_SIZE ; i++ )
{
p_key1[i] = p_buffer[4-i];
}
for( i = 0 ; i < 32 ; ++i )
{
CryptKey( 0, i, p_challenge, p_key_check );
if( memcmp( p_key_check, p_key1, KEY_SIZE ) == 0 )
{
snprintf( psz_warning, sizeof(psz_warning),
"drive authenticated, using variant %d", i );
_dvdcss_debug( dvdcss, psz_warning );
i_variant = i;
break;
}
}
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 )
{
_dvdcss_error( dvdcss, "ioctl ReportKeyChallenge failed" );
ioctl_InvalidateAgid( dvdcss->i_fd, &dvdcss->css.i_agid );
return -1;
}
/* Send challenge to host */
for( i = 0 ; i < 10 ; ++i )
{
p_challenge[i] = p_buffer[9-i];
}
CryptKey( 1, i_variant, p_challenge, p_key2 );
/* Get key2 from host */
for( i = 0 ; i < KEY_SIZE ; ++i )
{
p_buffer[4-i] = p_key2[i];
}
/* Send key2 to LU */
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( p_challenge, p_key1, KEY_SIZE );
memcpy( p_challenge + KEY_SIZE, p_key2, KEY_SIZE );
CryptKey( 2, i_variant, p_challenge, dvdcss->css.p_bus_key );
return 0;
}
/*****************************************************************************
* PrintKey : debug function that dumps a key value
*****************************************************************************/
static void PrintKey( dvdcss_t dvdcss, char *prefix, uint8_t const *data )
{
char psz_output[80];
sprintf( psz_output, "%s%02x:%02x:%02x:%02x:%02x", prefix,
data[0], data[1], data[2], data[3], data[4] );
_dvdcss_debug( dvdcss, psz_output );
}
/*****************************************************************************
* _dvdcss_title: crack or decrypt the current title key if needed
*****************************************************************************
* This function should only be called by dvdcss->pf_seek and should eventually
* not be external if possible.
*****************************************************************************/
int _dvdcss_title ( dvdcss_t dvdcss, int i_block )
{
dvd_title_t *p_title;
dvd_title_t *p_newtitle;
dvd_key_t p_title_key;
int i_fd, i_ret = -1, b_cache = 0;
if( ! dvdcss->b_scrambled )
{
return 0;
}
/* Check if we've already cracked this key */
p_title = dvdcss->p_titles;
while( p_title != NULL
&& p_title->p_next != NULL
&& p_title->p_next->i_startlb <= i_block )
{
p_title = p_title->p_next;
}
if( p_title != NULL
&& p_title->i_startlb == i_block )
{
/* We've already cracked this key, nothing to do */
memcpy( dvdcss->css.p_title_key, p_title->p_key, sizeof(dvd_key_t) );
return 0;
}
/* Check whether the key is in our disk cache */
if( dvdcss->psz_cachefile[0] )
{
/* XXX: be careful, we use sprintf and not snprintf */
sprintf( dvdcss->psz_block, "%.10x", i_block );
i_fd = open( dvdcss->psz_cachefile, O_RDONLY );
b_cache = 1;
if( i_fd >= 0 )
{
if( read( i_fd, p_title_key, 5 ) == 5 )
{
_dvdcss_debug( dvdcss, "key found in cache" );
/* Don't try to save it again */
b_cache = 0;
i_ret = 1;
}
close( i_fd );
}
}
/* Crack or decrypt CSS title key for current VTS */
if( i_ret < 0 )
{
i_ret = _dvdcss_titlekey( dvdcss, i_block, p_title_key );
if( i_ret < 0 )
{
_dvdcss_error( dvdcss, "fatal error in vts css key" );
return i_ret;
}
if( i_ret == 0 )
{
_dvdcss_debug( dvdcss, "unencrypted title" );
/* We cache this anyway, so we don't need to check again. */
}
}
/* Key is valid, we store it on disk. */
if( b_cache )
{
i_fd = open( dvdcss->psz_cachefile, O_RDWR|O_CREAT|O_EXCL, 0644 );
if( i_fd >= 0 )
{
write( i_fd, p_title_key, 5 );
close( i_fd );
}
}
/* Find our spot in the list */
p_newtitle = NULL;
p_title = dvdcss->p_titles;
while( ( p_title != NULL ) && ( p_title->i_startlb < i_block ) )
{
p_newtitle = p_title;
p_title = p_title->p_next;
}
/* Save the found title */
p_title = p_newtitle;
/* Write in the new title and its key */
p_newtitle = malloc( sizeof( dvd_title_t ) );
p_newtitle->i_startlb = i_block;
memcpy( p_newtitle->p_key, p_title_key, KEY_SIZE );
/* Link it at the head of the (possibly empty) list */
if( p_title == NULL )
{
p_newtitle->p_next = dvdcss->p_titles;
dvdcss->p_titles = p_newtitle;
}
/* Link the new title inside the list */
else
{
p_newtitle->p_next = p_title->p_next;
p_title->p_next = p_newtitle;
}
memcpy( dvdcss->css.p_title_key, p_title_key, KEY_SIZE );
return 0;
}
/*****************************************************************************
* _dvdcss_disckey: get disc key.
*****************************************************************************
* This function should only be called if DVD ioctls are present.
* It will set dvdcss->i_method = DVDCSS_METHOD_TITLE if it fails to find
* a valid disc key.
* Two decryption methods are offered:
* -disc key hash crack,
* -decryption with player keys if they are available.
*****************************************************************************/
int _dvdcss_disckey( dvdcss_t dvdcss )
{
unsigned char p_buffer[ DVD_DISCKEY_SIZE ];
dvd_key_t p_disc_key;
int i;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -