11

私は暗号化のコースを受講していますが、課題が残っています。手順は次のとおりです。

平文のplain6.txtはDESでencrypt6.datに暗号化されており、8文字の文字列(64ビットのうち8番目のビットはすべて無視されます)として与えられた64ビットの鍵を使用します。すべての文字は文字(小文字または大文字)です。大文字と小文字) と数字 (0 ~ 9)。

課題を完了するには、2 月 12 日 23.59 までに暗号鍵を送ってください。

注: 8 バイト (64 ビット) のキーを取得する予定です。各バイトは、DES で使用されない最下位ビットを除いて、キーの対応するバイトと一致する必要があります。

Python での最初の試みのコードは次のとおりです。

import time
from Crypto.Cipher import DES

class BreakDES(object):
    def __init__(self, file, passwordLength = 8, testLength = 8):
        self.file = file
        self.passwordLength = passwordLength
        self.testLength = testLength
        self.EncryptedFile = open(file + '.des')
        self.DecryptedFile = open(file + '.txt')
        self.encryptedChunk = self.EncryptedFile.read(self.testLength)
        self.decryptedChunk = self.DecryptedFile.read(self.testLength)
        self.start = time.time()
        self.counter = 0
        self.chars = range(48, 58) + range(65, 91) + range(97, 123)
        self.key = False
        self.broken = False

        self.testPasswords(passwordLength, 0, '')

        if not self.broken:
            print "Password not found."

        duration = time.time() - self.start

        print "Brute force took %.2f" % duration
        print "Tested %.2f per second" % (self.counter / duration)

    def decrypt(self):
        des = DES.new(self.key.decode('hex'))
        if des.decrypt(self.encryptedChunk) == self.decryptedChunk:
            self.broken = True
            print "Password found: 0x%s" % self.key
        self.counter += 1

    def testPasswords(self, width, position, baseString):
            for char in self.chars:
                if(not self.broken):
                    if position < width:
                        self.testPasswords(width, position + 1, baseString + "%c" % char)
                        self.key = (baseString + "%c" % char).encode('hex').zfill(16)
                        self.decrypt()

# run it for password length 4
BreakDES("test3", 4)

私は 60.000 試行/秒の速度を得ています。62 文字を超える 8 バイトのパスワードは 13 兆通りの可能性を提供します。つまり、この速度では、解決するのに 130 年かかることになります。これは効率的な実装ではなく、C のような高速な言語やそのフレーバーで高速化できることはわかっていますが、それらでプログラミングしたことはありません。速度が 10 倍になったとしても、1 秒あたり 10,000,000,000 時間の範囲に入るには、まだ大きな飛躍があります。

私は何が欠けていますか?これは弱いキーであるはずです:)。まあ、完全な 256 文字セットよりも弱いです。

編集

割り当てに関するいくつかのあいまいさのため、ここに完全な説明とキャリブレーション用のいくつかのテスト ファイルがあります: http://users.abo.fi/ipetre/crypto/assignment6.html

編集2

これは、i7 2600K でコアあたり約 2.000.000 パスワード/秒を取得する大雑把な C 実装です。パスワードの最初の文字を指定する必要があり、異なるコア/コンピューターで複数のインスタンスを手動で実行できます。これを使用して、4台のコンピューターで数時間以内に問題を解決できました。

#include <stdio.h>      /* fprintf */
#include <stdlib.h>     /* malloc, free, exit */
#include <unistd.h>
#include <string.h>     /* strerror */
#include <signal.h>
#include <openssl/des.h>

static long long unsigned nrkeys = 0; // performance counter

char *
Encrypt( char *Key, char *Msg, int size)
{
        static char*    Res;
        free(Res);
        int             n=0;
        DES_cblock      Key2;
        DES_key_schedule schedule;
        Res = ( char * ) malloc( size );
        /* Prepare the key for use with DES_ecb_encrypt */
        memcpy( Key2, Key,8);
        DES_set_odd_parity( &Key2 );
        DES_set_key_checked( &Key2, &schedule );
        /* Encryption occurs here */
        DES_ecb_encrypt( ( unsigned char (*) [8] ) Msg, ( unsigned char (*) [8] ) Res,
                           &schedule, DES_ENCRYPT );
        return (Res);
}

char *
Decrypt( char *Key, char *Msg, int size)
{
        static char*    Res;
        free(Res);
        int             n=0;
        DES_cblock      Key2;
        DES_key_schedule schedule;
        Res = ( char * ) malloc( size );
        /* Prepare the key for use with DES_ecb_encrypt */
        memcpy( Key2, Key,8);
        DES_set_odd_parity( &Key2 );
        DES_set_key_checked( &Key2, &schedule );
        /* Decryption occurs here */
        DES_ecb_encrypt( ( unsigned char (*) [8]) Msg, ( unsigned char (*) [8]) Res,
                           &schedule, DES_DECRYPT );
        return (Res);
}

void ex_program(int sig);

int main(int argc, char *argv[])
{
    (void) signal(SIGINT, ex_program);

    if ( argc != 4 ) /* argc should be 2 for correct execution */
    {
        printf( "Usage: %s ciphertext plaintext keyspace \n", argv[0] );
        exit(1);
    }

    FILE *f, *g;
    int counter, i, prime = 0, len = 8;
    char cbuff[8], mbuff[8];
    char letters[] = "02468ACEGIKMOQSUWYacegikmoqsuwy";
    int nbletters = sizeof(letters)-1;
    int entry[len];
    char *password, *decrypted, *plain;

    if(atoi(argv[3]) > nbletters-2) {
        printf("The range must be between 0-%d\n", nbletters-2);
        exit(1);
    }
    prime = atoi(argv[1])

    // read first 8 bytes of the encrypted file
    f = fopen(argv[1], "rb");
    if(!f) {
        printf("Unable to open the file\n");
        return 1;
    }
    for (counter = 0; counter < 8; counter ++) cbuff[counter] = fgetc(f);
    fclose(f);

    // read first 8 bytes of the plaintext file
    g = fopen(argv[2], "r");
    if(!f) {
        printf("Unable to open the file\n");
        return 1;
    }
    for (counter = 0; counter < 8; counter ++) mbuff[counter] = fgetc(g);
    fclose(g);

    plain = malloc(8);
    memcpy(plain, mbuff, 8);

    // fill the keys
    for(i=0 ; i<len ; i++) entry[i] = 0;
    entry[len-1] = prime;

    // loop until the length is reached
    do {
        password = malloc(8);
        decrypted = malloc(8);

        // build the pasword
        for(i=0 ; i<len ; i++) password[i] = letters[entry[i]];
        nrkeys++;

        // end of range and notices
        if(nrkeys % 10000000 == 0) {
            printf("Current key: %s\n", password);
            printf("End of range ");
            for(i=0; i<len; i++) putchar(letters[lastKey[i]]);
            putchar('\n');
        }

        // decrypt
        memcpy(decrypted,Decrypt(password,cbuff,8), 8);

        // compare the decrypted with the mbuff
        // if they are equal, exit the loop, we have the password
        if (strcmp(mbuff, decrypted) == 0)
        {
            printf("We've got it! The key is: %s\n", password);
            printf("%lld keys searched\n", nrkeys);
            exit(0);
        }

        free(password);
        free(decrypted);

        // spin up key until it overflows
        for(i=0 ; i<len && ++entry[i] == nbletters; i++) entry[i] = 0;
    } while(i<len);

    return 0;
}

void ex_program(int sig) {
 printf("\n\nProgram terminated %lld keys searched.\n", nrkeys);
 (void) signal(SIGINT, SIG_DFL);
 exit(0);
}
4

5 に答える 5

6

I would assume the desired solution is to actually implement the algorithmn. Then, since your're decrypting yourself, you can bail early, which, assuming the plain text is also A-Za-z0-9, gives you a 98% chance of being able to stop after decrypting a single byte, a 99.97% chance of stoping after decrypting 2 bytes, and a 99.9995% chance of stopping after 3 bytes.

Also, use C or Ocaml or something like that. You're probably spending MUCH more time doing string manipulation than you are doing cryption. Or, at least use multi-processing and spin up all your cores...

于 2012-02-02T20:11:31.057 に答える
5

明らかな 256 倍の速度向上があります。1 バイトあたり 1 ビットはキーの一部ではありません。DES には 56 ビットのキーしかありませんが、1 つが 64 ビットで渡されます。それがどのビットであるかを把握し、同等の文字を破棄します。

于 2012-02-02T22:47:52.070 に答える
2

私はかなりの助けを借りてきましたが、これは C での解決策です。私は C の初心者なので、おそらくバグや悪い習慣でいっぱいですが、うまくいきます。

CodeInChaos が把握したように、テストする必要があるのは 31 文字だけです。これは、DES がキーの 8 ビットごとを無視し、たとえば ASCII 文字を作成しb: *0110001*0c: *0110001*1キーの一部として使用した場合の暗号化/復号化が同一であるためです。

DES 復号化に OpenSSL ライブラリを使用しています。私のマシンでは、1 秒あたり約 180 万個のパスワードの速度が達成され、キー スペース全体をテストする合計時間は約 5 日間になります。これは締め切りを 1 日下回ります。上記のPythonコードよりもはるかに優れており、これは数年の領域にあります。

まだ改善の余地があり、おそらくコードは最適化され、スレッド化される可能性があります。すべてのコアを使用できれば、時間は 1 日強に短縮されると見積もっていますが、スレッド化の経験はまだありません。

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <openssl/des.h>

static long long unsigned nrkeys = 0; // performance counter

char *
Encrypt( char *Key, char *Msg, int size)
{
        static char*    Res;
        free(Res);
        int             n=0;
        DES_cblock      Key2;
        DES_key_schedule schedule;
        Res = ( char * ) malloc( size );
        /* Prepare the key for use with DES_ecb_encrypt */
        memcpy( Key2, Key,8);
        DES_set_odd_parity( &Key2 );
        DES_set_key_checked( &Key2, &schedule );
        /* Encryption occurs here */
        DES_ecb_encrypt( ( unsigned char (*) [8] ) Msg, ( unsigned char (*) [8] ) Res,
                           &schedule, DES_ENCRYPT );
         return (Res);
}

char *
Decrypt( char *Key, char *Msg, int size)
{
        static char*    Res;
        free(Res);
        int             n=0;
        DES_cblock      Key2;
        DES_key_schedule schedule;
        Res = ( char * ) malloc( size );
        /* Prepare the key for use with DES_ecb_encrypt */
        memcpy( Key2, Key,8);
        DES_set_odd_parity( &Key2 );
        DES_set_key_checked( &Key2, &schedule );
        /* Decryption occurs here */
        DES_ecb_encrypt( ( unsigned char (*) [8]) Msg, ( unsigned char (*) [8]) Res,
                           &schedule, DES_DECRYPT );
        return (Res);
}

void ex_program(int sig);

int main()
{
    (void) signal(SIGINT, ex_program);

    FILE *f, *g; // file handlers
    int counter, i, len = 8; // counters and password length
    char cbuff[8], mbuff[8]; // buffers
    char letters[] = "02468ACEGIKMOQSUWYacegikmoqsuwy"; // reduced letter pool for password brute force
    int nbletters = sizeof(letters)-1;
    int entry[len];
    char *password, *decrypted;

    // read first 8 bytes of the encrypted file
    f = fopen("test2.dat", "rb");
    if(!f) {
        printf("Unable to open the file\n");
        return 1;
    }
    for (counter = 0; counter < 8; counter ++) cbuff[counter] = fgetc(f);
    fclose(f);

    // read first 8 bytes of the plaintext file
    g = fopen("test2.txt", "r");
    if(!f) {
        printf("Unable to open the file\n");
        return 1;
    }
    for (counter = 0; counter < 8; counter ++) mbuff[counter] = fgetc(g);
    fclose(g);

    // fill the initial key
    for(i=0 ; i<len ; i++) entry[i] = 0;

    // loop until the length is reached
    do {
        password = malloc(8);
        decrypted = malloc(8);

        // build the pasword
        for(i=0 ; i<len ; i++) password[i] = letters[entry[i]];
        nrkeys++;

        if(nrkeys % 10000000 == 0) {
            printf("Current key: %s\n", password);
        }

        // decrypt
        memcpy(decrypted,Decrypt(password,cbuff,8), 8);

        // compare the decrypted with the mbuff
        // if they are equal, exit the loop, we have the password
        if (strcmp(mbuff, decrypted) == 0)
        {
            printf("We've got it! The key is: %s\n", password);
            printf("%lld keys searched", nrkeys);
            exit(0);
        }

        free(password);
        free(decrypted);

        // spin up key until it overflows
        for(i=0 ; i<len && ++entry[i] == nbletters; i++) entry[i] = 0;
    } while(i<len);

    return 0;
}

void ex_program(int sig) {
 printf("\n\nProgram terminated %lld keys searched.\n", nrkeys);
 (void) signal(SIGINT, SIG_DFL);
 exit(0);
}
于 2012-02-05T22:14:34.203 に答える
1

この回答は、他のより具体的な提案を補完するものかもしれませんが、最初に行うべきことはプロファイラーを実行することです。

ここに本当に素晴らしい例があります:

どのように Python スクリプトをプロファイリングできますか?

編集:

この特定のタスクについては、役に立たないことを理解しています。10 GHz の試用周波数は... 1 台のマシンでそれ未満の周波数を使用するのは困難です。おそらく、利用可能なハードウェアについて言及することができます。また、数時間実行することを目的としないでください。1 週間以内に妥当な確率で成功する方法を見つけたら、その方法を改善しながら実行させます。

于 2012-02-02T20:05:44.457 に答える
1

課題の言葉遣いに気付かずにはいられません。実際には、DES の実装やクラッカーを自分で提供するように要求されているわけではありません。もしそうなら、John the Ripperhashcatなどのツールを検討してみてはいかがでしょうか。

于 2012-02-02T20:32:15.173 に答える