コード例 #1
0
ファイル: lab5.py プロジェクト: jainam26/Applied-Crypto
def q3_realistic_aes_cache_attack(
        less_leaky_encipher=lab5_helper.less_leaky_encipher_example):
    """Question 3: Realistic cache timing attack on AES

    In this problem, you're still acting as Mallory and trying to perform a cache timing attack. 
    There's just one new hurdle that you must overcome. (As a consequence: do not attempt to solve 
    this problem until you have already solved Question 2.)

    I made one unrealistic assumption in the 'leaky_encipher' routine:
    I provided you with the set of bytes that were accessed in the final round of AES.
    Real caches unfortunately do not provide byte-level accuracy. I'll spare you the details; 
    the upshot is that it is common 16 values of the SubBytes array to fit within a single cacheline.

    That is: suppose Bob weren't running AES at all, but instead only makes a single table 
    lookup S[x] into the SubBytes array S. By observing which portion of the cache is activated, 
    a cache attack would let Mallory know whether Bob's access x was in the range 0-15, or the range 16-31, 
    or the range 32-47, ... or the range 240-255. However, Mallory couldn't tell anything beyond that. 
    Put another way: Mallory can learn the upper 4 bits of x but not the lower 4 bits.

    The 'lab5_helper.py' file contains Bob's code for this problem. It is the routine less_leaky_encipher_example 
    that only provides (the set of) the upper 4 bits of the location of each table lookup to Mallory; it otherwise 
    runs similarly to the code in Question 2.

    Your Task:
        Perform a cache timing attack even in this restricted setting. Your input-output behavior should 
        be the same as stated in Question 2.
        (The solution to this problem is pretty much exactly what Osvik, Shamir, and Tromer did to break 
        Linux's full disk encryption software, called dmcrypt.)
    """
    key = ''
    for index in range(0, 16):
        byteList = []
        for i in range(0, 256):
            byteList.append(i)
        while (len(byteList) != 1):
            randomMessage = os.urandom(16)
            result = less_leaky_encipher(randomMessage)
            cipherText = result[0]
            hexCipher = cipherText.hex()
            cipher_int = [
                int(hexCipher[i] + hexCipher[i + 1], 16)
                for i in range(0, len(hexCipher), 2)
            ]
            cache_line = list(result[1])
            test = cipher_int[index]
            candidate = []
            for x in byteList:
                temp = test ^ x
                val = lab5_helper.Sinv(temp)
                if val >> 4 in cache_line:
                    candidate.append(x)
            byteList = candidate
        temp = hex(byteList[0])
        temp = temp[2:]
        temp = temp.zfill(2)
        key += temp
    return key
コード例 #2
0
 def find_sinv(sinv_input):
     myList = []
     outputList = []
     for a in range(16):
         myList.append(16 * sinv_input + a)
     # print(myList)
     for x in range(256):
         res = lab5_helper.Sinv(x)
         for i in range(16):
             if res == myList[i]:
                 outputList.append(hex(x)[2:].rjust(2, "0"))
     return outputList
コード例 #3
0
def q3_realistic_aes_cache_attack(less_leaky_encipher=lab5_helper.less_leaky_encipher_example):
    """Realistic cache timing attack on AES"""

    def get_rand_string(perm_size):
        return ''.join(choices(printable, k=perm_size))
    index=0
    
    matrixP=[]
    last_round_key=""

    check=[0,0]
    intersection=[0,0,0]

        
    
    for i in range(0,31,2):
        matrixK=[]
        for s in range(40):
            index+=1
            plaintext=get_rand_string(16)
            cipher,cache=less_leaky_encipher(plaintext.encode())
            lists=[]
            hexC=binascii.hexlify(cipher)
            firstByte=hexC[i:i+2]
            poss=[]
            for j in range(256):
                key="{:02x}".format(j)
                xor=int(strxor(binascii.unhexlify(firstByte),binascii.unhexlify(key)).hex(),16)
                b=lab5_helper.Sinv(xor)
                poss.append(b)
            key_poss=[]
            for k,p in enumerate(poss):
                if p>>4 in cache:
                    key_poss.append(k)

            matrixK.append(key_poss)
            matrixP.append(poss)

        temp=set(matrixK[0])
        for m in range(len(matrixK)):
            temp=temp&set(matrixK[m])
        ans=list(temp)

        if len(ans)!=1:
            print("not done")
            break
        else:
            res=hex(ans[0])[2:]
            if len(res) ==1:
                res='0'+res
            last_round_key+=res
    return last_round_key
コード例 #4
0
 def find_sinv(sinv_input):
     for x in range(256):
         res = lab5_helper.Sinv(x)
         if res == sinv_input:
             return hex(x)[2:].rjust(2, "0")
コード例 #5
0
ファイル: lab5.py プロジェクト: jainam26/Applied-Crypto
def q2_simple_aes_cache_attack(
        leaky_encipher=lab5_helper.leaky_encipher_example):
    """Question 2: Simple cache timing attack on AES

    As Mallory, you must determine the last round key at the very end of AES.
    Since you are a legitimate user on the machine, you're welcome to encipher files 
    whenever you'd like, and you can also introspect the state of the cache using techniques 
    like Prime+Probe that we discussed in class.

    Bob's code for file enciphering is provided as the 'leaky_encipher' routine passed to this function
    (Note: you can find an example of the 'leaky_encipher' routine in 'lab5_helper.py'). 

    Bob's routine does both of the above operations for you: it enciphers a file and then helpfully 
    tells you how the 10th round S-box lookups have influenced the state of the cache, so you don't 
    need to inspect it yourself. Hence, 'leaky_encipher' has two outputs: the actual ciphertext plus a 
    Python set stating which cachelines are accessed during the final round's SubBytes operation. 

    Recall that SubBytes works on a byte-by-byte basis: each byte of the state is used to fetch a 
    specific location within the S-box array. The 'leaky_encipher' routine tells you which elements of 
    the S-box array were accessed, which as you recall from Lecture 10 is correlated with the key. 

    I'll state two caveats upfront:
        -   This problem conducts a last-round attack: that is, our attack scenario is explained in lecture 10 slides
            As a result, the cache lines are correlated with the last round key of AES, and not the first round key. 
            This is acceptable to Mallory because there's a known, public permutation that relates all of the round keys.

            In fact in my helper file 'aeskeyexp.py' I have provided a routine 'aes128_lastroundkey' that converts first -> last round keys. 
            I didn't actually give you the converse, but I assure you that it's equally as easy to compute. 
            Let's just declare victory as Mallory if we can find the last round key.

        -   Mallory cannot interrupt the state of execution of AES. She can only observe the contents of the cache after 
            it is finished. As a result: leaky_encipher only tells you the **set** of all table lookups made to the 10th 
            round S-box across all 16 bytes, without telling you which lookup is associated with which byte.

    Your Task:
        Complete this function with a solution that calls 'leaky_encipher' as many times as you wish 
        and uses the results to determine the key.
    Args:
        leaky_encipher  (func)  : performs an AES encipher on the input 16-bytes input `file_bytes`
            Args:
                file_bytes  (bytes)     : 16-bytes input to be passed to AES for enciphering
            Output:
                ret         (str, set)  : tuple with the actual ciphertext and a Python set stating which cachelines 
                                            are accessed during the final round's SubBytes operation.
    Output:
        ret             (str)   : hex-encoded 16-bytes string that represents the lastroundkey of AES in leaky_encipher
    How to verify your answer:
        assert(q2_simple_aes_cache_attack() ==
            aeskeyexp.aes128_lastroundkey(lab5_helper.TEST_KEY).hex())
    Note:
        The file `lab5_helper.py` contains some helper functions that you find useful in solving this question.
    """
    key = ''
    for index in range(0, 16):
        byteList = []
        for i in range(0, 256):
            byteList.append(i)
        while (len(byteList) != 1):
            randomMessage = os.urandom(16)
            result = leaky_encipher(randomMessage)
            cipherText = result[0]
            hexCipher = cipherText.hex()
            cipher_int = [
                int(hexCipher[i] + hexCipher[i + 1], 16)
                for i in range(0, len(hexCipher), 2)
            ]
            cache_line = list(result[1])
            test = cipher_int[index]
            candidate = []
            for x in byteList:
                temp = test ^ x
                val = lab5_helper.Sinv(temp)
                if val in cache_line:
                    candidate.append(x)
            byteList = candidate
        temp = hex(byteList[0])
        temp = temp[2:]
        temp = temp.zfill(2)
        key += temp
    return key