DRM FileOpen / PDF ================== FileOpen est la solution de DRM utilisée dans les fichiers PDF de l'AFNOR pour contourner l'obligation de publication des normes. Le seul moyen pour ouvrir les fichiers est l'utilisation du lecteur Acrobat de Adobe avec un plugin `FileOpen `_ (disponible uniquement sous plateforme x86). Une première analyse montre que le plugin reçoit une clé (paramètre http ``Code``). Voila un exemple de requête reçu: :: RetVal=1&ServId=btq_afnor&DocuId=&Ident3ID=NORBJ&Ident4ID=&Code=NORBJ&Perms=1 Ici, ``RetVal=1`` indique que le serveur a accepté la requête, ``Perms`` renvoie les permissions acceptées (`e.g` impression, copie, lecture, etc.) et ``Code`` renvoie la clé de chiffrement. La clé de chiffrement fonctionne exactement comme la clé de chiffrement de la protection standard de Adobe (la `file key` de 5 octets). Sauf qu'on n'a pas besoin d'algorithme compliqué pour la genérer, elle est obtenue directement depuis le serveur. Il semble que ``DocuId``, ``Ident3ID`` et ``Ident4ID`` soient générés à partir de valeurs contenus dans le document: * ``DocuId`` est dans la clé ``DUID`` du dictionnaire de chiffrement (``/Filter/FOPN_foweb``) * ``Ident4ID`` est la première partie de ``DocuID`` (jusqu'au ``'.'``) Dans le cas de l'`AFNOR `_, il semble que la clé soit toujours égale à ``'NORBJ'``. Ressources ---------- * `Chiffrement standard PDF `_ * `Un peu de doc sur FileOpen `_ * `AFNOR FAIL `_ Hack for poppler ---------------- .. highlight:: diff Le patch suivant code en dur la clé utilisée par l'AFNOR afin de pouvoir lire les normes obligatoires avec les logiciels basés sur `poppler `_: :: diff --git a/poppler/Decrypt.cc b/poppler/Decrypt.cc index ca294d3..2f6aff2 100644 --- a/poppler/Decrypt.cc +++ b/poppler/Decrypt.cc @@ -57,6 +57,8 @@ GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength, GooString *ownerPassword, GooString *userPassword, Guchar *fileKey, GBool encryptMetadata, GBool *ownerPasswordOk) { + +#if 0 Guchar test[32], test2[32]; GooString *userPassword2; Guchar fState[256]; @@ -114,6 +116,8 @@ GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength, return makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey, permissions, fileID, userPassword, fileKey, encryptMetadata); +#endif + return gTrue; } GBool Decrypt::makeFileKey2(int encVersion, int encRevision, int keyLength, diff --git a/poppler/SecurityHandler.cc b/poppler/SecurityHandler.cc index ea91e21..bde22f6 100644 --- a/poppler/SecurityHandler.cc +++ b/poppler/SecurityHandler.cc @@ -39,7 +39,8 @@ SecurityHandler *SecurityHandler::make(PDFDoc *docA, Object *encryptDictA) { #endif encryptDictA->dictLookup("Filter", &filterObj); - if (filterObj.isName("Standard")) { + //if (filterObj.isName("Standard")) { + if (filterObj.isName("FOPN_foweb")) { secHdlr = new StandardSecurityHandler(docA, encryptDictA); } else if (filterObj.isName()) { #ifdef ENABLE_PLUGINS @@ -127,6 +128,13 @@ StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA, Object *encryptDictA): SecurityHandler(docA) { + ok = gTrue; + fileID = NULL; + ownerKey = NULL; + userKey = NULL; + encAlgorithm = cryptRC4; + fileKeyLength = 5; +#if 0 Object versionObj, revisionObj, lengthObj; Object ownerKeyObj, userKeyObj, permObj, fileIDObj; Object fileIDObj1; @@ -238,6 +246,8 @@ StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA, lengthObj.free(); revisionObj.free(); versionObj.free(); +#endif + } StandardSecurityHandler::~StandardSecurityHandler() { @@ -261,6 +271,7 @@ void *StandardSecurityHandler::makeAuthData(GooString *ownerPassword, } void *StandardSecurityHandler::getAuthData() { +#if 0 #if HAVE_XPDFCORE XPDFCore *core; GooString *password; @@ -282,6 +293,8 @@ void *StandardSecurityHandler::getAuthData() { #else return NULL; #endif +#endif + return NULL; } void StandardSecurityHandler::freeAuthData(void *authData) { @@ -301,12 +314,17 @@ GBool StandardSecurityHandler::authorize(void *authData) { ownerPassword = NULL; userPassword = NULL; } - if (!Decrypt::makeFileKey(encVersion, encRevision, fileKeyLength, - ownerKey, userKey, permFlags, fileID, - ownerPassword, userPassword, fileKey, - encryptMetadata, &ownerPasswordOk)) { - return gFalse; - } + fileKey[0] = 'N'; + fileKey[1] = 'O'; + fileKey[2] = 'R'; + fileKey[3] = 'B'; + fileKey[4] = 'J'; + //if (!Decrypt::makeFileKey(encVersion, encRevision, fileKeyLength, + // ownerKey, userKey, permFlags, fileID, + // ownerPassword, userPassword, fileKey, + // encryptMetadata, &ownerPasswordOk)) { + // return gFalse; + //} return gTrue; } Voici un portage du patch fonctionnant avec `mupdf `_, qui est la bibliothèque utilisé par `sumatrapdf `_. :: diff -rN -u old-mupdf/mupdf/pdf_crypt.c new-mupdf/mupdf/pdf_crypt.c --- old-mupdf/mupdf/pdf_crypt.c 2009-12-09 16:17:18.000000000 +0100 +++ new-mupdf/mupdf/pdf_crypt.c 2009-12-09 16:17:18.000000000 +0100 @@ -27,7 +27,7 @@ pdf_freecrypt(crypt); return fz_throw("unspecified encryption handler"); } - if (strcmp(fz_toname(obj), "Standard") != 0) + if (strcmp(fz_toname(obj), "FOPN_foweb") != 0) { pdf_freecrypt(crypt); return fz_throw("unknown encryption handler: '%s'", fz_toname(obj)); @@ -37,13 +37,20 @@ obj = fz_dictgets(dict, "V"); if (fz_isint(obj)) crypt->v = fz_toint(obj); +#if 0 if (crypt->v != 1 && crypt->v != 2 && crypt->v != 4) { pdf_freecrypt(crypt); return fz_throw("unknown encryption version"); } +#endif crypt->length = 40; + crypt->stmf.method = PDF_CRYPT_RC4; + crypt->stmf.length = crypt->length; + crypt->strf.method = PDF_CRYPT_RC4; + crypt->strf.length = crypt->length; +#if 0 if (crypt->v == 2 || crypt->v == 4) { obj = fz_dictgets(dict, "Length"); @@ -161,6 +168,7 @@ pdf_freecrypt(crypt); return fz_throw("encryption dictionary missing permissions value"); } +#endif crypt->encryptmetadata = 1; obj = fz_dictgets(dict, "EncryptMetadata"); @@ -172,7 +180,7 @@ */ crypt->idlength = 0; - +#if 0 if (fz_isarray(id) && fz_arraylen(id) == 2) { obj = fz_arrayget(id, 0); @@ -187,6 +195,13 @@ } else fz_warn("missing file identifier, may not be able to do decryption"); +#endif + + crypt->key[0] = 'N'; + crypt->key[1] = 'O'; + crypt->key[2] = 'R'; + crypt->key[3] = 'B'; + crypt->key[4] = 'J'; #if 0 { @@ -461,6 +476,7 @@ int pdf_authenticatepassword(pdf_xref *xref, char *password) { +#if 0 if (xref->crypt) { if (pdf_authenticateuserpassword(xref->crypt, (unsigned char *)password, strlen(password))) @@ -469,6 +485,7 @@ return 1; return 0; } +#endif return 1; }