==Phrack Inc.== Volume 0x0b, Issue 0x3e, Phile #0x0e of 0x10 |=--------=[ A Dynamic Polyalphabetic Substitutation Cipher ]=-----------=| |=-----------------------------------------------------------------------=| |=---------------=[ Veins ]=---------------------=| 1 - Introduction 1.1 - Avant tout, un rappel. Qu'est-ce-qu'une substitition polyalphabétique ? 1.2 - Faiblesses dans une substitution polyalphabétique 1.3 - Théorie de l'information 2 - IMPLEMENTATION DE DPA-128 2.1 - DPA-128 utilisé en tant que fonction de hash à sens unique 2.2 - DPA-128 utilisé en tant que Générateur de Nombres Pseudo-Aléatoires 3 - Remerciements 4 - Références 5 - Code source --[ 1 - Introduction Dans phrack #51, mythrandir a discuté de la cryptanalyse des algorithmes monoalphabétiques et des substitutions et transpositions basiques. Ce papier discute d'une autre subsitution connue comme "substitution polyalphabétique" et de comment certains mécanismes peuvent être implémentés pour profiter de ses caractéristiques. Ce document se focalisera ensuite sur les "substititions polyalphabétiques dynamiques" qui sont une évolution des substitutions polyalphabétiques avec des s-tables dépendantes de la clef. Un crypteur "fonctionnel-mais-encore-en-chantier" sera présenté. C'est un cryptage à blocs (clef secrète) de 128 bits qui utilise des s-tables polyalphabétiques hautement dépendante de la clef. L'algorithme, DSA-128, consiste en une simple fonction qui fait 3 opérations sur le bloc. Ce n'est pas un réseau de Feistel mais respecte quand même le principe de diffusion et confusion de Shannon. Il n'a été revu que par un petit nombre de personnes, donc je déconseille fortement son usage car il n'a rien prouvé pour l'instant. Cependant, si vous l'utilisez et avez des commentaires, je serais heureux de les entendre, mais souvenez-vous, ne cryptez pas de trucs sensibles car il est probable que quelqu'un viendra, cassera l'algorithme et ira crier tous vos secrets sur IRC ;) Enfin, juste pour clarifier certaines choses. J'utilise l'acronyme DSA (pour "Dynamic Polyalphabetic Algorithms") dans ce document pour référer à la dépendance à la clef dans la subtitution polyalphabétique. J'ai vu des gens utiliser le terme "dynamique" pour des algorithmes qui utilisent des subtitutions polyalphabétiques dans un mode qui utilise un vecteur pseudo-aléatoire (CBC par exemple). Tant que je continuerais à utiliser l'acronyme, supposez que cette substitution dépendante de la clef fonctionne en totale abstraction du mode et que DPA-128 a une implémentation des modes ECB et CBC à l'heure où j'écris. De plus, le fait que je n'ai jamais vu d'implémentation de cryptage polyalphabétique dynamique ne signifie pas que toutes les idées de ce papier sont nouvelles. Certaines variantes de DES effectuent des subtitutions dépendantes de la clef en échangeant des lines de s-tables, et plusieurs algorithmes utilisent des fonctions de hash à sens unique pour les sous-clefs. --[ 1.1 - Avant tout, un rappel. Qu'est-ce-qu'une substitition polyalphabétique ? Une substitution polyalphabétique est un hybride entre une transposition et une substitution. Une transposition consiste en réorganiser les caracères du texte clair pour produire le message crypté. Supposez que mon message secret soit : THIS IS MY SECRET MESSAGE DONT READ IT, ARAM SUCKS Après l'avoir transposé en une table de 8 colonnes : il devient : T H I S I S M Y S E C R E T M E S S A G E D O N T R E A D I T A R A M S U C K S Le message crypté est produit en lisant les colonnes à la place des lignes : TSSTR HESRA ICAEM SRGAS IEEDU STDIC MMOTK YENAS Tandis que la subtitution consiste en interchanger les caractères dans le texte clair pour produire le message crypté : Supposez que mon message secret soit : THIS IS ANOTHER ATTEMPT TO PRESERVE MY PRIVACY L'alphabet de substitution est un simple réarangement : A B C D E F G H I J K L M N O P Q R S T U V W X Y Z Y Z W X U V S T Q R O P K L M N I J G H E F C D A B Le message crypté est produit en remplacant la lettre dans le texte clair par la nouvelle lettre dans l'alphabet réarrangé : HTQG QG YLMHTUJ YHHUKNH HM NJUGUJFU KA NJQFYWA Notez que ces deux méthodes ne requirèrent même pas de clef, les parties qui veulent partager le secret doivent partager le "protocole" qui est le nombre de colonnes pour la transposition ou l'alphabet réarrangé pour la substitution. En pratique, il existe des méthodes pour utiliser des clefs avec les substitutions et les transpositions mais finalement, aucune des deux n'est sécurisée, avec ou sans clef. Je ne décrirai pas comment vous pouvez les casser, les méthodes furent décrites dans phrack #51 si je me souviens bien, et elles sont si simples que certains magazines TV les utilisent dans leurs page de jeux. A présent revenons aux substitutions polyalphabétiques. Une table de substitution transposée ressemble plus ou moins à ceci : A B C D E F G H I J K L M N O P Q R S T U V W X Y Z B C D E F G H I J K L M N O P Q R S T U V W X Y Z A C D E F G H I J K L M N O P Q R S T U V W X Y Z A B D E F G H I J K L M N O P Q R S T U V W X Y Z A B C E F G H I J K L M N O P Q R S T U V W X Y Z A B C D F G H I J K L M N O P Q R S T U V W X Y Z A B C D E G H I J K L M N O P Q R S T U V W X Y Z A B C D E F H I J K L M N O P Q R S T U V W X Y Z A B C D E F G I J K L M N O P Q R S T U V W X Y Z A B C D E F G H J K L M N O P Q R S T U V W X Y Z A B C D E F G H I K L M N O P Q R S T U V W X Y Z A B C D E F G H I J L M N O P Q R S T U V W X Y Z A B C D E F G H I J K M N O P Q R S T U V W X Y Z A B C D E F G H I J K L N O P Q R S T U V W X Y Z A B C D E F G H I J K L M O P Q R S T U V W X Y Z A B C D E F G H I J K L M N P Q R S T U V W X Y Z A B C D E F G H I J K L M N O Q R S T U V W X Y Z A B C D E F G H I J K L M N O P R S T U V W X Y Z A B C D E F G H I J K L M N O P Q S T U V W X Y Z A B C D E F G H I J K L M N O P Q R T U V W X Y Z A B C D E F G H I J K L M N O P Q R S U V W X Y Z A B C D E F G H I J K L M N O P Q R S T V W X Y Z A B C D E F G H I J K L M N O P Q R S T U W X Y Z A B C D E F G H I J K L M N O P Q R S T U V X Y Z A B C D E F G H I J K L M N O P Q R S T U V W Y Z A B C D E F G H I J K L M N O P Q R S T U V W X Z A B C D E F G H I J K L M N O P Q R S T U V W X Y Ceci est connu sous le nom de "Table de Vignere", parce que ce fut inventé par Blaise Vigenere (un mathématitien français, pour votre culture). Au contraire des transpositions et des substitutions, une clef est requise. Supposez que ma clef secrète soit : BLEH Supposez que mon message secret soit : LAST ATTEMPT Le texte crypté est l'intersection de chaque caractère de la clef secrète et de chaque caractère du message secret. Les caractères de la clef sont cherché sur la première ligne, et les caractères du message sont cherchés sur la première colonne. Comme la clef est plus courte que le message, elle est complétée par elle-même donc devient : BLEHBLEHBLE Si elle avait été plus longue, alors soit le message aurait été rempli avec du bruit aléatoire, soit la clef aurait été tronquée (ce qu'on fait le plus souvent). Le texte crypté est obtenu en remplaçant une lettre dans le message par l'intersection du caractère courant du message et du caractère courant de la clef dans la table. Le message secret devient : MLWABUXLNAX Comme vous pouvez le remarquer, même si un caractère appraît deux fois ou plus dans le texte clair, il n'est pas crypté de la même façon selon le caractère de la clef utilisé pour la substitution. C'est ceci que signifie "polyalphabétique" dans "substitution polyalphabétique". Elle est connue depuis un moment comme "algorithme incassable" et une variante fut utilisée avec succès durant la Seconde Guerre Mondiale par la Résistance contre les Nazis. Alors que cela paraît plus fort que les transpositions et substititutions, c'est toujours très fragile et tant qu'un GNA n'est pas utilisé pour générer une clef qui soit aussi longue que les données à crypter (one-time pad [masque jetable]) il est possible de retrouver la taille de la clef, la clef elle-même et/ou le message, avec suffisamment de données à analyser. Les méthodes utilisées pour casser ce genre de crypteurs sont hors du sujet de ce papier mais une recherche sur google vous donnera suffisamment de résultats ;). Les tables polyalphabétiques ont trois propriétés interréssantes : a) f(a, b) == c f(a, b) == f(b, a) mais... f(a, c) != b et en supposant c = f(a,b), alors il existe f1 telle que f1(a,c) == b b) en utilisant le layout ASCII [NdT : disposition de la table ASCII], il existe 2562^2 combinaisons qui produiront les 256 résultats possibles (en incluant le caractère original). c) si nous supposons que la clef est vraiment aléatoire ET fait la même taille que le message à crypter, alors tous les résultats sont équiprobables. Et l'équibrobabilité signifie que : - Si vous ne prenez qu'un seul caractère dans le texte crypté, alors vous avez autant de chances pour qu'il fasse partie des caractères qui apparaissentt en clair. Ils apparraissent tous le même nombre de fois dans la table et sont chacun le résultat d'autant de combinaisons. - Il n'y a pas de substitution "inutile". Si la substitution d'un caractère 'A' résulte en un caractère 'A', alors elle n'est pas considérée comme inutile car il a autant de chance de sortir que n'importe quel autre. ---[ 1.2 Fragilités des substitutions polyalphabétiques Comme je l'ai dit précedemment, l'algorithme est fragile. Les fragilités sont nombreuses et sont pour la plupart liées à la fuite d'information à propos du texte clair, de la clef et/ou de la table de substitution. Si quelqu'un peut crypter des données en utilisant la même clef, il peut déterminer la taille de la clef avec un message et déterminer la structure de la table de substitution avec un autre, ceci lui donnant toutes les informations nécessaires pour comprendre le texte crypté, et tout autre message crypté avec la même clef. Mais comprenez bien qu'il n'a pas BESOIN d'être capable de crypter des données, c'est juste plus simple ;). Le fait que la clef est concaténée à elle-même ne change rien, et en réalité une implémentation sur ordinateur fonctionnerait sur des données en utilisant un modulo de la taille de la clef pour sauver de la mémoire. Les raisons qui font que c'est si simple sont celles-ci : - si on choisit une clef A et une clef B telles qu'elles ne diffèrent que d'un bit, alors le texte chiffré ne diffèrera que d'un octet. - si on choisit un message A et un message B tels qu'ils ne diffèrent également que d'un bit, alors le texte crypté ne diffèrera que d'un octet. - si on change un bit dans le texte crypté et qu'on le décrypte, le message résultant ne diffèrera que d'un octet. - si on a une connaissance partielle de la clef, ou du message, alors on peut déterminer quelles substitutions ne sont pas probables et donc réduire drastiquement la complexité d'une attaque. Une connaissance partielle de la clef donne également à une analyse statistique une chance de casser le texte crypté alors la substitution polyalphabétique avait toutes les caractéristiques pour éviter que cela se produise. Donc, résumons-nous. Les substitutions polyalphabétiques telles que décrites ci-dessus sont vulnérables à une attaque par texte clair choisi, par texte connu, corrélation de clef, et éventuellement à une attaque statistique. Oh... j'oubliais... toute information partielle révèle de l'information à propos d'autres donnés non-liées. Si je connais en partie le texte clair, alors en accédant au texte chiffré je suis en mesure de retrouver une partie de la clef ; en ayant connaissance d'une partie de la clef et accès au texte crypté, je suis capable de retrouver une partie du texte clair. Il n'y a pas un point faible, il n'y a que des points faibles. ----[ 1.3 - Théorie de l'information Shannon a décrit deux propriétés qu'un algorithme de chiffrement à blocs doit avoir pour être fort. Tous les algorithmes les vérifiant ne sont pas forts, mais ceux qui ne les vérifient pas sont le plus souvent faibles. Ces proriétés sont la "confusion" et la "diffusion". La première est celle que nous atteignons avec les substitutions polyalphabétiques, c'est-à-dire l'incapacité à déduire à partir d'un simple octet encrypté, sans aucune autre information, de quelle substitution il résulte. Ceci vient de l'équiprobabilité que nous donnent les tables polyalphabétiques. La seconde fait défaut à cet algorithme et est une des raisons de sa vulnérabilité. La diffusion est une caractéristique où une cause mineure produit un résultat majeur. Elle est parfois appelée "effet boule de neige" ["cascading effect"]. Basiquement, la diffusion devrait assurer qu'un changement d'un octet dans la clef altère le texte crypté dans son ensemble, qu'un changement d'un octet dans le texte clair altère également le texte crypté dans son ensemble, et qu'un changement d'un octet dans le texte crypté altère le texte clair dans son ensemble. Cette altération totale est seulement en apparence, et un coup d'oeil plus avancé au texte crypté complet révèlerait une moyenne de l'ordre de la moitié des octets modifiés, comme vous le remarquerez dans la sortie de "bitdiff" plus tard dans ce papier. Alors qu'il est difficile de décider si un algorithme de cryptage possède ou non une confusion et une diffusion correctes, elles produisent toutes deux un résultat entropique qui peut être mesuré en utilisant différentes méthodes. Ces méthodes seront utilisées dans ce papier mais expliquées plus loin dans les références. Un algorithme qui ne produit pas de vraie entropie est faible (vraie entropie == bruit blanc). Un moyen d'ajouter de la confusion est d'assurer que le texte crypté n'est pas dépendant de la clef, caractère par caractère. Changer un bit de la clef devrait changer tout le texte crypté. On peut y arriver en utilisant une fonction de hash à sens unique pour la génération de la clef. Certaines fonctions de hash à sens unique ont des propriétés qui les rends utilisables, qui sont : h = f(M) - quelle que soit la longueur de M, h a une taille fixée - supposant M, il devrait être simple de calculer h - supposant h, il devrait être difficile de calculer M - un changement d'un bit de M altère la moitié des bits de h (en moyenne) - il devrait être difficile de trouver un M' pour un M fixé tel que f(M) = f(M') - il devrait être difficile de trouver un quelconque M et M' tel que f(M) = f(M') Les deux dernières propriétés semblent être identique mais en pratique il est "plus simple" de produire une collision aléatoire que de produire une collision pour une sortie attendue. Supposant que h fait 128 bits, trouver une collision particulière prend au plus 2^128 essais, tandis que trouver une collision prend au plus 2^64 essais. Ceci est connu comme l'attaque aniversaire. L'usage d'une telle fonction rendra la corrélation de clef difficilement faisable, car en choisissant deux clefs qui ont une relation, on obtiendra des sous-clefs qui n'ont aucune relation, même si la relation est une simple différence d'un bit. Je ne parle même pas des attaques basées sur une connaissance partielle de la clef ;) Ceci évitera également aux utilisateurs de choisir des clefs fragiles, délibérément ou non, car il sera difficile de trouver une clef qui produira une sous-clef fragile (en supposant qu'il y a des clefs fragiles ;)) après passage dans la fonction de hash. Par clef fragile, je ne veux pas dire les clefs comme "aaaa" ou "foobar", mais les clefs qui produiront une sous-clef qui introduit une fragilité dans le processus de cryptage (comme les quatre clefs fragiles de DES). La fonction n'étant pas réversible, une connaissance partielle du texte clair et l'accès au texte crypté ne révèle pas la clef mais la sous-clef, de laquelle vous ne pouvez pas obtenir d'information sur la clef. Si l'algorithme s'itère pour plusieurs rondes, il est possible de générer des sous-clefs en appelant f avec la sous-clef précédente : round1: f(k) round2: f(f(k)) round3: f(f(f(k))) et ainsi de suite... Notez que rien n'empêche une implémentation de précalculer les sous-clefs pour de meilleures performances (c'est ce que fait mon implémentation) plutôt que les calculer pour chaque bloc. Ceci donne que la connaissance de la sous-clef du round 3 ne donne pas d'information à propos de la sous-clef du round 2 ou du round 1. Cela fait un point faible de moins ;) Finalement, cela augmentera la confusion en créant une relation entre chaque bit de la clef entrée par l'utilisateur et chaque octet du texte crypté. Malheureusement, ce n'est pas assez. Nous avons ajouté de la confusion mais malgré qu'il ne soit théoriquement pas possible de retrouver la clef, même en ayant accès au message complet et au texte crypté complet, il est toujours possible de retrouver la sous-clef avec une connaissance partielle et de décrypter n'importe quelle donnée cryptée avec la clef originale. C'est ici que la diffusion entre en jeu, avec une méthode appelée "enchaînement d'octet" ["byte chaining"]. L'enchaînement d'octet est à un bloc ce que l'enchaînement de blocs est au texte crypté, une propagation de changements qui vont affecter tous les données subséquentes. Ceci est effectué avec un simple XOR, où chaque octet du bloc est XOR-é avec le suivant (modulo la taille du bloc pour avoir le dernier octet xor-é avec le premier). De cette manière, un changement d'un seul bit dans un octet aura des répercussions sur tous les octets qui le suivent. Si la fonction utilisée pour crypter les données est appelée pour plus d'un round, alors tous les octets sont garantis avoir été altérés par au moins un octet. Cette opération est effectuée avant le cryptage donc le résultat d'un octet crypté ne dépend pas seulement de l'octet courant mais de tous ceux qui ont été utilisés pour l'enchaînement d'octets. A chaque round, l'effet boule de neige fait son travail et le changement se propage au travers du bloc. Il est possible d'augmenter la complexité en utilisant une permutation liée à la clef avant le cryptage. DPA-128 utilise un glissement lié à la clef mais ceci peut, d'une certaine manière, être considéré comme une permutation. Certaines fonctions connues sous le nom de "fonctions de hashage de chaîne" peuvent calculer une valeur entière à partir d'une chaîne. Elles sont souvent utilisées par les développeurs C pour créer des tables de hashage et elles sont plutôt simple à écrire. Il est possible d'utiliser ces fonctions sur une sous-clef pour créer un glissement circulaire lié à la clef à l'intérieur du bloc : - nous avons une sous-clef pour le round que nous avons calculé en utilisant f, cette sous-clef est hashée pour produire un entier. La fonction de hash n'a pas besoin de respecter de contrainte grâce aux propriétés de f. Le paranoïaque pourrait implémenter une fonction qui a de faibles collisions et une belle répartition, mais comme elle est appliquée au résultat de f, elle hérite de certaines de ses caractéristiques. - supposant que la taille du bloc est 128, nous réduisons cet entier à 128 (7 bits). il n'y a rien de magique là-dedans, juste un modulo. - le résultat est utilisé pour glisser le bloc circulairement >>> Notez que la relation de clef pour le glissement n'a pas plus de sécurité qu'un simple glissement d'octet - au moins sur la table de Vigenere - mais ajoute simplement plus de confusion. Cela fut initialement présenté comme une mesure de sécurité pour les tables de substitutions qui n'ont pas de résultats équiprobables. Cela empêche l'élimination de certaines combinaisons de substitutions en analysant quels bits sont mis dans octet crypté lorsque vous connaissez le texte clair. Depuis le texte crypté, il n'est pas possible de déterminer si un block a été glissé (la valeur de hash de la clef pourrait avoir été 0 ou un produit de 128, qui sait ;) et s'il a été glissé, il n'est pas possible de savoir d'où viennent les bits (les octets où ils étaient à l'origine et quelle fut leur position) ce qui rend difficile de déterminer si le bit de signe sur un octet particulier est réellement un bit de signe ou non et s'il faisait partie de cet octet ou non. De plus, le glissement dépend de la sous-clef utilisée pour un round donc il sera différent à chaque round et aide la diffusion au travers de la phase d'enchaînement d'octets. Finalement, il est possible, en utilisant la même méthode, de créer une relation entre une sous-clef et la table de subtitution. C'est ici que la subtitution polyalphabétique dynamique entre en jeu ! Comme nous l'avons vu, une substitution polyalphabétique a 256^2 substitutions, avec 256 résultats. Cela signifie que si un attaquant voudrait tester toutes les combinaisons possibles, il aurait à essayer 256 combinaisons pour un caractère pour être sûr que le bon couple a été utilisé (s'il connaissait la structure de la table de substitution, 256 sinon). Il est possible d'augmenter cette valeur en créant une relation entre la clef et la table de substitution. Il y a 256 caractères, donc il est possible de créer 256 tables différentes en glissant UN octet sur chaque ligne : au lieu de : 0 1 2 3 4 5 6 7 8 9 ... 1 2 3 4 5 6 7 8 9 ... 2 3 4 5 6 7 8 3 4 5 6 7 8 4 5 6 7 8 5 .... ... nous terminons avec (n étant le glissement) : n%256 (n+1)%256 (n+2)%256 (n+3)%256 (n+4)%256 (n+5)%256 ... (n+1)%256 (n+2)%256 (n+3)%256 (n+4)%256 (n+5)%256 ... (n+2)%256 (n+3)%256 (n+4)%256 (n+5)%256 (n+6)%256 (n+3)%256 (n+4)%256 (n+5)%256 (n+6)%256 (n+7)%256 (n+4)%256 (n+5)%256 (n+6)%256 (n+7)%256 (n+8)%256 (n+5)%256 (n+6)%256 (n+7)%256 (n+8)%256 (n+9)%256 (n+6)%256 ... ... Ceci signifie qu'un attaquant aurait besoin d'essayer 256^2 combinaisons avant de savoir avec certitude que la bonne combinaison a été utilisée. Il doit utiliser les mêmes combinaisons qu'avant mais avec toutes les variations de "n". "n" peut être calculé en utilisant le même méthode que pour le glissement de bloc mais comme il y a 256 glissements possibles pour la table de substitution, le résultat sera réduit modulo 128 (8 bits). Les tables étant structurées de façon logique, elles peuvent être représentées par de l'arithmétique ce qui évite le besoin de stocker les 256 tables possibles et sauve pas mal de mémoire. Il est également possible avec plus de travail de créer des s-tables polyalphabétiques qui sont mélangée au lieu d'être glissées, de telles tables partageraient toujours les caractéristiques du polyalphabétisme mais empêche une connaissance partielle de la table de déduire la totalité de la structure interne. Je n'ai pas eu suffisamment de temps pour continuer à travailler sur ceci, donc je suis incapable d'en donner un exemple, malgré tout, de simples tables comme celle du dessus sont suffisantes à mon avis. Soient k le caractère de la clef, d le caractère du message et s le glissement : le cryptage peut être représenté par cete équation : (k + d + s) mod 256 alors que le décryptage est : ((256 + d) - k - s) mod 256 La partie amusante est que lorsque que vous jouer avec les statistiques, vous obtenez une vision très différente selon que vous êtes dans la position de l'attaquant ou du bon gars tentant de garder ses secrets. Supposons qu'il y a n rounds, alors vous avez (256^2)+m substitutions utilisables, où 1 <= m <= n et n <= 256. Cela vient du fait que certaines sous-clefs peuvent produire des tables de substitutions identiques. De l'autre côté, et je ne fais pas des maths pour cela ;), l'attaquant ne doit pas seulement deviner quelles substitutions ont été effectuées, mais également les tables dans lesquelles elles furent effectuées... exactement dans le même ordre... fuite de données qui ne l'informe pas sur les sous-clefs utilisées pour générer les tables dont il tente de déterminer la structure ;) Le resultat n'est PAS équiprobable, parce que cela requièrerait exactement 256 rounds avec différentes tables ce qui est difficilement faisable (il faudrait, si j'ai bon, essayer 2^128^256 clefs pour juste déterminer si c'est faisable), mais du point de vue de l'attaquant, même une recherche exhaustive pourrait créer une indécision car plusieurs clefs résulteront probablement au même cryptage si appliquées à différants messages (plusieurs produirons le même cryptage si appliquées à la poubelle également ;). --[ 2 - IMPLEMENTATION DE DPA-128 Comme je l'ai dit, DPA-128 est un code à clef secrète et à blocs. La taille de ses blocs et de sa clef est de 128 bits. Ce n'est pas une limitation imposée par l'algorithme qui est facilement adaptable pour différentes tailles de clefs et de blocs. Il se compose de 16 rounds, chacun effectuant : - un enchaînement d'octets ; - un glissement sur la sous-clef ; - une substitution polyalphabétique avec la sous-clef. Tous les rounds ont leur propre sous-clef. L'implémentation utilise toutes les idées expliquées dans cet article et avant que je fournisse le code, voici quelques tests effectués dessus. ----[ 2.1 - DPA-128 utilisé comme fonction de hash à sens unique Bruce Schneier a expliqué dans "Cryptographie Appliquée" que certains codes peuvent être tournés en fonction de hash à sens unique en les utilisant en mode BC (CBC en l'occurence) utilisant une clef fixée et un vecteur d'initialisation avec plus ou moins d'efficacité. Il est difficile de déterminer si DPA-128 est efficace car il n'a pas été analysé par beaucoup de monde et je le considère efficace pour produire des checksums comme pour crypter. Si il y a une faiblesse dans l'algorithme alors la checksum ne sera pas sûre. Le même principe s'applique sur DPA-128 utilisé comme GNPA. Donc ... j'ai fait quelques tests ;) J'ai utilisé trois outils, le premier, "bitdiff", est un petit utilitaire qui parcours deux fichier et les compare bit par bit. Il sort ensuite le nombre de bits qui ont changé et la répartition des zéros et des un. Une sortie ressemble à ceci : % ./bitdiff file1 file2 64 bits have changed. ratio for file1: 0's: 55 1's: 73 ratio for file2: 0's: 71 1's: 57 J'ai également utilisé l'outil "segments", qui compte les segments de bits identiques dans un fichier. Une sortie ressemble à ceci : % ./segments file1 0's segments: 1 => 19 2 => 6 3 => 4 4 => 0 1's segments: 1 => 13 2 => 7 3 => 3 4 => 3 Enfin j'ai utilisé un outil existant nommé "ent" qui est disponible à http://www.fourmilab.ch/random/ qui effectue plusieurs tests d'entropie, aidant à déterminer : - si DPA-128 passe les tests déterministes et comment il se compare à un GNPA (j'ai utilisé le /dev/urandom de NetBSD) ; - quel est l'impact sur une checksum lorsqu'un bit change dans un fichier. Théoriquement, un algorithme de cryptage équiprobable ne serait pas une bonne idée pour une fonction de hash à sens unique car il serait facilement sujet à collision, mais comme je l'ai expliqué, le résultat semble être équiprobable alors qu'il y a un rang limité de substitutions possibles pour une clef fixée. Il fait un checksum de /etc/password trois fois, la première était sur le vrai fichier, la seconde sur le fichier avec un changement d'un bit et la troisième sur le fichier avec une addition de 6 octets. Tous les octets furent affectés, les tests avec bitdiff montrèrent qu'un changement d'un bit produit une moyenne de 60 bits modifiés dans la checksum de 128 bits. % ./dpa sum passwd |hexdump 0000000 be85 3b72 1a76 48e6 5d08 939b 104f 3f23 0000010 % ./dpa sum passwd.1 | hexdump 0000000 f9d3 c5fe d146 2170 144d 900d 0e99 c64b 0000010 % ./dpa sum passwd.2 | hexdump 0000000 fa19 4869 3f61 798a 2e81 91e9 bc92 78ee 0000010 Après j'ai redirigé le checksum dans des fichiers, j'ai appelé bitdiff dessus. Les fichiers ne contenaient pas la représentation hexadécimale, mais la sortie réelle de 128 bits : % ./bitdiff passwd.chk passwd.1.chk 63 bits have changed. ratio for passwd.chk: 0's: 65 1's: 63 ratio for passwd.1.chk: 0's: 68 1's: 60 % ./bitdiff passwd.chk passwd.2.chk 61 bits have changed. ratio for passwd.chk: 0's: 65 1's: 63 ratio for passwd.2.chk: 0's: 64 1's: 64 Vous remarquerez une jolie répartition des zéros et des un, voyons ce que segments a à dire à propos de ceci : % ./segments passwd.chk 0's segments: 1 => 13 2 => 10 3 => 3 4 => 2 1's segments: 1 => 15 2 => 4 3 => 5 4 => 0 % ./segments passwd.1.chk 0's segments: 1 => 11 2 => 8 3 => 5 4 => 3 5 => 0 1's segments: 1 => 13 2 => 9 3 => 2 4 => 0 5 => 1 % ./segments passwd.2.chk 0's segments: 1 => 12 2 => 10 3 => 3 4 => 1 5 => 0 1's segments: 1 => 16 2 => 3 3 => 4 4 => 3 5 => 1 Et bien tout ce que nous pouvons remarquer est qu'il y a surtout de petits segments et qu'ils sont bien répartis. Je passe le test d'entropie car il illustrera l'usage de DPA-128 comme GNPA ;). ----[ 2.2 - DPA-128 utilisé comme GNPA Pour les tests suivants, concernant les segments et l'entropie : - le fichier "urandom.seed" est constitué de 1024 octets lus depuis le /dev/urandom de NetBSD 1.6.1 - le fichier "dpa.seed" est constitué du résultat d'un cryptage ECB du main.c de DPA et d'une réduction de la sortie à 1024 octets. Ceci signifie qu'alors que les tests sur urandom.seed s'appliquent sur le résultat d'un GNPA, les tests sur dpa.seed peuvent être reproduits. Ils montrent une bonne entropie pour le cryptage d'une valeur fixée et les résultats devraient être à peu près les mêmes dans le cadre d'une utilisation en tant que GNPA. Les tests effectués par "ent" sont décrits sur leur site web, je ne vais pas les décrire car c'est en dehors du sujet de cet article et que je le ferais moins bien que leur site. % ./segments urandom.seed 0's segments: 1 => 1019 2 => 418 3 => 212 4 => 88 5 => 35 6 => 18 1's segments: 1 => 1043 2 => 448 3 => 179 4 => 74 5 => 32 6 => 13 % ./segments dpa.seed 0's segments: 1 => 1087 2 => 443 3 => 175 4 => 72 5 => 29 6 => 18 1's segments: 1 => 1039 2 => 453 3 => 195 4 => 67 5 => 34 6 => 15 % ./ent -b urandom.seed Entropy = 0.999928 bits per bit. Optimum compression would reduce the size of this 8192 bit file by 0 percent. Chi square distribution for 8192 samples is 0.82, and randomly would exceed this value 50.00 percent of the times. Arithmetic mean value of data bits is 0.4950 (0.5 = random). Monte Carlo value for Pi is 3.058823529 (error 2.63 percent). Serial correlation coefficient is -0.002542 (totally uncorrelated = 0.0). % ./ent -b dpa.seed Entropy = 1.000000 bits per bit. Optimum compression would reduce the size of this 8192 bit file by 0 percent. Chi square distribution for 8192 samples is 0.00, and randomly would exceed this value 75.00 percent of the times. Arithmetic mean value of data bits is 0.5000 (0.5 = random). Monte Carlo value for Pi is 3.200000000 (error 1.86 percent). Serial correlation coefficient is -0.003906 (totally uncorrelated = 0.0). % ./ent -bc urandom.seed Value Char Occurrences Fraction 0 4137 0.505005 1 4055 0.494995 Total: 8192 1.000000 Entropy = 0.999928 bits per bit. Optimum compression would reduce the size of this 8192 bit file by 0 percent. Chi square distribution for 8192 samples is 0.82, and randomly would exceed this value 50.00 percent of the times. Arithmetic mean value of data bits is 0.4950 (0.5 = random). Monte Carlo value for Pi is 3.058823529 (error 2.63 percent). Serial correlation coefficient is -0.002542 (totally uncorrelated = 0.0). % ./ent -bc dpa.seed Value Char Occurrences Fraction 0 4096 0.500000 1 4096 0.500000 Total: 8192 1.000000 Entropy = 1.000000 bits per bit. Optimum compression would reduce the size of this 8192 bit file by 0 percent. Chi square distribution for 8192 samples is 0.00, and randomly would exceed this value 75.00 percent of the times. Arithmetic mean value of data bits is 0.5000 (0.5 = random). Monte Carlo value for Pi is 3.200000000 (error 1.86 percent). Serial correlation coefficient is -0.003906 (totally uncorrelated = 0.0). Les derniers tests ont du vous donner une idée de la confusion, de la diffusion et de l'entropie présente dans un texte crypté par DPA-128. D'autres résultats sont disponibles en ligne sur ma page web, je n'ai pas voulu en mettre trop ici car ils ressemblent tous à la même chose ;) --[ 3 - Remerciements J'aimerais remercier quelques personnes : k' qui m'a aidé sur les versions précédentes et certaines parties de DPA-128, acid, qui a supporté mon harcèlement perpétuel (hey essaie-le s'il te plaît !), pitufo pour avoir été le premier testeur et benchmarker de DPA-128, hypno pour avoir lu ceci et sorti de mauvaises phrases :), br1an pour avoir également lu ceci et donné des conseils, un Dr dont le nom restera privé qui a audité DPA-128 et mayhem qui m'a suggéré d'écrire un papier à propos de DPA. --[ 4 - REFERENCES . http://www.tristeza.org/projects/dpa/ ma page pour le projet DPA avec des exemples et plein de tests . http://www.csua.berkeley.edu/cypherpunks/ cypherpunks . http://www.fourmilab.ch/random/ les tests d'entropie et leur description . http://www.schneier.com/paper-blowfish-fse.html un papier sur blowfish et quelles fonctionnalités qu'un code doit avoir . "applied cryptography", Bruce Schneier LE livre ;) --[ 5 - Code source Tous le code est fournit sous la licence ISC, faites-en ce que vous voulez mais s'il vous plaît, s'il vous plaît ne l'utilisez pas pour crypter des données sensibles sauf si vous savez ce que vous faîtes (ceci signifie que vous ne pourriez pas le casser et que vous faîtes confiance en vos capacités). Le code n'est PAS optimisé pour la vitesse, il est toujours en construction, et plusieurs parties peuvent être améliorées, je suis juste un peu pressé et au moment où vous lirez ceci, il sera probablement un peu plus propre ;) Si vous comptez utiliser DPA-128 même si je vous avertis encore de ne pas le faire, voici quelques recommandations : - Le code suivant accepte des clefs comme paramètre ou comme fichier. Il est préférable pour plusieurs raisons d'utiliser un fichier, mais la meilleure raison (à part que quelqu'un peut faire un "ps" au mauvais moment...) est que vous pouvez avoir votre clef comme résultat d'un GNPA : % dd if=/dev/urandom of=/home/veins/.dpa/secret.key bs=1024 count=1 Les chances que quelqu'un devine votre clef devienent très petites ;) - Utilisez le mode CBC. L'impact de l'utilisation du mode CBC sur les performances est trop faible pour être une excuse pour ne pas l'utiliser. Pour crypter : % dpa -a enc -m cbc -k file:secret.key -d file:/etc/passwd -o p.enc Pour décrypter: % dpa -a dec -m cbc -k file:secret.key -d file:p.enc -o p.dec /* * Copyright (c) 2004 Chehade Veins * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ 8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - /* bitdiff.c */ /* * This is a small utility to compare the bits in two files. It is ugly * and could be rewritten in a sexier way but it does its job so no * need to waste time on it ;) * */ #include #include #include #include int main(int argc, char *argv[]) { int i; int size1, size2; /* size counters */ char *s1, *s2; int s1_0, s1_1; /* in s1: 0s and 1s counter */ int s2_0, s2_1; /* in s2: 0s and 1s counter */ int fd1, fd2; unsigned int cnt; unsigned int diff; unsigned int total; struct stat sa; struct stat sb; if (argc < 3) return (EX_USAGE); fd1 = open(argv[1], O_RDONLY); fd2 = open(argv[2], O_RDONLY); if (fd1 < 0 || fd2 < 0) return (EX_SOFTWARE); fstat(fd1, &sa); fstat(fd2, &sb); size1 = sa.st_size; size2 = sb.st_size; s1 = mmap(NULL, sa.st_size, PROT_READ, MAP_PRIVATE, fd1, 0); s2 = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd2, 0); if (s1 == (void *)MAP_FAILED || s2 == (void *)MAP_FAILED) return (EX_SOFTWARE); s1_1 = s2_1 = s1_0 = s2_0 = diff = total = 0; while (size1 && size2) { for (i = 7, cnt = 0; i >= 0; --i, ++cnt) { if (((*s1 >> i) & 0x1) != ((*s2 >> i) & 0x1)) ++diff; if ((*s1 >> i) & 0x1) ++s1_1; else if (((*s1 >> i) & 0x1) == 0) ++s1_0; if ((*s2 >> i) & 0x1) ++s2_1; else if (((*s2 >> i) & 0x1) == 0) ++s2_0; ++total; } ++s1; ++s2; size1--; size2--; } if (diff == 0) printf("bit strings are identical\n"); else { printf("%d bits have changed.\n", diff, total); printf("ratio for %s:\n", argv[1]); printf("\t0's: %d\n", s1_0); printf("\t1's: %d\n", s1_1); printf("\n"); printf("ratio for %s:\n", argv[2]); printf("\t0's: %d\n", s2_0); printf("\t1's: %d\n", s2_1); } munmap(s1, sa.st_size); munmap(s2, sb.st_size); return (EX_OK); } 8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - /* segments.c */ /* * This is a small utility to count the segments of identical bits in a * file. It could also be rewritten in a sexier way but... * */ #include #include #include #include int main(int argc, char *argv[]) { int i; int fd; int cnt; int last; int biggest; int size; char *map; struct stat sb; unsigned int STATS[2][32]; if (argc < 2) return (EX_USAGE); /* Initialize the segments counters */ for (cnt = 0; cnt < 2; ++cnt) for (i = 0; i < 32; ++i) STATS[cnt][i] = 0; /* Open and map the file in memory */ fd = open(argv[1], O_RDONLY); if (fd < 0) return (EX_SOFTWARE); fstat(fd, &sb); map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (map == (void *)MAP_FAILED) return (EX_SOFTWARE); last = -1; biggest = 0; size = sb.st_size; while (size--) { for (i = 7, cnt = 0; i >= 0; --i, ++cnt) { if ((*map >> i) & 0x1) { if (last == 0) { if (cnt > biggest) biggest = cnt; if (cnt >= 32) errx(EX_SOFTWARE, "This cannot be an entropy source ;)"); STATS[last][cnt] += 1; cnt = 0; } last = 1; } else { if (last == 1) { if (cnt > biggest) biggest = cnt; if (cnt >= 32) errx(EX_SOFTWARE, "This cannot be an entropy source ;)"); STATS[last][cnt] += 1; cnt = 0; } last = 0; } } ++map; } munmap(map, sb.st_size); printf("0's segments:\n"); for (i = 1; i < biggest; i++) printf("\t%d => %d\n", i, STATS[0][i]); printf("\n1's segments:\n"); for (i = 1; i < biggest; i++) printf("\t%d => %d\n", i, STATS[1][i]); return (EX_OK); } 8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - Again, the source code that follows is a work in progress, and some parts deserve a cleaner rewrite. data.c is truly ugly ;) It was tested on Linux & BSD/i386, SunOS/sparc and OSF1/alpha, if it does not run on your unix box, porting it should be trivial. 8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - # Makefile NAME = dpa SRCS = main.c\ bitshift.c\ bytechain.c\ blockchain.c\ E.c\ D.c\ S_E.c\ S_D.c\ iv.c\ ecb.c\ cbc.c\ checksum128.c\ hash32.c\ key.c\ data.c\ sum.c\ usage.c OBJS = $(SRCS:.c=.o) CFLAGS = LDFLAGS = $(NAME) : $(OBJS) cc -o $(NAME) $(OBJS) $(LDFLAGS) clean : rm -f *.o *~ fclean : clean rm -f $(NAME) re : fclean $(NAME) 8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - /* include/dpa.h */ #ifndef _DPA_H_ #define _DPA_H_ #define DPA_KEY_SIZE 16 #define DPA_BLOCK_SIZE 16 #define DPA_ENCRYPT 0 #define DPA_DECRYPT 1 #define DPA_MODE_ECB 0 #define DPA_MODE_CBC 1 struct s_dpa_sub_key { unsigned char key[DPA_KEY_SIZE]; unsigned char shift; }; typedef struct s_dpa_sub_key DPA_SUB_KEY; struct s_dpa_key { struct s_dpa_sub_key subkey[16]; }; typedef struct s_dpa_key DPA_KEY; struct s_dpa_data { unsigned char *data; unsigned long length; }; typedef struct s_dpa_data DPA_DATA; void checksum128(unsigned char *, unsigned char *, unsigned int); unsigned long hash32(unsigned char *, unsigned int); unsigned char dpa_encrypt(unsigned int, unsigned int, unsigned int); unsigned char dpa_decrypt(unsigned int, unsigned int, unsigned int); void DPA_ecb_encrypt(DPA_KEY *, DPA_DATA *, DPA_DATA *); void DPA_ecb_decrypt(DPA_KEY *, DPA_DATA *, DPA_DATA *); void DPA_cbc_encrypt(DPA_KEY *, DPA_DATA *, DPA_DATA *); void DPA_cbc_decrypt(DPA_KEY *, DPA_DATA *, DPA_DATA *); void DPA_sum(DPA_KEY *, DPA_DATA *, DPA_DATA *); void DPA_set_key(DPA_KEY *, unsigned char *, unsigned int); void DPA_set_keyfile(DPA_KEY *, char *); void DPA_set_data(DPA_DATA *, unsigned char *, unsigned int); void DPA_set_datafile(DPA_DATA *, char *); void DPA_set_ciphertext(DPA_DATA *, DPA_DATA *, int, int); void DPA_write_to_file(DPA_DATA *, char *); void DPA_sum_write_to_file(DPA_DATA *, char *); void rbytechain(unsigned char *); void lbytechain(unsigned char *); void rbitshift(unsigned char *, unsigned int); void lbitshift(unsigned char *, unsigned int); void blockchain(unsigned char *, unsigned char *); void IV(unsigned char *); void E(unsigned char *, unsigned char *, unsigned int); void D(unsigned char *, unsigned char *, unsigned int); void S_E(unsigned char *, unsigned char *, unsigned int); void S_D(unsigned char *, unsigned char *, unsigned int); void usage(void); #endif 8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - /* checksum128.c */ /* NEEDS_FIX */ /* * This function creates a 128 bits (16 bytes) checksum out of a variable * length input. It has NOT been verified so it is most likely broken and * subject to collisions even though I was not able to find any myself. * * The following constraints need to be respected: * - the function has to return a 128 bits value no matter what; * - it should be difficult to determine the result by knowing the input; * - it should be difficult to determine the input by knowing the result; * - it should be difficult to find an input that will produce an identic * result as a known input; * - it should be difficult to find two inputs that will produce the same * result; * - it should be easy to compute the result of an input; * * If checksum128() happens to be broken, DPA-128 could be fixed by * replacing it with any one-way hash function that produces a 128 bits * output (MD5 comes to mind first ;). */ #define __NBROUNDS 32 void checksum128(unsigned char *key, unsigned char *skey, unsigned int size) { unsigned int cnt; unsigned int length; unsigned long a; unsigned long b; unsigned long c; unsigned long d; unsigned char *save; /* Initialization of contexts */ a = 0xdeadbeef; b = 0xadbeefde; c = 0xbeefdead; d = 0xefdeadbe; for (cnt = 0; cnt < __NBROUNDS; ++cnt) { for (length = 0, save = key; length < size; ++save, ++length) { /* each context is first summed up with the cumplement of * the current ascii character. */ a = (a + ~(*save)); b = (b + ~(*save)); c = (c + ~(*save)); d = (d + ~(*save)); /* Confusion */ /* * Context A is summed with the product of: * - cumplement of B, C and cumplement of D; * * Context B is summed with the product of: * - cumplement of C, D and cumplement of A; * * Context C is summed with the product of: * - cumplement of D, A and cumplement of B; * * Context D is summed with the product of: * - cumplement of A, B and cumplement of C; * * Every context has a repercussion on all others * including itself, and multiplication makes it * hard to determine the previous values of each * contexts after a few rounds. */ a += ~b * c * ~d; b += ~c * d * ~a; c += ~d * a * ~b; d += ~a * b * ~c; } /* Diffusion */ /* * The bytes of each contexts are shuffled within the * same context, the first byte of A becomes the last * which becomes the first. the second becomes the * third which becomes the second. This permutation * is also applied to B, C and D, just before they go * through another round. */ a = (((a & 0x000000ff) << 24) + ((a & 0x0000ff00) << 8) + ((a & 0x00ff0000) >> 8) + ((a & 0xff000000) >> 24)); b = (((b & 0x000000ff) << 24) + ((b & 0x0000ff00) << 8) + ((b & 0x00ff0000) >> 8) + ((b & 0xff000000) >> 24)); c = (((c & 0x000000ff) << 24) + ((c & 0x0000ff00) << 8) + ((c & 0x00ff0000) >> 8) + ((c & 0xff000000) >> 24)); d = (((d & 0x000000ff) << 24) + ((d & 0x0000ff00) << 8) + ((d & 0x00ff0000) >> 8) + ((d & 0xff000000) >> 24)); } /* Diffusion */ /* * The Checksum is constructed by taking respectively * the first byte of A, B, C and D, then the second, * the third and the fourth. */ skey[0] = (a & 0xff000000) >> 24; skey[1] = (b & 0xff000000) >> 24; skey[2] = (c & 0xff000000) >> 24; skey[3] = (d & 0xff000000) >> 24; skey[4] = (a & 0x00ff0000) >> 16; skey[5] = (b & 0x00ff0000) >> 16; skey[6] = (c & 0x00ff0000) >> 16; skey[7] = (d & 0x00ff0000) >> 16; skey[8] = (a & 0x0000ff00) >> 8; skey[9] = (b & 0x0000ff00) >> 8; skey[10] = (c & 0x0000ff00) >> 8; skey[11] = (d & 0x0000ff00) >> 8; skey[12] = (a & 0x000000ff); skey[13] = (b & 0x000000ff); skey[14] = (c & 0x000000ff); skey[15] = (d & 0x000000ff); } 8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - /* hash32.c */ /* * This function computes a 32 bits output out a variable length input. It is * not important to have a nice distribution and low collisions as it is used * on the output of checksum128() (see checksum128.c). There is a requirement * though, the function should not consider \0 as a key terminator. */ unsigned long hash32(unsigned char *k, unsigned int length) { unsigned long h; for (h = 0; *k && length; ++k, --length) h = 13 * h + *k; return (h); } 8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - /* bytechain.c */ #include "include/dpa.h" void rbytechain(unsigned char *block) { int i; for (i = 0; i < DPA_BLOCK_SIZE; ++i) block[i] ^= block[(i + 1) % DPA_BLOCK_SIZE]; return; } void lbytechain(unsigned char *block) { int i; for (i = DPA_BLOCK_SIZE - 1; i >= 0; --i) block[i] ^= block[(i + 1) % DPA_BLOCK_SIZE]; return; } 8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - /* bitshift.c */ #include #include "include/dpa.h" void rbitshift(unsigned char *block, unsigned int shift) { unsigned int i; unsigned int div; unsigned int mod; unsigned int rel; unsigned char mask; unsigned char remainder; unsigned char sblock[DPA_BLOCK_SIZE]; if (shift) { mask = 0; shift %= 128; div = shift / 8; mod = shift % 8; rel = DPA_BLOCK_SIZE - div; for (i = 0; i < mod; ++i) mask |= (1 << i); for (i = 0; i < DPA_BLOCK_SIZE; ++i) { remainder = ((block[(rel + i - 1) % DPA_BLOCK_SIZE]) & mask) << (8 - mod); sblock[i] = ((block[(rel + i) % DPA_BLOCK_SIZE]) >> mod) | remainder; } } memcpy(block, sblock, DPA_BLOCK_SIZE); } void lbitshift(unsigned char *block, unsigned int shift) { int i; unsigned int div; unsigned int mod; unsigned int rel; unsigned char mask; unsigned char remainder; unsigned char sblock[DPA_BLOCK_SIZE]; if (shift) { mask = 0; shift %= 128; div = shift / 8; mod = shift % 8; rel = DPA_BLOCK_SIZE + div; for (i = 0; i < (8 - mod); ++i) mask |= (1 << i); mask = ~mask; for (i = 0; i < DPA_BLOCK_SIZE; ++i) { remainder = (block[(rel + i + 1) % DPA_BLOCK_SIZE] & mask) >> (8 - mod); sblock[i] = ((block[(rel + i) % DPA_BLOCK_SIZE]) << mod) | remainder; } } memcpy(block, sblock, DPA_BLOCK_SIZE); } 8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - /* S_E.c */ #include "include/dpa.h" /* * The substitution table looks like this: * * (s+0)%256 (s+1)%256 (s+2)%256 (s+3)%256 (s+4)%256 (s+5)%256 (s+6)%256 ... * (s+1)%256 (s+2)%256 (s+3)%256 (s+4)%256 (s+5)%256 (s+6)%256 (s+7)%256 ... * (s+2)%256 (s+3)%256 (s+4)%256 (s+5)%256 (s+6)%256 (s+7)%256 (s+8)%256 ... * (s+3)%256 (s+4)%256 (s+5)%256 (s+6)%256 (s+7)%256 (s+8)%256 (s+9)%256 ... * (s+4)%256 (s+5)%256 (s+6)%256 (s+7)%256 ... * (s+5)%256 (s+6)%256 (s+7)%256 (s+8)%256 ... * (s+6)%256 (s+7)%256 (s+8)%256 (s+9)%256 ... * ... */ void S_E(unsigned char *key, unsigned char *block, unsigned int s) { int i; for (i = 0; i < DPA_BLOCK_SIZE; ++i) block[i] = (key[i] + block[i] + s) % 256; return; } 8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - /* S_D.c */ #include "include/dpa.h" void S_D(unsigned char *key, unsigned char *block, unsigned int s) { int i; for (i = 0; i < DPA_BLOCK_SIZE; ++i) block[i] = ((256 + block[i]) - key[i] - s) % 256; return; } 8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - /* E.c */ #include "include/dpa.h" /* This is the function that is iterated at each round to encrypt */ void E(unsigned char *key, unsigned char *block, unsigned int shift) { rbytechain(block); rbitshift(block, shift); S_E(key, block, shift); } 8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - /* D.c */ #include "include/dpa.h" /* This is the function used to decrypt */ void D(unsigned char *key, unsigned char *block, unsigned int shift) { S_D(key, block, shift); lbitshift(block, shift); lbytechain(block); } 8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - /* blockchain.c */ #include "include/dpa.h" /* Block chaining for BC modes */ void blockchain(unsigned char *dst, unsigned char *src) { int i; for (i = 0; i < DPA_BLOCK_SIZE; ++i) dst[i] = dst[i] ^ src[i]; return; } 8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - /* iv.c */ #include #include #include #include "include/dpa.h" /* Initialization vector */ void IV(unsigned char *block) { int i; srandom(time(NULL) % getpid()); for (i = 0; i < DPA_BLOCK_SIZE; ++i) block[i] = random(); } 8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - /* key.c */ #include #include #include #include #include #include #include "include/dpa.h" /* This is the function used to precompute the subkeys */ void DPA_set_key(DPA_KEY *k, unsigned char *key, unsigned int len) { /* Compute subkey #0 */ checksum128(key, k->subkey[0].key, len); /* Compute subkey #1 -> #15: k.n = H(k.(n-1)%16), where 0 <= n <= 15 */ checksum128(k->subkey[0].key, k->subkey[1].key, DPA_KEY_SIZE); checksum128(k->subkey[1].key, k->subkey[2].key, DPA_KEY_SIZE); checksum128(k->subkey[2].key, k->subkey[3].key, DPA_KEY_SIZE); checksum128(k->subkey[3].key, k->subkey[4].key, DPA_KEY_SIZE); checksum128(k->subkey[4].key, k->subkey[5].key, DPA_KEY_SIZE); checksum128(k->subkey[5].key, k->subkey[6].key, DPA_KEY_SIZE); checksum128(k->subkey[6].key, k->subkey[7].key, DPA_KEY_SIZE); checksum128(k->subkey[7].key, k->subkey[8].key, DPA_KEY_SIZE); checksum128(k->subkey[8].key, k->subkey[9].key, DPA_KEY_SIZE); checksum128(k->subkey[9].key, k->subkey[10].key, DPA_KEY_SIZE); checksum128(k->subkey[10].key, k->subkey[11].key, DPA_KEY_SIZE); checksum128(k->subkey[11].key, k->subkey[12].key, DPA_KEY_SIZE); checksum128(k->subkey[12].key, k->subkey[13].key, DPA_KEY_SIZE); checksum128(k->subkey[13].key, k->subkey[14].key, DPA_KEY_SIZE); checksum128(k->subkey[14].key, k->subkey[15].key, DPA_KEY_SIZE); /* Paranoia: overwrite subkey #0 to prevent a possible biais in H * from revealing informations about the initial key. */ checksum128(k->subkey[15].key, k->subkey[0].key, DPA_KEY_SIZE); /* Compute shifts. Shifts are inverted to break a possible relation * between shiftings and subkeys. The last subkey is used to compute * the first shift, and so on... */ k->subkey[0].shift = hash32(k->subkey[15].key, DPA_KEY_SIZE); k->subkey[1].shift = hash32(k->subkey[14].key, DPA_KEY_SIZE); k->subkey[2].shift = hash32(k->subkey[13].key, DPA_KEY_SIZE); k->subkey[3].shift = hash32(k->subkey[12].key, DPA_KEY_SIZE); k->subkey[4].shift = hash32(k->subkey[11].key, DPA_KEY_SIZE); k->subkey[5].shift = hash32(k->subkey[10].key, DPA_KEY_SIZE); k->subkey[6].shift = hash32(k->subkey[9].key, DPA_KEY_SIZE); k->subkey[7].shift = hash32(k->subkey[8].key, DPA_KEY_SIZE); k->subkey[8].shift = hash32(k->subkey[7].key, DPA_KEY_SIZE); k->subkey[9].shift = hash32(k->subkey[6].key, DPA_KEY_SIZE); k->subkey[10].shift = hash32(k->subkey[5].key, DPA_KEY_SIZE); k->subkey[11].shift = hash32(k->subkey[4].key, DPA_KEY_SIZE); k->subkey[12].shift = hash32(k->subkey[3].key, DPA_KEY_SIZE); k->subkey[13].shift = hash32(k->subkey[2].key, DPA_KEY_SIZE); k->subkey[14].shift = hash32(k->subkey[1].key, DPA_KEY_SIZE); k->subkey[15].shift = hash32(k->subkey[0].key, DPA_KEY_SIZE); } /* And this one for using a file as a secret key */ void DPA_set_keyfile(DPA_KEY *k, char *filename) { int fd; void *key; struct stat sb; fd = open(filename, O_RDONLY); if (fd < 0) { fprintf(stderr, "failed to open %s as a secret key.\n", filename); exit(1); } fstat(fd, &sb); key = (unsigned char *)mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (key == (void *)MAP_FAILED) { fprintf(stderr, "mmap() call failure.\n"); exit(1); } DPA_set_key(k, key, sb.st_size); } 8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - /* data.c */ /* * Warning: ugliest file ;) */ #include #include #include #include #include #include #include #include #include "include/dpa.h" void DPA_set_data(DPA_DATA *d, unsigned char *data, unsigned int len) { d->data = data; d->length = len; } void DPA_set_datafile(DPA_DATA *d, char *filename) { int fd; struct stat sb; fd = open(filename, O_RDONLY); if (fd < 0) { fprintf(stderr, "failed to open data file %s.\n", filename); exit(1); } fstat(fd, &sb); d->data = (unsigned char *)mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (d->data == (void *)MAP_FAILED) { fprintf(stderr, "mmap() call failure.\n"); exit(1); } d->length = sb.st_size; } /* Allocate enough memory to hold the result of encryption/decryption */ void DPA_set_ciphertext(DPA_DATA *d, DPA_DATA *c, int mode, int action) { int sz; sz = 0; if (action == DPA_ENCRYPT) { if (mode == DPA_MODE_ECB) { if ((d->length % DPA_BLOCK_SIZE) == 0) sz = d->length + DPA_BLOCK_SIZE; else sz = d->length + (DPA_BLOCK_SIZE - (d->length % DPA_BLOCK_SIZE)) + DPA_BLOCK_SIZE; } else if (mode == DPA_MODE_CBC) { if ((d->length % DPA_BLOCK_SIZE) == 0) sz = d->length + (DPA_BLOCK_SIZE * 2); else sz = d->length + (DPA_BLOCK_SIZE - (d->length % DPA_BLOCK_SIZE)) + (DPA_BLOCK_SIZE * 2); } } else if (action == DPA_DECRYPT) { if (mode == DPA_MODE_ECB) sz = d->length - DPA_BLOCK_SIZE; else if (mode == DPA_MODE_CBC) sz = d->length - (DPA_BLOCK_SIZE * 2); } c->data = (unsigned char *)mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); if (c->data == (void *)MAP_FAILED) { fprintf(stderr, "mmap() call failure.\n"); exit(1); } c->length = sz; } /* Write the result of encryption/decryption to filename */ void DPA_write_to_file(DPA_DATA *data, char *filename) { int fd; int cnt; int wasfile; wasfile = 0; if (!strcmp(filename, "-")) fd = 1; else { fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0600); if (fd < 0) { fprintf(stderr, "failed to open result file %s.\n", filename); exit(1); } wasfile = 1; } for (cnt = 0; cnt < data->length;) if ((data->length - cnt) < DPA_BLOCK_SIZE) cnt += write(fd, data->data + cnt, data->length - cnt); else cnt += write(fd, data->data + cnt, DPA_BLOCK_SIZE); if (wasfile) close(fd); } /* Write the result of checksum to filename in base 16 */ void DPA_sum_write_to_file(DPA_DATA *data, char *filename) { int fd; int cnt; int cnt2; int wasfile; unsigned char base[] = "0123456789abcdef"; unsigned char buffer[DPA_BLOCK_SIZE * 2 + 2]; wasfile = 0; if (!strcmp(filename, "-")) fd = 1; else { fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0600); if (fd < 0) { fprintf(stderr, "failed to open result file %s.\n", filename); exit(1); } wasfile = 1; } for (cnt = cnt2 = 0; cnt < DPA_BLOCK_SIZE; ++cnt, (cnt2 += 2)) { buffer[cnt2] = base[*(data->data + data->length - DPA_BLOCK_SIZE + cnt) / 16]; buffer[cnt2 + 1] = base[*(data->data + data->length - DPA_BLOCK_SIZE + cnt) % 16]; } buffer[DPA_BLOCK_SIZE * 2] = '\n'; buffer[DPA_BLOCK_SIZE * 2 + 1] = '\0'; write(fd, buffer, DPA_BLOCK_SIZE * 2 + 2); if (wasfile) close(fd); } 8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - /* ecb.c */ /* * Encryption/Decryption in ECB mode. */ #include #include #include #include "include/dpa.h" /* XXX - for better performances, unroll the loops ;) */ void DPA_ecb_encrypt(DPA_KEY *key, DPA_DATA *data, DPA_DATA *cipher) { int j; int cnt; unsigned char *cptr; unsigned char block[DPA_BLOCK_SIZE]; cnt = data->length; cptr = cipher->data; memset(block, 0, 16); for (; cnt > 0; data->data += DPA_BLOCK_SIZE, cptr += DPA_BLOCK_SIZE) { if (cnt < DPA_BLOCK_SIZE) { memcpy(block, data->data, cnt); memset(block + cnt, 0, DPA_BLOCK_SIZE - cnt); } else memcpy(block, data->data, DPA_BLOCK_SIZE); for (j = 0; j < 16; ++j) E(key->subkey[j].key, block, key->subkey[j].shift); memcpy(cptr, block, DPA_BLOCK_SIZE); cnt -= DPA_BLOCK_SIZE; } /* Padding block */ memset(block, 0, DPA_BLOCK_SIZE); if (data->length % DPA_BLOCK_SIZE) block[DPA_BLOCK_SIZE - 1] = DPA_BLOCK_SIZE - data->length % DPA_BLOCK_SIZE; for (j = 0; j < 16; ++j) E(key->subkey[j].key, block, key->subkey[j].shift); memcpy(cptr, block, DPA_BLOCK_SIZE); } void DPA_ecb_decrypt(DPA_KEY *key, DPA_DATA *data, DPA_DATA *cipher) { int j; int cnt; unsigned char padding; unsigned char *cptr; unsigned char block[DPA_BLOCK_SIZE]; /* Data is padded so... we got at least 2 * DPA_BLOCK_SIZE bytes and * data->length / DPA_BLOCK_SIZE should be even */ if ((data->length % DPA_BLOCK_SIZE) || data->length < (2 * DPA_BLOCK_SIZE)) exit(1); /* Extract padding information */ memcpy(block, data->data + data->length - DPA_BLOCK_SIZE, DPA_BLOCK_SIZE); for (j = 15; j >= 0; --j) D(key->subkey[j].key, block, key->subkey[j].shift); padding = block[DPA_BLOCK_SIZE - 1]; cipher->length -= padding; cptr = cipher->data; cnt = data->length - DPA_BLOCK_SIZE; memset(block, 0, DPA_BLOCK_SIZE); for (; cnt > 0; cnt -= DPA_BLOCK_SIZE, data->data += DPA_BLOCK_SIZE, cptr += DPA_BLOCK_SIZE) { memcpy(block, data->data, DPA_BLOCK_SIZE); for (j = 15; j >= 0; --j) D(key->subkey[j].key, block, key->subkey[j].shift); if (cnt >= DPA_BLOCK_SIZE) memcpy(cptr, block, DPA_BLOCK_SIZE); else memcpy(cptr, block, DPA_BLOCK_SIZE - (padding % DPA_BLOCK_SIZE)); } } 8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - /* cbc.c */ /* * Encryption/Decryption in CBC mode. */ #include #include #include #include "include/dpa.h" /* XXX - for better performances, unroll the loops ;) */ void DPA_cbc_encrypt(DPA_KEY *key, DPA_DATA *data, DPA_DATA *cipher) { int j; int cnt; unsigned char *cptr; unsigned char block[DPA_BLOCK_SIZE]; unsigned char iv[DPA_BLOCK_SIZE]; unsigned char xblock[DPA_BLOCK_SIZE]; /* IV */ cptr = cipher->data; IV(iv); memcpy(xblock, iv, DPA_BLOCK_SIZE); for (j = 0; j < 16; ++j) E(key->subkey[j].key, iv, key->subkey[j].shift); memcpy(cptr, iv, DPA_BLOCK_SIZE); cptr += DPA_BLOCK_SIZE; cnt = data->length; memset(block, 0, 16); for (; cnt > 0; data->data += DPA_BLOCK_SIZE, cptr += DPA_BLOCK_SIZE) { if (cnt < DPA_BLOCK_SIZE) { memcpy(block, data->data, cnt); memset(block + cnt, 0, DPA_BLOCK_SIZE - cnt); } else memcpy(block, data->data, DPA_BLOCK_SIZE); blockchain(block, xblock); for (j = 0; j < 16; ++j) E(key->subkey[j].key, block, key->subkey[j].shift); memcpy(xblock, block, DPA_BLOCK_SIZE); memcpy(cptr, block, DPA_BLOCK_SIZE); cnt -= DPA_BLOCK_SIZE; } /* Padding */ memset(block, 0, DPA_BLOCK_SIZE); if (data->length % DPA_BLOCK_SIZE) block[DPA_BLOCK_SIZE - 1] = DPA_BLOCK_SIZE - data->length % DPA_BLOCK_SIZE; blockchain(block, xblock); for (j = 0; j < 16; ++j) E(key->subkey[j].key, block, key->subkey[j].shift); memcpy(cptr, block, DPA_BLOCK_SIZE); } void DPA_cbc_decrypt(DPA_KEY *key, DPA_DATA *data, DPA_DATA *cipher) { int j; int cnt; unsigned char padding; unsigned char *cptr; unsigned char block[DPA_BLOCK_SIZE]; unsigned char xblock[DPA_BLOCK_SIZE]; unsigned char xblockprev[DPA_BLOCK_SIZE]; unsigned char *xorptr; /* * CBC mode uses padding, data->length / DPA_BLOCK_SIZE _MUST_ be even. * Also, we got a block for the IV, at least a block for the data and * a block for the padding information, this makes the size of cryptogram * at least 3 * DPA_BLOCK_SIZE. */ if ((data->length % DPA_BLOCK_SIZE) || data->length < (3 * DPA_BLOCK_SIZE)) exit(1); /* Extract padding information by undoing block chaining on last block */ memcpy(block, data->data + data->length - DPA_BLOCK_SIZE, DPA_BLOCK_SIZE); for (j = 15; j >= 0; --j) D(key->subkey[j].key, block, key->subkey[j].shift); xorptr = data->data + data->length - (DPA_BLOCK_SIZE * 2); blockchain(block, xorptr); padding = block[DPA_BLOCK_SIZE - 1]; cipher->length -= padding; /* Extract Initialization vector */ memcpy(xblock, data->data, DPA_BLOCK_SIZE); for (j = 15; j >= 0; --j) D(key->subkey[j].key, xblock, key->subkey[j].shift); cptr = cipher->data; cnt = data->length - (DPA_BLOCK_SIZE * 2); memset(block, 0, DPA_BLOCK_SIZE); for (data->data += DPA_BLOCK_SIZE; cnt >= DPA_BLOCK_SIZE; cnt -= DPA_BLOCK_SIZE, data->data += DPA_BLOCK_SIZE, cptr += DPA_BLOCK_SIZE) { memcpy(block, data->data, DPA_BLOCK_SIZE); memcpy(xblockprev, block, DPA_BLOCK_SIZE); for (j = 15; j >= 0; --j) D(key->subkey[j].key, block, key->subkey[j].shift); blockchain(block, xblock); if (cnt >= DPA_BLOCK_SIZE) memcpy(cptr, block, DPA_BLOCK_SIZE); else memcpy(cptr, block, DPA_BLOCK_SIZE - (padding % DPA_BLOCK_SIZE)); memcpy(xblock, xblockprev, DPA_BLOCK_SIZE); } } 8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - /* sum.c */ /* NEEDS_FIX */ /* * This is basically a CBC encryption with a fixed IV and fixed key, the * last block being the checksum. This needs a rewrite because there is * no need to allocate memory for the whole ciphertext as only two blocks * are needed. */ #include #include #include #include "include/dpa.h" /* XXX - for better performances, unroll the loops ;) */ void DPA_sum(DPA_KEY *key, DPA_DATA *data, DPA_DATA *cipher) { int j; int cnt; unsigned char *cptr; unsigned char block[DPA_BLOCK_SIZE]; unsigned char iv[DPA_BLOCK_SIZE]; unsigned char xblock[DPA_BLOCK_SIZE]; /* Fixed key */ DPA_set_key(key, (unsigned char *)"deadbeef", 8); /* Fixed IV */ memcpy(iv, "0123456789abcdef", DPA_BLOCK_SIZE); memcpy(xblock, iv, DPA_BLOCK_SIZE); cptr = cipher->data; memcpy(xblock, iv, DPA_BLOCK_SIZE); for (j = 0; j < 16; ++j) E(key->subkey[j].key, iv, key->subkey[j].shift); memcpy(cptr, iv, DPA_BLOCK_SIZE); cptr += DPA_BLOCK_SIZE; cnt = data->length; memset(block, 0, 16); for (; cnt > 0; data->data += DPA_BLOCK_SIZE, cptr += DPA_BLOCK_SIZE) { if (cnt < DPA_BLOCK_SIZE) { memcpy(block, data->data, cnt); memset(block + cnt, 0, DPA_BLOCK_SIZE - cnt); } else memcpy(block, data->data, DPA_BLOCK_SIZE); blockchain(block, xblock); for (j = 0; j < 16; ++j) E(key->subkey[j].key, block, key->subkey[j].shift); memcpy(xblock, block, DPA_BLOCK_SIZE); memcpy(cptr, block, DPA_BLOCK_SIZE); cnt -= DPA_BLOCK_SIZE; } memset(block, 0, DPA_BLOCK_SIZE); if (data->length % DPA_BLOCK_SIZE) block[DPA_BLOCK_SIZE - 1] = DPA_BLOCK_SIZE - data->length % DPA_BLOCK_SIZE; blockchain(block, xblock); for (j = 0; j < 16; ++j) E(key->subkey[j].key, block, key->subkey[j].shift); memcpy(cptr, block, DPA_BLOCK_SIZE); } 8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - /* usage.c */ #include #include #include #include void usage(void) { fprintf(stderr, "usage: dpa -a action -m mode -k key -d data -o outfile\n"); fprintf(stderr, " dpa -s filename\n"); fprintf(stderr, "\taction can be : encrypt, decrypt\n"); fprintf(stderr, "\tmode can be : ecb, cbc\n"); fprintf(stderr, "\tkey can be : \"key\" or file:/path/to/keyfile\n"); fprintf(stderr, "\tdata can be : \"data\" or file:/path/to/datafile\n"); fprintf(stderr, "\toutfile can be: \"-\" (stdout) or a filename\n"); fprintf(stderr, "\twhen -s is used, a checksum of filename is computed\n"); exit (EX_USAGE); } 8<- - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - 8< - - - - - /* main.c */ #include #include #include #include #include "include/dpa.h" int main(int argc, char *argv[]) { int kflag; int dflag; int sflag; int mflag; int aflag; int oflag; int opt; int mode; int action; char *output; DPA_KEY key; DPA_DATA data; DPA_DATA cipher; mode = DPA_MODE_ECB; action = DPA_ENCRYPT; output = "-"; mflag = aflag = kflag = dflag = sflag = oflag = 0; while ((opt = getopt(argc, argv, "a:m:k:d:o:s:")) != -1) { switch (opt) { case 'a': if (!strcmp(optarg, "enc") || !strcmp(optarg, "encrypt")) action = DPA_ENCRYPT; else if (!strcmp(optarg, "dec") || !strcmp(optarg, "decrypt")) action = DPA_DECRYPT; else { fprintf(stderr, "unknown action, expected encrypt or decrypt\n"); return (EX_USAGE); } aflag = 1; break; case 'm': if (!strcmp(optarg, "ecb")) mode = DPA_MODE_ECB; else if (!strcmp(optarg, "cbc")) mode = DPA_MODE_CBC; else { fprintf(stderr, "unknown mode, expected ecb or cbc\n"); return (EX_USAGE); } mflag = 1; break; case 'k': if (strncmp(optarg, "file:", 5) || strlen(optarg) == 5) DPA_set_key(&key, (unsigned char *)optarg, strlen(optarg)); else DPA_set_keyfile(&key, optarg + 5); kflag = 1; break; case 'd': if (strncmp(optarg, "file:", 5) || strlen(optarg) == 5) DPA_set_data(&data, (unsigned char *)optarg, strlen(optarg)); else DPA_set_datafile(&data, optarg + 5); dflag = 1; break; case 'o': output = optarg; oflag = 1; break; case 's': DPA_set_datafile(&data, optarg); sflag = 1; break; default: usage(); } } if ((!aflag || !mflag || !kflag || !dflag) && !sflag) usage(); if (sflag) { DPA_set_ciphertext(&data, &cipher, DPA_MODE_CBC, DPA_ENCRYPT); DPA_sum(&key, &data, &cipher); DPA_sum_write_to_file(&cipher, output); } else { DPA_set_ciphertext(&data, &cipher, mode, action); if (action == DPA_ENCRYPT) { if (mode == DPA_MODE_ECB) DPA_ecb_encrypt(&key, &data, &cipher); else if (mode == DPA_MODE_CBC) DPA_cbc_encrypt(&key, &data, &cipher); } else if (action == DPA_DECRYPT) { if (mode == DPA_MODE_ECB) DPA_ecb_decrypt(&key, &data, &cipher); else if (mode == DPA_MODE_CBC) DPA_cbc_decrypt(&key, &data, &cipher); } DPA_write_to_file(&cipher, output); } return (EX_OK); } |=[ EOF ]=---------------------------------------------------------------=| Traduit par [DegenereScience]DecereBrain, le 3 Novembre 2004, 06:27. Bande-son de ma nuit blanche : Front 242 - PULSE ("Together") Fear Factory - Digimortal ("Back the fuck up") Ministry - Greatest Fits ("N.W.O.") France Info - Bush wins ("world loose") Relu par N-0-X, en Octobre 2007.