Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

chiffrement AES et gonflement du fichier d'origine

8 réponses
Avatar
Kevin Denis
Bonjour,

j'utilise openSSL pour faire quelques tests de chiffrement. Un point
m'étonne, concernant le salage:

$ dd if=/dev/urandom of=512octets bs=512 count=1
$ openssl enc -aes-128-cbc -nosalt -in 512octets -pass pass:word -out nosalt1
$ openssl enc -aes-128-cbc -nosalt -in 512octets -pass pass:word -out nosalt2
$ openssl enc -aes-128-cbc -nosalt -in 512octets -pass pass:word -out nosalt3
$ openssl enc -aes-128-cbc -salt -in 512octets -pass pass:word -out salt1
$ openssl enc -aes-128-cbc -salt -in 512octets -pass pass:word -out salt2
$ openssl enc -aes-128-cbc -salt -in 512octets -pass pass:word -out salt3
$ sha256sum *salt*
0abe59946b9ba78bc2a623621268f9f23ddc6d3a30d6b16a5395fbb0718700f2 nosalt1
0abe59946b9ba78bc2a623621268f9f23ddc6d3a30d6b16a5395fbb0718700f2 nosalt2
0abe59946b9ba78bc2a623621268f9f23ddc6d3a30d6b16a5395fbb0718700f2 nosalt3
b64df3b8f17c9d5dc5045e10c725d3b7fefae052451e4c6620b34a48f89d520d salt1
5be23d8e04ecfcf2f7391a030896d7cb570237b71ff6888707806a6af8aa2883 salt2
74bb1e04e0cc8f6179490aa6d60fc2cef91aee8dd8c125de518450a69762b5e1 salt3

Jusque là, normal. Le chiffrement non salé donne toujours le même
résultat, le chiffrement salé change.

Mais:
$ ls -l *salt*
-rw-r--r-- 1 kevin users 528 2010-11-08 12:36 nosalt1
-rw-r--r-- 1 kevin users 528 2010-11-08 12:36 nosalt2
-rw-r--r-- 1 kevin users 528 2010-11-08 12:36 nosalt3
-rw-r--r-- 1 kevin users 544 2010-11-08 12:36 salt1
-rw-r--r-- 1 kevin users 544 2010-11-08 12:36 salt2
-rw-r--r-- 1 kevin users 544 2010-11-08 12:36 salt3

J'aurai pensé que le chiffrement non salé conservait la taille
d'origine du fichier. Il n'en est pas le cas. (le fichier salé est plus
gros que le fichier non salé, ce qui me semble normal).

Quels sont ces 16 octets en plus?
Merci
--
Kevin

8 réponses

Avatar
Francois Grieu
Le 08/11/2010 12:39, Kevin Denis a écrit :
Bonjour,

j'utilise openSSL pour faire quelques tests de chiffrement. Un point
m'étonne, concernant le salage:

$ dd if=/dev/urandom ofQ2octets bsQ2 count=1
$ openssl enc -aes-128-cbc -nosalt -in 512octets -pass pass:word -out nosalt1
$ openssl enc -aes-128-cbc -nosalt -in 512octets -pass pass:word -out nosalt2
$ openssl enc -aes-128-cbc -nosalt -in 512octets -pass pass:word -out nosalt3
$ openssl enc -aes-128-cbc -salt -in 512octets -pass pass:word -out salt1
$ openssl enc -aes-128-cbc -salt -in 512octets -pass pass:word -out salt2
$ openssl enc -aes-128-cbc -salt -in 512octets -pass pass:word -out salt3
$ sha256sum *salt*
0abe59946b9ba78bc2a623621268f9f23ddc6d3a30d6b16a5395fbb0718700f2 nosalt1
0abe59946b9ba78bc2a623621268f9f23ddc6d3a30d6b16a5395fbb0718700f2 nosalt2
0abe59946b9ba78bc2a623621268f9f23ddc6d3a30d6b16a5395fbb0718700f2 nosalt3
b64df3b8f17c9d5dc5045e10c725d3b7fefae052451e4c6620b34a48f89d520d salt1
5be23d8e04ecfcf2f7391a030896d7cb570237b71ff6888707806a6af8aa2883 salt2
74bb1e04e0cc8f6179490aa6d60fc2cef91aee8dd8c125de518450a69762b5e1 salt3

Jusque là, normal. Le chiffrement non salé donne toujours le même
résultat, le chiffrement salé change.

Mais:
$ ls -l *salt*
-rw-r--r-- 1 kevin users 528 2010-11-08 12:36 nosalt1
-rw-r--r-- 1 kevin users 528 2010-11-08 12:36 nosalt2
-rw-r--r-- 1 kevin users 528 2010-11-08 12:36 nosalt3
-rw-r--r-- 1 kevin users 544 2010-11-08 12:36 salt1
-rw-r--r-- 1 kevin users 544 2010-11-08 12:36 salt2
-rw-r--r-- 1 kevin users 544 2010-11-08 12:36 salt3

J'aurai pensé que le chiffrement non salé conservait la taille
d'origine du fichier. Il n'en est pas le cas. (le fichier salé est plus
gros que le fichier non salé, ce qui me semble normal).

Quels sont ces 16 octets en plus?
Merci



Je conjecture que openssl enc -aes-128-cbc -nosalt fait en sorte
que chiffrer puis déchiffrer n'importe quel fichier le laisse
inchangé, y compris sa longueur.
Ceci rend inévitable d'allonger un peu la longueur de certains
fichiers: AES opérant par blocs de 16 octets, les fichiers de
1 à 15 octets chiffrés avec aes-128-cbc font inévitablement
16 octets au moins, ce qui ne laisse plus assez de fichiers
chiffrés de 16 octets pour que les fichiers de 16 octets chiffrés
fassent 16 octets.
La technique la plus courante est ISO/IEC 9797-1 Padding Method 2:
on ajoute un octet à 0x80 puis d'autant d'octet à 0x00 qu'il faut
pour atteindre un multiple la taille de bloc. Ce qui dans le
contexte fait que chiffrer n octets produit n+16-(n%15) octets.

Je ne sais pas quelle méthode utilise OpenSSL, il y a l'embarras
du choix
http://en.wikipedia.org/wiki/Padding_%28cryptography%29#Padding_methods
http://fr.wikipedia.org/wiki/Remplissage_%28cryptographie%29

François Grieu
Avatar
Thomas Pornin
According to Kevin Denis :
J'aurai pensé que le chiffrement non salé conservait la taille
d'origine du fichier. Il n'en est pas le cas.



AES est un algorithme de chiffrement par blocs. Il transforme des
blocs de 16 octets en d'autres blocs de 16 octets. Le mode CBC suppose
que l'entrée a une taille multiple de 16. Comme le mode "enc" accepte
des entrées de taille arbitraire, OpenSSL est bien obligé d'appliquer
ce qu'on appelle du "padding" : rajouter des octets au bout, de telle
sorte que :
1. la taille après padding soit multiple de 16
2. au déchiffrement, on puisse enlever sans ambiguité le padding

En réfléchissant un peu, on peut voir qu'il est mathématiquement
impossible de réaliser ces deux propriétés sans devoir étendre
_systématiquement_ la taille. Autrement dit, si la taille d'entrée est
déjà multiple de 16, alors il va falloir aller au multiple de 16
suivant. C'est ce que fait le padding PKCS#5 : ça rajoute n octets
(n >= 1) qui ont la valeur numérique "n". Ainsi, au déchiffrement,
on peut regarder le dernier octet obtenu, et ainsi savoir combien
d'octets sont du padding et doivent être enlevés.

Il existe de nombreuses variantes sur ce principe, et, par ailleurs,
comme c'est de la cryptographie et non des mathématiques, on a parfois
des ruses applicables (il y en a une qui s'appelle CTS, comme
"ciphertext stealing", qui permet de faire ça). Il y a aussi d'autres
modes de chiffrement, distinct de CBC (par exemple CTR) qui n'ont pas
besoin d'une entrée de taille multiple de 16.

En bref, pour enlever des 16 octets supplémentaires, ajoutez l'option
"-nopad", mais il faudra alors que l'entrée ait toujours une taille
multiple de 16.


Il convient de noter que :

1. Ce que fait OpenSSL avec la commande "openssl enc" n'est compatible
qu'avec OpenSSL, et encore, pas toutes les versions.

2. Utiliser un mot de passe sans "salt" est un risque de sécurité. Cela
permet d'optimiser une recherche sur le mot de passe (dans le sens
suivant : l'attaquant devra se payer le dictionnaire, mais il
pourra le faire _une_ fois, et attaquer ensuite de nombreux messages
chiffrés avec des mots de passes différents).

3. Le mode CBC nécessite une IV. OpenSSL dérive cette IV du mot de
passe. Cela veut dire qu'en mode "nosalt", si on utilise le même
mot de passe deux fois, on va aussi utiliser la même IV. C'est
_aussi_ un problème de sécurité avec CBC (pas le même que le point 2).


--Thomas Pornin
Avatar
Thomas Pornin
According to Francois Grieu :
La technique la plus courante est ISO/IEC 9797-1 Padding Method 2:



Ça doit dépendre des domaines. Dans le mien, c'est PKCS#5 qui est
utilisée le plus souvent (et, d'après le manuel d'OpenSSL, c'est ce
qu'OpenSSL applique en l'occurrence). L'effet est le même : au moins
un octet en plus, et on atteint le multiple de 16 suivant.


--Thomas Pornin
Avatar
Kevin Denis
Le 08-11-2010, Thomas Pornin a écrit :
En bref, pour enlever des 16 octets supplémentaires, ajoutez l'option
"-nopad", mais il faudra alors que l'entrée ait toujours une taille
multiple de 16.



Ok, ça fonctionne. Je travaille avec des multiples de 512 qui sont bien
évidemment aussi multiples de 16.

Il convient de noter que :

1. Ce que fait OpenSSL avec la commande "openssl enc" n'est compatible
qu'avec OpenSSL, et encore, pas toutes les versions.



Je pensais qu'openssl était standard, dans le sens ou un (dé)chiffrement
effectué par lui est utilisable par n'importe quel autre programme (?)

2. Utiliser un mot de passe sans "salt" est un risque de sécurité. Cela
permet d'optimiser une recherche sur le mot de passe (dans le sens
suivant : l'attaquant devra se payer le dictionnaire, mais il
pourra le faire _une_ fois, et attaquer ensuite de nombreux messages
chiffrés avec des mots de passes différents).



Comment fonctionnent alors les systèmes de Full Disk Encryption? Ils
ont un espace chiffré égal à l'espace disque (moins quelques octest
négligeables d'en-tête). Et si le chiffré est égal en taille au clair,
alors pas de salt? Ce qui signifierait donc une diminution de la
sécurité. Mais si on ajoute du salt, alors l'espace disque "chiffrable"
est bien plus petit que l'espace disque réel. Comment est résolu ce
problème?

3. Le mode CBC nécessite une IV. OpenSSL dérive cette IV du mot de
passe. Cela veut dire qu'en mode "nosalt", si on utilise le même
mot de passe deux fois, on va aussi utiliser la même IV. C'est
_aussi_ un problème de sécurité avec CBC (pas le même que le point 2).



Je spécifie moi même l'iv en fait, dans mes tests.

Merci
--
Kevin
Avatar
Thomas Pornin
According to Kevin Denis :
Je pensais qu'openssl était standard, dans le sens ou un (dé)chiffrement
effectué par lui est utilisable par n'importe quel autre programme (?)



L'implémentation d'AES est standard : pour une même clé de 128 bits, un
bloc de 16 octets sera transformé dans le même bloc chiffré que ce que
produit n'importe quelle autre implémentation de l'AES.

Mais de l'AES à un chiffrement de message par mot de passe, il y a un
pas. Ce que fait "openssl enc" est une tambouille spécifique à OpenSSL
et qui n'est décrite nulle part ailleurs que dans le code source
d'OpenSSL.

Il faut dire que les standards de chiffrement de messages sont un peu
plus "haut niveau" que ça. Par exemple, S/MIME est un standard de
chiffrement et signature d'emails. S/MIME utilise CMS, dérivé de PKCS#7.
OpenSSL a une implémentation de S/MIME ("openssl smime").


Comment fonctionnent alors les systèmes de Full Disk Encryption?



Une salt est un élément de sécurité important dans la transformation
d'un mot de passe en une clé. Pour être précis, un "mot de passe" est
une information confidentielle qui peut rentrer dans le cerveau d'un
humain, et est donc vulnérable aux recherches exhaustives, parce que les
cerveaux humains sont comme ça, ils ne sont pas très doués pour la
mémorisation. La salt est un mécanisme qui s'assure que si un mot de
passe donné est attaquable avec un certain coût, l'attaquant devra payer
ce coût pour _chaque_ mot de passe attaqué, sans possibilité de partage
de l'effort.

Un système de chiffrement de disque dur, même s'il est protégé par un
mot de passe, va d'abord se fabriquer une unique clé maître, de grande
taille, qui servira pour tout le disque. Comme on souhaite en général
pouvoir changer le mot de passe, la technique usuelle est de choisir une
grosse clé maître aléatoire, et de stocker cette clé dans un entête,
chiffrée symétriquement avec une clé dérivée du mot de passe (c'est là
et seulement là qu'il y a une salt).

Pour le reste, il faut encore se préoccuper des IV. Si on considère le
disque entier comme un unique gros message en mode CBC, alors, pour
déchiffrer un secteur, il faut lire ce secteur _et_ les derniers 16
octets du secteur précédent. C'est un peu pénalisant du point de vue
performances. Une autre méthode est de considérer chaque secteur (de 512
octets) comme un message indépendant, et d'utiliser comme IV une valeur
dérivée de la clé et du numéro de secteur (c'est un peu délicat à faire :
on veut des IV réparties uniformément dans l'espace des blocs de 16
octets, et imprédictibles). Une autre méthode encore consisterait à
ne pas utiliser le mode CBC, mais le mode CTR, où le numéro de
secteur donne naturellement les informations nécessaires.

Si on veut en outre du contrôle d'intégrité, alors c'est plus compliqué.
Beaucoup plus.


--Thomas Pornin
Avatar
Kevin Denis
Le 08-11-2010, Thomas Pornin a écrit :
Je pensais qu'openssl était standard, dans le sens ou un (dé)chiffrement
effectué par lui est utilisable par n'importe quel autre programme (?)



L'implémentation d'AES est standard : pour une même clé de 128 bits, un
bloc de 16 octets sera transformé dans le même bloc chiffré que ce que
produit n'importe quelle autre implémentation de l'AES.



Ok, je vais chercher un programme qui ne fasse rien d'autre que de
l'AES.

Mais de l'AES à un chiffrement de message par mot de passe, il y a un
pas. Ce que fait "openssl enc" est une tambouille spécifique à OpenSSL
et qui n'est décrite nulle part ailleurs que dans le code source
d'OpenSSL.



Ok.

Il faut dire que les standards de chiffrement de messages sont un peu
plus "haut niveau" que ça. Par exemple, S/MIME est un standard de
chiffrement et signature d'emails. S/MIME utilise CMS, dérivé de PKCS#7.
OpenSSL a une implémentation de S/MIME ("openssl smime").


Comment fonctionnent alors les systèmes de Full Disk Encryption?





<snip la clé de haut niveau et la master key>

Pour le reste, il faut encore se préoccuper des IV. Si on considère le
disque entier comme un unique gros message en mode CBC, alors, pour
déchiffrer un secteur, il faut lire ce secteur _et_ les derniers 16
octets du secteur précédent. C'est un peu pénalisant du point de vue
performances.



Surtout qu'a priori si je change un des 16 derniers octets du bloc 'n'
je devrais rechiffrer le bloc 'n+1'? Et par cascade, éventuellement le
disque entier?

Une autre méthode est de considérer chaque secteur (de 512
octets) comme un message indépendant, et d'utiliser comme IV une valeur
dérivée de la clé et du numéro de secteur



Apparement c'est ce que je suis en train de faire. La clé que j'utilise
est ce que vous appelez la 'master key' et l'IV est le numéro de
secteur (pour faire simple).

(c'est un peu délicat à faire :
on veut des IV réparties uniformément dans l'espace des blocs de 16
octets, et imprédictibles).



Uniformément réparties, ok. Mais imprédictibles? Il le faut un minimum
pour pouvoir déchiffrer le bloc après coup tout de même?

Une autre méthode encore consisterait à
ne pas utiliser le mode CBC, mais le mode CTR, où le numéro de
secteur donne naturellement les informations nécessaires.



D'accord. Mais ce qui m'ennuie tout de même la dedans, c'est que
la même donnée sur le même secteur avec la même clé donnera toujours
le même chiffré. C'est ce que je voudrais éviter.

Si on veut en outre du contrôle d'intégrité, alors c'est plus compliqué.
Beaucoup plus.



Dans mon idée, je délègue ça au filesystem qui utilisera la partition
chiffrée.
--
Kevin
Avatar
Thomas Pornin
According to Kevin Denis :
Surtout qu'a priori si je change un des 16 derniers octets du bloc 'n'
je devrais rechiffrer le bloc 'n+1'? Et par cascade, éventuellement le
disque entier?



Oui, effectivement.


Uniformément réparties, ok. Mais imprédictibles? Il le faut un minimum
pour pouvoir déchiffrer le bloc après coup tout de même?



Je voulais dire, imprédictibles du point de vue de l'attaquant (ne
pas pouvoir écrire un fichier en clair et savoir dès ce moment quelle
IV sera utilisée).


D'accord. Mais ce qui m'ennuie tout de même la dedans, c'est que
la même donnée sur le même secteur avec la même clé donnera toujours
le même chiffré. C'est ce que je voudrais éviter.



Dans ce cas, les choses sont plus compliquées. Il va falloir dépenser un
peu de place disque. Si on veut qu'un secteur de 512 octets soit chiffré
de façon différente à chaque écriture, alors il va falloir que la
variante chiffrée soit plus grande que 512 octets. 16 octets aléatoires
suffiraient amplement (et ça peut alors prendre la forme d'un
chiffrement CBC, les 16 octets étant l'IV). Dans ce cadre, les secteurs
sont groupés par 33 : 32 secteurs de données, un 33ème pour les IV. La
perte de place est de l'ordre de 3%. Mais la lecture d'un secteur
demande également la lecture du secteur contenant l'IV correspondante,
et l'écriture d'un secteur chiffré exige de réécrire le secteur
contenant l'IV aussi. Il n'est pas clair que le secteur soit la bonne
granularité, surtout que les OS récent sur PC ont tendance à faire les
accès par 4 Ko (comme une page de la MMU) et que les disques récents ont
des secteurs de 4 Ko aussi.


Dans mon idée, je délègue ça au filesystem qui utilisera la partition
chiffrée.



Je ne suis pas persuadé que séparer intégrité et chiffrement soit une
tactique optimale face à un attaquant actif. Le contrôle d'intégrité
d'un disque dur est une chose compliquée parce qu'on veut prendre en
charge des modifications partielles, et la recherche de la performance
pour le chiffrement (par réduction du nombre d'I/O) devrait suivre les
mêmes chemins.

Une référence qui pourrait être une bonne lecture sur le sujet :

http://citeseerx.ist.psu.edu/viewdoc/summary?doi.1.1.23.976


--Thomas Pornin
Avatar
Kevin Denis
Le 09-11-2010, Thomas Pornin a écrit :
Uniformément réparties, ok. Mais imprédictibles? Il le faut un minimum
pour pouvoir déchiffrer le bloc après coup tout de même?



Je voulais dire, imprédictibles du point de vue de l'attaquant (ne
pas pouvoir écrire un fichier en clair et savoir dès ce moment quelle
IV sera utilisée).



Ok. Donc a priori un hash du numéro de secteur et de la master key
doit résoudre le problème (entre autre solutions, j'imagine).

D'accord. Mais ce qui m'ennuie tout de même la dedans, c'est que
la même donnée sur le même secteur avec la même clé donnera toujours
le même chiffré. C'est ce que je voudrais éviter.



Dans ce cas, les choses sont plus compliquées. Il va falloir dépenser un
peu de place disque. Si on veut qu'un secteur de 512 octets soit chiffré
de façon différente à chaque écriture, alors il va falloir que la
variante chiffrée soit plus grande que 512 octets. 16 octets aléatoires
suffiraient amplement (et ça peut alors prendre la forme d'un
chiffrement CBC, les 16 octets étant l'IV). Dans ce cadre, les secteurs
sont groupés par 33 : 32 secteurs de données, un 33ème pour les IV. La
perte de place est de l'ordre de 3%. Mais la lecture d'un secteur
demande également la lecture du secteur contenant l'IV correspondante,
et l'écriture d'un secteur chiffré exige de réécrire le secteur
contenant l'IV aussi.



Oui. Ce serait l'idée, avec des réécritures de blocs choisies aléatoirement
afin qu'un attaquant n'ayant accès qu'au chiffré ne puisse pas déduire par
différenciation quels blocs ont été réellement écrits/modifiés.

Il n'est pas clair que le secteur soit la bonne
granularité, surtout que les OS récent sur PC ont tendance à faire les
accès par 4 Ko (comme une page de la MMU) et que les disques récents ont
des secteurs de 4 Ko aussi.



Ok. 512o ou 4ko, le principe ne change pas.

Dans mon idée, je délègue ça au filesystem qui utilisera la partition
chiffrée.



Je ne suis pas persuadé que séparer intégrité et chiffrement soit une
tactique optimale face à un attaquant actif. Le contrôle d'intégrité
d'un disque dur est une chose compliquée parce qu'on veut prendre en
charge des modifications partielles, et la recherche de la performance
pour le chiffrement (par réduction du nombre d'I/O) devrait suivre les
mêmes chemins.



Je suis d'accord, mais je ne vois pas trop d'inconvénients à demander
au filesystem d'assurer l'intégrité de ses fichiers. Le demander à la
couche de chiffrement m'a l'air effectivement compliqué, à tout le
moins, couteux en ressources. De plus, comment réagir si le chiffrement
n'est plus intègre? Une remontée warning en userland? un kernel panic?
Le filesystem m'a l'air bien plus adapté et je ne vois pas comment un
attaquant pourrait en profiter, puisque les contrôles d'intégrité sont
eux-mêmes chiffrés.

Une référence qui pourrait être une bonne lecture sur le sujet :

http://citeseerx.ist.psu.edu/viewdoc/summary?doi.1.1.23.976



Ok, merci.
--
Kevin