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;
}