[libdvdcss-devel] [Git][videolan/libdvdcss][master] 11 commits: CPRM: adjust cpxm struct member naming
Jean-Baptiste Kempf (@jbk)
gitlab at videolan.org
Sun Jan 18 23:02:46 UTC 2026
Jean-Baptiste Kempf pushed to branch master at VideoLAN / libdvdcss
Commits:
e72928cd by Saifelden Mohamed Ismail at 2026-01-16T23:16:53+02:00
CPRM: adjust cpxm struct member naming
- - - - -
290f9b5b by Saifelden Mohamed Ismail at 2026-01-16T23:16:53+02:00
CPRM: add apstb to cpxm struct
- - - - -
7db494df by Saifelden Mohamed Ismail at 2026-01-16T23:16:53+02:00
CPRM: remove unused vr_get_k_t
- - - - -
b5d13027 by Saifelden Mohamed Ismail at 2026-01-16T23:16:53+02:00
CPXM: use calloc for cpxm struct
- - - - -
871ecbfb by Saifelden Mohamed Ismail at 2026-01-16T23:35:24+02:00
CPRM: add initialization
- - - - -
81297e5c by Saifelden Mohamed Ismail at 2026-01-17T13:06:04+02:00
CPRM: fix decryption function
- - - - -
9bcf7a12 by Saifelden Mohamed Ismail at 2026-01-17T13:06:06+02:00
nitpick: remove unused psz_file[PATH_MAX]
- - - - -
198e61b6 by Saifelden Mohamed Ismail at 2026-01-17T13:06:06+02:00
CPRM: update header doc for dvdcpxm_init
- - - - -
f6191520 by Saifelden Mohamed Ismail at 2026-01-17T13:06:06+02:00
CPRM: fix dvdcpxm_seek unused variable warning
- - - - -
103b58a7 by Saifelden Mohamed Ismail at 2026-01-17T13:06:06+02:00
API: add dvdcss_get_encryption_type
build: update soname and version
- - - - -
2682a4a7 by Saifelden Mohamed Ismail at 2026-01-17T13:10:41+02:00
update NEWS to 1.6.0
- - - - -
7 changed files:
- NEWS
- meson.build
- src/cpxm.h
- src/dvdcss/dvdcpxm.h
- src/dvdcss/dvdcss.h
- src/libdvdcpxm.c
- src/libdvdcss.c
Changes:
=====================================
NEWS
=====================================
@@ -1,3 +1,15 @@
+--------------------------------
+Changes between 1.5.0 and 1.6.0:
+--------------------------------
+
+ * Add dvdcss_get_encryption_type function to the public API
+ This allows applications to query the disc's copyright protection system
+ type (CPST) directly from the drive, enabling better distinction between
+ CSS/CPPM and CPRM protected media during initialization.
+ * Added support for CPRM (Content Protection for Recordable Media) decryption.
+ * Updated dvdcpxm_setup to allow context reuse via NULL arguments and to accept a raw CPRM key in place of the MKB.
+
+
Changes between 1.4.3 and 1.5.0:
--------------------------------
=====================================
meson.build
=====================================
@@ -1,5 +1,5 @@
project('libdvdcss', 'c',
- version: '1.5.0',
+ version: '1.6.0',
meson_version: '>= 0.60.0',
default_options: ['buildtype=debugoptimized',
'c_std=c17',
@@ -17,7 +17,7 @@ cdata = configuration_data()
dvdcss_inc_dirs = include_directories('.', 'src', 'src/dvdcss')
# The version number for the shared library
-dvdcss_soname_version = '2.3.0'
+dvdcss_soname_version = '2.4.0'
dvdcss_version = meson.project_version()
dvdcss_version_split = dvdcss_version.split('.')
=====================================
src/cpxm.h
=====================================
@@ -50,7 +50,8 @@ typedef struct cpxm
uint64_t media_key;
uint64_t id_album;
uint64_t id_media;
- uint64_t vr_k_te;
+ uint64_t vr_k_t;
+ uint64_t apstb;
} cpxm_s;
/* for persistance */
=====================================
src/dvdcss/dvdcpxm.h
=====================================
@@ -67,7 +67,9 @@ extern "C" {
*/
/* Called after the DVD is opened to initialize the cpxm struct, Must be run after dvdcss_open */
-LIBDVDCSS_EXPORT int dvdcpxm_init(dvdcss_t dvdcss, uint8_t *p_mkb);
+/* in the case of DVD-Audio, the MKB must be given as input,
+ * in the case of DVD-VR, the title key should be given as input */
+LIBDVDCSS_EXPORT int dvdcpxm_init(dvdcss_t dvdcss, uint8_t *p_input);
/* Same as dvdcss_close but frees the cpxm struct as well */
LIBDVDCSS_EXPORT int dvdcpxm_close ( dvdcss_t dvdcss );
=====================================
src/dvdcss/dvdcss.h
=====================================
@@ -106,6 +106,8 @@ LIBDVDCSS_EXPORT const char *dvdcss_error ( const dvdcss_t );
LIBDVDCSS_EXPORT int dvdcss_is_scrambled ( dvdcss_t );
+LIBDVDCSS_EXPORT int dvdcss_get_encryption_type ( dvdcss_t );
+
#ifdef __cplusplus
}
#endif
=====================================
src/libdvdcpxm.c
=====================================
@@ -495,43 +495,14 @@ int process_mkb( uint8_t *p_mkb, device_key_t *p_dev_keys, int nr_dev_keys, uint
return -1;
}
-int vr_get_k_te( p_cpxm cpxm,char *psz_vr_mangr )
-{
- FILE *f_vr_m;
- uint8_t vmgi_mat[512];
- int ret = -1;
-
- f_vr_m = fopen( psz_vr_mangr, "rb" );
-
- if ( !f_vr_m )
- return -1;
-
- if ( fread(vmgi_mat, 1, 512, f_vr_m) == 512 )
- {
- if ( memcmp( vmgi_mat, "DVD_RTR_VMG0", 12 ) == 0 )
- {
- cpxm->vr_k_te = 0;
- if ( vmgi_mat[267] & 1 ) /* Check encrypted title key status bit */
- {
- memcpy( &cpxm->vr_k_te, &vmgi_mat[268], sizeof( cpxm->vr_k_te ) );
- B2N_64( cpxm->vr_k_te );
- }
- ret = 0;
- }
- }
-
- fclose( f_vr_m );
- return ret;
-}
-
/* Function should be called on a dvdcss var to set cppm struct which needs
* to persist in order to decrypt the media */
-LIBDVDCSS_EXPORT int dvdcpxm_init( dvdcss_t dvdcss, uint8_t *p_mkb )
+LIBDVDCSS_EXPORT int dvdcpxm_init( dvdcss_t dvdcss, uint8_t *p_input )
{
/* In the case that p_mkb is received as null, then either you were unable
* to read the mkb or the encryption type is cprm */
/* if no file mkb file is passed, check cache */
- if (!p_mkb)
+ if (!p_input)
{
cpxm_cache *cpxm_iterator = g_cpxm_cache;
struct stat file_stat;
@@ -550,15 +521,16 @@ LIBDVDCSS_EXPORT int dvdcpxm_init( dvdcss_t dvdcss, uint8_t *p_mkb )
return -1;
}
- p_cpxm cpxm = malloc(sizeof(cpxm_s));
+ p_cpxm cpxm = calloc(1, sizeof(cpxm_s));
if (!cpxm)
return -1;
dvdcss->cpxm = cpxm;
- char psz_file[PATH_MAX];
int ret = -1;
+ uint8_t *p_mkb;
+
c2_init();
switch ( dvdcss->media_type )
{
@@ -566,6 +538,8 @@ LIBDVDCSS_EXPORT int dvdcpxm_init( dvdcss_t dvdcss, uint8_t *p_mkb )
ret = 0;
break;
case COPYRIGHT_PROTECTION_CPPM:
+ /* the input is the media key block */
+ p_mkb = p_input;
if ( cppm_set_id_album( dvdcss ) == 0 )
{
if ( p_mkb )
@@ -590,15 +564,19 @@ LIBDVDCSS_EXPORT int dvdcpxm_init( dvdcss_t dvdcss, uint8_t *p_mkb )
free( p_mkb );
if (ret) break;
}
- snprintf( psz_file, PATH_MAX, "%s/DVD_RTAV/VR_MANGR.IFO", dvdcss->psz_device );
- ret = vr_get_k_te( dvdcss->cpxm,psz_file );
- if (ret)
- {
- snprintf( psz_file, PATH_MAX, "%s/DVD_RTAV/VR_MANGR.BUP", dvdcss->psz_device );
- ret = vr_get_k_te( dvdcss->cpxm,psz_file );
- if (ret) break;
- }
+
+ /* get the media unique key */
+ uint64_t k_mu = c2_g( cpxm->media_key, cpxm->id_media ) & 0x00ffffffffffffff;
+
+ /* decrypt the encrypted title key */
+ uint64_t k_te;
+ READ64_BE( k_te , p_input );
+ uint64_t k_t = c2_dec( k_mu, k_te ) & 0x00ffffffffffffff;
+
+ /* store decrypted title key for vr decryption */
+ cpxm->vr_k_t = k_t;
+
}
break;
}
@@ -731,34 +709,48 @@ int cppm_decrypt_block( uint8_t *p_buffer, int flags, uint64_t id_album, uint64_
return encrypted;
}
-int cprm_decrypt_block( uint8_t *p_buffer, int flags, uint64_t id_album, uint64_t media_key )
+/*
+ * check if MPEG Video headers exist
+ * Returns:
+ * 1 = Decryption successful
+ * 0 = Decryption failed
+ */
+int is_valid_mpeg_payload(uint8_t *buffer) {
+ for (size_t i = 0; i < DVDCPXM_BLOCK_SIZE - 4; i++) {
+ /* Look for the Start Code Prefix (00 00 01) */
+ if (buffer[i] == 0x00 && buffer[i+1] == 0x00 && buffer[i+2] == 0x01) {
+ uint8_t code = buffer[i+3];
+ if (code == 0xB3) return 1; // Sequence Header
+ if (code == 0xB8) return 1; // GOP Header
+ if (code == 0x00) return 1; // Picture Header
+ }
+ }
+ return 0;
+}
+
+int cprm_decrypt_block( uint8_t *p_buffer, int flags, uint64_t vr_k_t, uint64_t apstb )
{
- uint64_t d_kc_i, k_au, k_i, k_c;
+ uint64_t d_tkc, k_i, k_c;
int encrypted;
encrypted = 0;
if ( mpeg2_check_pes_scrambling_control( p_buffer ) )
{
- k_au = c2_g( id_album, media_key ) & 0x00ffffffffffffff;
-
- READ64_BE( d_kc_i, &p_buffer[24] );
- k_i = c2_g( d_kc_i, k_au ) & 0x00ffffffffffffff;
-
- READ64_BE( d_kc_i, &p_buffer[32] );
- k_i = c2_g( d_kc_i, k_i ) & 0x00ffffffffffffff;
+ /* Add the CPRM_CI byte (or APSTB bits) to the title key to get K_i.
+ * We treat this as a full byte (0-255) to support both DVD-VR and DVD-Video. */
+ k_i = vr_k_t + ( apstb );
- READ64_BE( d_kc_i, &p_buffer[40] );
- k_i = c2_g( d_kc_i, k_i ) & 0x00ffffffffffffff;
-
- READ64_BE( d_kc_i, &p_buffer[48] );
- k_i = c2_g( d_kc_i, k_i ) & 0x00ffffffffffffff;
-
- READ64_BE( d_kc_i, &p_buffer[84] );
- k_c = c2_g( d_kc_i, k_i ) & 0x00ffffffffffffff;
+ /* read the Title Key Conversion Data */
+ READ64_BE( d_tkc , &p_buffer[84] );
+ k_c = c2_g( k_i, d_tkc ) & 0x00ffffffffffffff;
c2_dcbc( &p_buffer[DVDCPXM_BLOCK_SIZE - DVDCPXM_ENCRYPTED_SIZE], k_c, DVDCPXM_ENCRYPTED_SIZE );
mpeg2_reset_pes_scrambling_control( p_buffer );
- encrypted = 1;
+ /* check if decryption failed */
+ if ( is_valid_mpeg_payload( p_buffer ) )
+ encrypted = 1;
+ else
+ encrypted = -1;
}
if ( ( flags & DVDCPXM_PRESERVE_CCI ) != DVDCPXM_PRESERVE_CCI )
@@ -774,7 +766,44 @@ int dvdcpxm_decrypt( p_cpxm cpxm, int media_type,void *p_buffer, int flags )
case COPYRIGHT_PROTECTION_CPPM:
return cppm_decrypt_block( (uint8_t *) p_buffer, flags, cpxm->id_album, cpxm->media_key );
case COPYRIGHT_PROTECTION_CPRM:
- return cprm_decrypt_block( (uint8_t* ) p_buffer, flags, cpxm->id_album ,cpxm->media_key );
+ {
+ /* return early if there is no encryption to avoid allocating 2kb */
+ if ( !mpeg2_check_pes_scrambling_control( p_buffer ) )
+ return cprm_decrypt_block( (uint8_t* ) p_buffer, flags, cpxm->vr_k_t, cpxm->apstb );
+
+ /* we are not sure if apstb is correct, so we operate on a copied buffer first */
+ uint8_t temp_buffer[DVDCPXM_BLOCK_SIZE];
+
+ /* For DVD-VR we only need to check the first 4 possible values since apstb is 2 bits
+ * for DVD-Video with CPRM we need to check more, since CPRM_CI is more bits */
+ uint64_t nr_possible_values = 256;
+
+ /* assume the retained value is initially correct */
+ memcpy(temp_buffer, p_buffer, DVDCPXM_BLOCK_SIZE);
+ int result = cprm_decrypt_block( temp_buffer, flags, cpxm->vr_k_t, cpxm->apstb );
+
+ if ( result == 1 ) {
+ memcpy(p_buffer, temp_buffer, DVDCPXM_BLOCK_SIZE);
+ return result;
+ }
+
+ /* our old value must not have been correct, we must guess */
+ for ( uint64_t guess = 0; guess < nr_possible_values ; guess++ ) {
+
+ /* skip if we already checked this above */
+ if ( guess == cpxm->apstb ) continue;
+
+ /* try our value */
+ memcpy(temp_buffer, p_buffer, DVDCPXM_BLOCK_SIZE);
+ result = cprm_decrypt_block( temp_buffer, flags, cpxm->vr_k_t, guess );
+ if ( result == 1 ) {
+ memcpy(p_buffer, temp_buffer, DVDCPXM_BLOCK_SIZE);
+ cpxm->apstb = guess;
+ return result;
+ }
+ }
+
+ }
}
return 0;
@@ -855,6 +884,7 @@ int dvdcpxm_read ( dvdcss_t dvdcss, void *p_buffer,
int dvdcpxm_seek ( dvdcss_t dvdcss, int i_blocks, int i_flags )
{
+ (void)i_flags;
return dvdcss_seek( dvdcss, i_blocks, DVDCSS_NOFLAGS );
}
=====================================
src/libdvdcss.c
=====================================
@@ -872,3 +872,13 @@ LIBDVDCSS_EXPORT int dvdcss_is_scrambled ( dvdcss_t dvdcss )
{
return dvdcss->b_scrambled;
}
+
+/**
+ * \brief Return the type of encryption
+ *
+ * \return 0 if the disc is unencrypted, 1 if cppm or css, 2 if cprm
+ */
+LIBDVDCSS_EXPORT int dvdcss_get_encryption_type ( dvdcss_t dvdcss )
+{
+ return dvdcss->media_type;
+}
View it on GitLab: https://code.videolan.org/videolan/libdvdcss/-/compare/a69b73ea32857043ba9911e3092906c18676d031...2682a4a7ed782e700a5b920f6f85c4f9736921c3
--
View it on GitLab: https://code.videolan.org/videolan/libdvdcss/-/compare/a69b73ea32857043ba9911e3092906c18676d031...2682a4a7ed782e700a5b920f6f85c4f9736921c3
You're receiving this email because of your account on code.videolan.org.
VideoLAN code repository instance
More information about the libdvdcss-devel
mailing list