tinyAES

Post your FreeBASIC source, examples, tips and tricks here. Please don’t post code without including an explanation.
srvaldez
Posts: 3425
Joined: Sep 25, 2005 21:54

tinyAES

Post by srvaldez »

an attempt at translating tiny-AES-c to FB https://github.com/kokke/tiny-AES-c

AES.bi

Code: Select all

#pragma once

#include once "crt/string.bi"

extern "C"

#define _AES_H_

#ifndef CBC
    #define CBC 1
#endif
#ifndef ECB
    #define ECB 1
#endif
#ifndef CTR
    #define CTR 1
#endif
#define AES_BLOCKLEN 16 ' Block length in bytes - AES is 128b block only

#if defined(AES256) and (AES256 = 1)
    #define AES_KEYLEN 32
    #define AES_keyExpSize 240
#elseif defined(AES192) and (AES192 = 1)
    #define AES_KEYLEN 24
    #define AES_keyExpSize 208
#else
    #define AES_KEYLEN 16   ' Key length in bytes
    #define AES_keyExpSize 176
    #ifndef AES128
        #define AES128 1
    #endif
    #if AES128 = 0
        #define AES128 1
    #endif
#endif

type AES_ctx
    RoundKey(0 to AES_keyExpSize-1) as ubyte
    #if (defined(CBC) and (CBC = 1)) or (defined(CTR) and (CTR = 1))
        Iv(0 to AES_BLOCKLEN-1) as ubyte
    #endif
end type

declare sub AES_init_ctx(byval ctx as AES_ctx ptr, byval key as const zstring ptr)
#if (defined(CBC) and (CBC = 1)) or (defined(CTR) and (CTR = 1))
    declare sub AES_init_ctx_iv(byval ctx as AES_ctx ptr, byval key as const zstring ptr, byval iv as const zstring ptr)
    declare sub AES_ctx_set_iv(byval ctx as AES_ctx ptr, byval iv as const zstring ptr)
#endif
#if defined(ECB) and (ECB = 1)
    ' buffer size is exactly AES_BLOCKLEN bytes; 
    ' you need only AES_init_ctx as IV is not used in ECB 
    declare sub AES_ECB_encrypt(byval ctx as const AES_ctx ptr, byval buf as zstring ptr)
    declare sub AES_ECB_decrypt(byval ctx as const AES_ctx ptr, byval buf as zstring ptr)
#endif
#if defined(CBC) and (CBC = 1)
    ' buffer size MUST be mutile of AES_BLOCKLEN;
    ' Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
    ' NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv()
    '        no IV should ever be reused with the same key 
    declare sub AES_CBC_encrypt_buffer(byval ctx as AES_ctx ptr, byval buf as zstring ptr, byval length as uinteger)
    declare sub AES_CBC_decrypt_buffer(byval ctx as AES_ctx ptr, byval buf as zstring ptr, byval length as uinteger)
#endif
#if defined(CTR) and (CTR = 1)

    ' Same function for encrypting as for decrypting. 
    ' IV is incremented for every block, and used after encryption as XOR-compliment for output
    ' Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
    ' NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv()
    '        no IV should ever be reused with the same key 
    declare sub AES_CTR_xcrypt_buffer(byval ctx as AES_ctx ptr, byval buf as zstring ptr, byval length as uinteger)
#endif

const Nb = 4
#if defined(AES256) and (AES256 = 1)
    #define Nk 8
    #define Nr 14
#elseif defined(AES192) and (AES192 = 1)
    #define Nk 6
    #define Nr 12
#else
    #define Nk 4        ' The number of 32 bit words in a key.
    #define Nr 10       ' The number of rounds in AES Cipher.
#endif

const MULTIPLY_AS_A_FUNCTION = 0
dim shared sbox(0 to 255) as const ubyte = {&h63, &h7c, &h77, &h7b, &hf2, &h6b, &h6f, &hc5, &h30, &h01, _
    &h67, &h2b, &hfe, &hd7, &hab, &h76, &hca, &h82, &hc9, &h7d, &hfa, &h59, &h47, &hf0, &had, &hd4, &ha2, _
    &haf, &h9c, &ha4, &h72, &hc0, &hb7, &hfd, &h93, &h26, &h36, &h3f, &hf7, &hcc, &h34, &ha5, &he5, &hf1, _
    &h71, &hd8, &h31, &h15, &h04, &hc7, &h23, &hc3, &h18, &h96, &h05, &h9a, &h07, &h12, &h80, &he2, &heb, _
    &h27, &hb2, &h75, &h09, &h83, &h2c, &h1a, &h1b, &h6e, &h5a, &ha0, &h52, &h3b, &hd6, &hb3, &h29, &he3, _
    &h2f, &h84, &h53, &hd1, &h00, &hed, &h20, &hfc, &hb1, &h5b, &h6a, &hcb, &hbe, &h39, &h4a, &h4c, &h58, _
    &hcf, &hd0, &hef, &haa, &hfb, &h43, &h4d, &h33, &h85, &h45, &hf9, &h02, &h7f, &h50, &h3c, &h9f, &ha8, _
    &h51, &ha3, &h40, &h8f, &h92, &h9d, &h38, &hf5, &hbc, &hb6, &hda, &h21, &h10, &hff, &hf3, &hd2, &hcd, _
    &h0c, &h13, &hec, &h5f, &h97, &h44, &h17, &hc4, &ha7, &h7e, &h3d, &h64, &h5d, &h19, &h73, &h60, &h81, _
    &h4f, &hdc, &h22, &h2a, &h90, &h88, &h46, &hee, &hb8, &h14, &hde, &h5e, &h0b, &hdb, &he0, &h32, &h3a, _
    &h0a, &h49, &h06, &h24, &h5c, &hc2, &hd3, &hac, &h62, &h91, &h95, &he4, &h79, &he7, &hc8, &h37, &h6d, _
    &h8d, &hd5, &h4e, &ha9, &h6c, &h56, &hf4, &hea, &h65, &h7a, &hae, &h08, &hba, &h78, &h25, &h2e, &h1c, _
    &ha6, &hb4, &hc6, &he8, &hdd, &h74, &h1f, &h4b, &hbd, &h8b, &h8a, &h70, &h3e, &hb5, &h66, &h48, &h03, _
    &hf6, &h0e, &h61, &h35, &h57, &hb9, &h86, &hc1, &h1d, &h9e, &he1, &hf8, &h98, &h11, &h69, &hd9, &h8e, _
    &h94, &h9b, &h1e, &h87, &he9, &hce, &h55, &h28, &hdf, &h8c, &ha1, &h89, &h0d, &hbf, &he6, &h42, &h68, _
    &h41, &h99, &h2d, &h0f, &hb0, &h54, &hbb, &h16}
#if (defined(CBC) and CBC = 1) or (defined(ECB) and ECB = 1)
    dim shared rsbox(0 to 255) as const ubyte = { _
        &h52, &h09, &h6a, &hd5, &h30, &h36, &ha5, &h38, &hbf, &h40, &ha3, &h9e, &h81, &hf3, &hd7, &hfb, _
        &h7c, &he3, &h39, &h82, &h9b, &h2f, &hff, &h87, &h34, &h8e, &h43, &h44, &hc4, &hde, &he9, &hcb, _
        &h54, &h7b, &h94, &h32, &ha6, &hc2, &h23, &h3d, &hee, &h4c, &h95, &h0b, &h42, &hfa, &hc3, &h4e, _
        &h08, &h2e, &ha1, &h66, &h28, &hd9, &h24, &hb2, &h76, &h5b, &ha2, &h49, &h6d, &h8b, &hd1, &h25, _
        &h72, &hf8, &hf6, &h64, &h86, &h68, &h98, &h16, &hd4, &ha4, &h5c, &hcc, &h5d, &h65, &hb6, &h92, _
        &h6c, &h70, &h48, &h50, &hfd, &hed, &hb9, &hda, &h5e, &h15, &h46, &h57, &ha7, &h8d, &h9d, &h84, _
        &h90, &hd8, &hab, &h00, &h8c, &hbc, &hd3, &h0a, &hf7, &he4, &h58, &h05, &hb8, &hb3, &h45, &h06, _
        &hd0, &h2c, &h1e, &h8f, &hca, &h3f, &h0f, &h02, &hc1, &haf, &hbd, &h03, &h01, &h13, &h8a, &h6b, _
        &h3a, &h91, &h11, &h41, &h4f, &h67, &hdc, &hea, &h97, &hf2, &hcf, &hce, &hf0, &hb4, &he6, &h73, _
        &h96, &hac, &h74, &h22, &he7, &had, &h35, &h85, &he2, &hf9, &h37, &he8, &h1c, &h75, &hdf, &h6e, _
        &h47, &hf1, &h1a, &h71, &h1d, &h29, &hc5, &h89, &h6f, &hb7, &h62, &h0e, &haa, &h18, &hbe, &h1b, _
        &hfc, &h56, &h3e, &h4b, &hc6, &hd2, &h79, &h20, &h9a, &hdb, &hc0, &hfe, &h78, &hcd, &h5a, &hf4, _
        &h1f, &hdd, &ha8, &h33, &h88, &h07, &hc7, &h31, &hb1, &h12, &h10, &h59, &h27, &h80, &hec, &h5f, _
        &h60, &h51, &h7f, &ha9, &h19, &hb5, &h4a, &h0d, &h2d, &he5, &h7a, &h9f, &h93, &hc9, &h9c, &hef, _
        &ha0, &he0, &h3b, &h4d, &hae, &h2a, &hf5, &hb0, &hc8, &heb, &hbb, &h3c, &h83, &h53, &h99, &h61, _
        &h17, &h2b, &h04, &h7e, &hba, &h77, &hd6, &h26, &he1, &h69, &h14, &h63, &h55, &h21, &h0c, &h7d }
#endif
dim shared Rcon(0 to 10) as const ubyte = {&h8d, &h01, &h02, &h04, &h08, &h10, &h20, &h40, &h80, &h1b, &h36}
#define getSBoxValue(num) sbox((num))

private sub KeyExpansion(byval RoundKey as ubyte ptr, byval Key as const ubyte ptr)
    dim i as ulong
    dim j as ulong
    dim k as ulong
    dim tempa(0 to 3) as ubyte
    i = 0
    while i < Nk
        RoundKey[((i * 4) + 0)] = Key[((i * 4) + 0)]
        RoundKey[((i * 4) + 1)] = Key[((i * 4) + 1)]
        RoundKey[((i * 4) + 2)] = Key[((i * 4) + 2)]
        RoundKey[((i * 4) + 3)] = Key[((i * 4) + 3)]
        i += 1
    wend
    i = Nk
    while i < (Nb * (Nr + 1))
        scope
            k = (i - 1) * 4
            tempa(0) = RoundKey[(k + 0)]
            tempa(1) = RoundKey[(k + 1)]
            tempa(2) = RoundKey[(k + 2)]
            tempa(3) = RoundKey[(k + 3)]
        end scope
        if (i mod Nk) = 0 then
            scope
                dim u8tmp as const ubyte = tempa(0)
                tempa(0) = tempa(1)
                tempa(1) = tempa(2)
                tempa(2) = tempa(3)
                tempa(3) = u8tmp
            end scope
            scope
                tempa(0) = sbox(tempa(0))
                tempa(1) = sbox(tempa(1))
                tempa(2) = sbox(tempa(2))
                tempa(3) = sbox(tempa(3))
            end scope
            tempa(0) = tempa(0) xor Rcon((i / Nk))
        end if
#if defined(AES256) and (AES256 = 1)
    if (i mod Nk = 4) then
        ' Function Subword()
        scope
            tempa(0) = getSBoxValue(tempa(0))
            tempa(1) = getSBoxValue(tempa(1))
            tempa(2) = getSBoxValue(tempa(2))
            tempa(3) = getSBoxValue(tempa(3))
        end scope
    end if
#endif
        j = i * 4
        k = (i - Nk) * 4
        RoundKey[(j + 0)] = RoundKey[(k + 0)] xor tempa(0)
        RoundKey[(j + 1)] = RoundKey[(k + 1)] xor tempa(1)
        RoundKey[(j + 2)] = RoundKey[(k + 2)] xor tempa(2)
        RoundKey[(j + 3)] = RoundKey[(k + 3)] xor tempa(3)
        i += 1
    wend
end sub

private sub AES_init_ctx(byval ctx as AES_ctx ptr, byval key as const zstring ptr)
    KeyExpansion(@(ctx->RoundKey(0)), key)
end sub

#if (defined(CBC) and (CBC = 1)) or (defined(CTR) and (CTR = 1))
    sub AES_init_ctx_iv(byval ctx as AES_ctx ptr, byval key as const zstring ptr, byval iv as const zstring ptr)
        KeyExpansion(@(ctx->RoundKey(0)), key)
        memcpy (@(ctx->Iv(0)), iv, AES_BLOCKLEN)
    end sub
    sub AES_ctx_set_iv(byval ctx as AES_ctx ptr, byval iv as const zstring ptr)
        memcpy (@(ctx->Iv(0)), iv, AES_BLOCKLEN)
    end sub
#endif

private sub AddRoundKey(byval round as ubyte, byval state as ubyte ptr, byval RoundKey as const ubyte ptr)
    dim i as ubyte
    dim j as ubyte
    i = 0
    while i < 4
        j = 0
        while j < 4
            state[i*4+j] xor= RoundKey[(round * Nb * 4) + (i * Nb) + j]
            j += 1
        wend
        i += 1
    wend
end sub

private sub SubBytes(byval state as ubyte ptr)
    dim i as ubyte
    dim j as ubyte
    i = 0
    while i < 4
        j = 0
        while j < 4
            state[j*4+i] = sbox(state[j*4+i])
            j += 1
        wend
        i += 1
    wend
end sub

private sub ShiftRows(byval state as ubyte ptr)
    dim temp as ubyte
    temp = state[0*4+1]
    state[0*4+1] = state[1*4+1]
    state[1*4+1] = state[2*4+1]
    state[2*4+1] = state[3*4+1]
    state[3*4+1] = temp
    temp = state[0*4+2]
    state[0*4+2] = state[2*4+2]
    state[2*4+2] = temp
    temp = state[1*4+2]
    state[1*4+2] = state[3*4+2]
    state[3*4+2] = temp
    temp = state[0*4+3]
    state[0*4+3] = state[3*4+3]
    state[3*4+3] = state[2*4+3]
    state[2*4+3] = state[1*4+3]
    state[1*4+3] = temp
end sub

private function xtime(byval x as ubyte) as ubyte
    return (x shl 1) xor (((x shr 7) and 1) * &h1b)
end function

private sub MixColumns(byval state as ubyte ptr)
    dim i as ubyte
    dim Tmp as ubyte
    dim Tm as ubyte
    dim t as ubyte
    i = 0
    while i < 4
        t = state[i*4+0]
        Tmp = ((state[i*4+0] xor state[i*4+1]) xor state[i*4+2]) xor state[i*4+3]
        Tm = state[i*4+0] xor state[i*4+1]
        Tm = xtime(Tm)
        state[i*4+0] xor= Tm xor Tmp
        Tm = state[i*4+1] xor state[i*4+2]
        Tm = xtime(Tm)
        state[i*4+1] xor= Tm xor Tmp
        Tm = state[i*4+2] xor state[i*4+3]
        Tm = xtime(Tm)
        state[i*4+2] xor= Tm xor Tmp
        Tm = state[i*4+3] xor t
        Tm = xtime(Tm)
        state[i*4+3] xor= Tm xor Tmp
        i += 1
    wend
end sub

#if MULTIPLY_AS_A_FUNCTION
    function Multiply(byval x as ubyte, byval y as ubyte) as ubyte
        return (((y and 1) * x) xor _
            ((y shr 1 and 1) * xtime(x)) xor _
            ((y shr 2 and 1) * xtime(xtime(x))) xor _
            ((y shr 3 and 1) * xtime(xtime(xtime(x)))) xor _
            ((y shr 4 and 1) * xtime(xtime(xtime(xtime(x)))))) ' this last call to xtime() can be omitted
    end function
#else
    #define Multiply(x, y) ((((((y and 1) * x) xor (((y shr 1) and 1) * xtime(x))) xor (((y shr 2) and 1) * xtime(xtime(x)))) xor (((y shr 3) and 1) * xtime(xtime(xtime(x))))) xor (((y shr 4) and 1) * xtime(xtime(xtime(xtime(x))))))
#endif

#if (defined(CBC) and CBC = 1) or (defined(ECB) and ECB = 1)
    /'
        function getSBoxInvert(byval num as ubyte) as ubyte
            return rsbox(num)
        end function
    '/
    #define getSBoxInvert(num) (rsbox((num)))

    ' MixColumns function mixes the columns of the state matrix.
    ' The method used to multiply may be difficult to understand for the inexperienced.
    ' Please use the references to gain more information.
    sub InvMixColumns(byval state as ubyte ptr)
        dim as long i
        dim as ubyte a, b, c, d
        'for (i = 0; i < 4; ++i)
        i = 0
        while (i < 4)
            a = state[i*4+0]
            b = state[i*4+1]
            c = state[i*4+2]
            d = state[i*4+3]

            state[i*4+0] = Multiply(a, &h0e) xor Multiply(b, &h0b) xor Multiply(c, &h0d) xor Multiply(d, &h09)
            state[i*4+1] = Multiply(a, &h09) xor Multiply(b, &h0e) xor Multiply(c, &h0b) xor Multiply(d, &h0d)
            state[i*4+2] = Multiply(a, &h0d) xor Multiply(b, &h09) xor Multiply(c, &h0e) xor Multiply(d, &h0b)
            state[i*4+3] = Multiply(a, &h0b) xor Multiply(b, &h0d) xor Multiply(c, &h09) xor Multiply(d, &h0e)
            i+=1
        wend
    end sub

    ' The SubBytes Function Substitutes the values in the
    ' state matrix with values in an S-box.
    sub InvSubBytes(byval state as ubyte ptr)
        dim as ubyte i, j
        ' for (i = 0; i < 4; ++i)
        i = 0
        while (i < 4)
            ' for (j = 0; j < 4; ++j)
            j = 0
            while (j < 4)
                state[j*4+i] = getSBoxInvert(state[j*4+i])
                j+=1
            wend
            i+=1
        wend
    end sub

    sub InvShiftRows(byval state as ubyte ptr)
        dim as ubyte temp

        ' Rotate first row 1 columns to right  
        temp = state[3*4+1]
        state[3*4+1] = state[2*4+1]
        state[2*4+1] = state[1*4+1]
        state[1*4+1] = state[0*4+1]
        state[0*4+1] = temp

        ' Rotate second row 2 columns to right 
        temp = state[0*4+2]
        state[0*4+2] = state[2*4+2]
        state[2*4+2] = temp

        temp = state[1*4+2]
        state[1*4+2] = state[3*4+2]
        state[3*4+2] = temp

        ' Rotate third row 3 columns to right
        temp = state[0*4+3]
        state[0*4+3] = state[1*4+3]
        state[1*4+3] = state[2*4+3]
        state[2*4+3] = state[3*4+3]
        state[3*4+3] = temp
    end sub
#endif ' #if (defined(CBC) and CBC = 1) or (defined(ECB) and ECB = 1)

private sub Cipher(byval state as ubyte ptr, byval RoundKey as const ubyte ptr)
    dim round as ubyte = 0
    AddRoundKey(0, state, RoundKey)
    round = 1
    while 1
        SubBytes(state)
        ShiftRows(state)
        if round = Nr then
            exit while
        end if
        MixColumns(state)
        AddRoundKey(round, state, RoundKey)
        round += 1
    wend
    AddRoundKey(Nr, state, RoundKey)
end sub

#if (defined(CBC) and CBC = 1) or (defined(ECB) and ECB = 1)
sub InvCipher(byval state as ubyte ptr, byval RoundKey as const ubyte ptr)
    dim as ubyte round = 0

    ' Add the First round key to the state before starting the rounds.
    AddRoundKey(Nr, state, RoundKey)

    ' There will be Nr rounds.
    ' The first Nr-1 rounds are identical.
    ' These Nr rounds are executed in the loop below.
    ' Last one without InvMixColumn()
    'for (round = (Nr - 1); ; --round)
    round = (Nr - 1)
    while 1
        InvShiftRows(state)
        InvSubBytes(state)
        AddRoundKey(round, state, RoundKey)
        if (round = 0) then
            exit while
        end if
        InvMixColumns(state)
        round -= 1
    wend

end sub
#endif ' #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)

/'*****************************************************************************'/
/'* Public functions:                                                         *'/
/'*****************************************************************************'/
#if defined(ECB) and (ECB = 1)


sub AES_ECB_encrypt(byval ctx as const AES_ctx ptr, byval buf as zstring ptr)
  ' The next function call encrypts the PlainText with the Key using AES algorithm.
  Cipher(cptr(ubyte ptr, buf), @(ctx->RoundKey(0)))
end sub

sub AES_ECB_decrypt(byval ctx as const AES_ctx ptr, byval buf as zstring ptr)
  ' The next function call decrypts the PlainText with the Key using AES algorithm.
  InvCipher(cptr(ubyte ptr, buf), @(ctx->RoundKey(0)))
end sub


#endif ' #if defined(ECB) and (ECB = 1)

#if defined(CBC) and (CBC = 1)


sub XorWithIv(byval buf as zstring ptr, byval Iv as const ubyte ptr)
    dim as ubyte i
    'for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size
    i = 0
    while (i < AES_BLOCKLEN)
        buf[i] xor= Iv[i]
        i+=1
    wend
end sub

sub AES_CBC_encrypt_buffer(byval ctx as AES_ctx ptr, byval buf as zstring ptr, byval length as uinteger)
    dim as uinteger i
    dim as ubyte ptr Iv = @(ctx->Iv(0))
    'for (i = 0; i < length; i += AES_BLOCKLEN)
    i = 0
    while (i < length)
        XorWithIv(buf, Iv)
        Cipher(cptr(ubyte ptr, buf), @(ctx->RoundKey(0)))
        Iv = buf
        buf += AES_BLOCKLEN
        i += AES_BLOCKLEN
    wend
    ' store Iv in ctx for next call
    memcpy(@(ctx->Iv(0)), Iv, AES_BLOCKLEN)
end sub

sub AES_CBC_decrypt_buffer(byval ctx as AES_ctx ptr, byval buf as zstring ptr, byval length as uinteger)
    dim as uinteger i
    dim as ubyte storeNextIv(0 to AES_BLOCKLEN-1)
    'for (i = 0; i < length; i += AES_BLOCKLEN)
    i = 0
    while (i < length)
        memcpy(@storeNextIv(0), buf, AES_BLOCKLEN)
        InvCipher(cptr(ubyte ptr, buf), @(ctx->RoundKey(0)))
        XorWithIv(buf, @(ctx->Iv(0)))
        memcpy(@(ctx->Iv(0)), @storeNextIv(0), AES_BLOCKLEN)
        buf += AES_BLOCKLEN
        i += AES_BLOCKLEN
    wend

end sub

#endif ' #if defined(CBC) and (CBC = 1)

#if defined(CTR) and (CTR = 1)

' Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key
sub AES_CTR_xcrypt_buffer(byval ctx as AES_ctx ptr, byval buf as zstring ptr, byval length as uinteger)
    dim as ubyte buffer(0 to AES_BLOCKLEN-1)
    
    dim as uinteger i
    dim as long bi
    'for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi)
    i = 0
    bi = AES_BLOCKLEN
    while (i < length)
        if (bi = AES_BLOCKLEN) then ' we need to regen xor compliment in buffer
            memcpy(@buffer(0), @(ctx->Iv(0)), AES_BLOCKLEN)
            Cipher(cptr(ubyte ptr, @buffer(0)), @(ctx->RoundKey(0)))

            ' Increment Iv and handle overflow
            'for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi)
            bi = (AES_BLOCKLEN - 1)
            while (bi >= 0)
    ' inc will overflow
                if (ctx->Iv(bi) = 255) then
                    ctx->Iv(bi) = 0
                    bi-=1
                    continue while
                end if 
                ctx->Iv(bi) += 1
                exit while   
            wend
            bi = 0
        end if

        buf[i] = (buf[i] xor buffer(bi))
        i+=1
        bi+=1
    wend
end sub

#endif ' #if defined(CTR) and (CTR = 1)

end extern
very simple test

Code: Select all

#define CBC 1
#define ECB 1
#define CTR 1

' set one of these to 1
#define AES128 0
#define AES192 0
#define AES256 1

#include "aes.bi"

dim as AES_ctx ctx
dim as zstring ptr buf=callocate(256)
dim as long length, i

*buf="3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068"
length=len(*buf)
AES_init_ctx(@ctx, "123456789123456789")

'encrypt 16 bytes at a time
for i=0 to length step 16
    AES_ECB_encrypt(@ctx, buf+i)
next

?*buf

'decrypt 16 bytes at a time
for i=0 to length step 16
    AES_ECB_decrypt(@ctx, buf+i)
next

?*buf
deallocate(buf)
Sleep
I realize that the encrypted string should probably be Base64 Encoded
note on the translation of (*state)[ i ][ j ], state is treated as a 4 x 4 matrix
state[i, j] won't work, instead I use state[i*4+j]
srvaldez
Posts: 3425
Joined: Sep 25, 2005 21:54

Re: tinyAES

Post by srvaldez »

another very simple test

Code: Select all

#define CBC 1
#define ECB 1
#define CTR 1

'set one of these to 1
#define AES128 0
#define AES192 0
#define AES256 1

#include "aes.bi"

dim as AES_ctx ctx
dim as zstring ptr buf=callocate(256)
dim as long length, i
dim as string key = "abcdefghijklmnop"
dim as string iv  = "qrstuvwxyz123456"

*buf="3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068"
length=len(*buf)
if (length\16)*16 < length then
	length=(length\16+1)*16
end if

AES_init_ctx_iv(@ctx, key, iv)
AES_CBC_encrypt_buffer(@ctx, buf, length)

?*buf
AES_init_ctx_iv(@ctx, key, iv)
AES_CBC_decrypt_buffer(@ctx, buf, length)

?*buf
deallocate(buf)

Sleep
dodicat
Posts: 8024
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: tinyAES

Post by dodicat »

Hi srvaldez.
fb 1.10.1
windows.
I get (in the .bi file)
... error 20: Type mismatch, found ')' in '#if defined(AES256) and (AES256 = 1)'
...error 20: Type mismatch, found ')' in '#if defined(AES256) and (AES256 = 1)'
...error 20: Type mismatch, found ')' in '#if defined(AES256) and (AES256 = 1)'


Results:
Compilation failed

System:
FBIde: 0.4.6
fbc: FreeBASIC Compiler - Version 1.10.1 (2023-12-24), built for win64 (64bit)
OS: Windows NT 6.2 (build 9200)
srvaldez
Posts: 3425
Joined: Sep 25, 2005 21:54

Re: tinyAES

Post by srvaldez »

Hi dodicat :)
did you put the following code before including aes.bi ?

Code: Select all

'set one of these to 1, these 3 lines must be present before including aes.bi
#define AES128 0
#define AES192 0
#define AES256 1

#include "aes.bi"
failure to do so may give the errors you mentioned, if it's something else then please post your code
srvaldez
Posts: 3425
Joined: Sep 25, 2005 21:54

Re: tinyAES

Post by srvaldez »

here's the simple example which base64 encodes the encrypted message

Code: Select all

Declare Function CryptBinaryToString Lib "Crypt32"Alias "CryptBinaryToStringA" _
(byval pbBinary As zstring Ptr, byval cbBinary As uLong, byval dwFlags As uLong, _
 byval pszString As zstring Ptr,byval pcchString As uLong Ptr) As Long

Declare Function CryptStringToBinary Lib "Crypt32"Alias "CryptStringToBinaryA" _
(byval pszString As zstring Ptr, byval cchString As uLong, byval dwFlags As uLong, byval pbBinary As zstring Ptr, _
 byval pcbBinary As uLong Ptr, byval pdwSkip As uLong ptr, byval pdwFlags As uLong Ptr) As Long

#define CBC 1
#define ECB 1
#define CTR 1

'set one of these to 1
#define AES128 1
#define AES192 0
#define AES256 0

#include "aes.bi"

dim as AES_ctx ctx
dim as string buffer
dim as long length, fnum, L, flag=0
dim as string key = "abcdefghijklmnop"
dim as string iv  = "qrstuvwxyz123456"

buffer="3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068"

length=len(buffer)
if (length\16)*16 < length then
	length=(length\16+1)*16
end if

dim as zstring ptr buf=callocate(length)
dim as zstring ptr buff=callocate(length)
*buf=buffer

AES_init_ctx_iv(@ctx, key, iv) 'mandatory initialization
AES_CBC_encrypt_buffer(@ctx, buf, length) 'encrypt your message
CryptBinaryToString(buf, length, flag, 0, @L) ' get the size in bytes needed in L
dim as zstring ptr outbuf=callocate(L)
CryptBinaryToString(buf, length, flag, outbuf, @L) 'now convert the encrypted string to base64

Print "base64 of the encrypted string is "
Print *outbuf
CryptStringToBinary(outbuf, L, flag, buff, @L, 0, 0) 'convert the base64 encrypted string to binary

Print "the binary encrypted string is "
Print *buff

AES_init_ctx_iv(@ctx, key, iv) 'mandatory initialization
AES_CBC_decrypt_buffer(@ctx, buff, length) 'decrypt the binary string

Print "the decrypted binary string is "
Print *buff
deallocate(buf)
deallocate(outbuf)
deallocate(buff)
Sleep
dodicat
Posts: 8024
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: tinyAES

Post by dodicat »

Thanks srvaldez, it works now.
deltarho[1859]
Posts: 4388
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: tinyAES

Post by deltarho[1859] »

Firstly, AES.bi is a masterclass of clean classical BASIC.

I am now going to debunk a myth about secret versus top secret.

Some people seem to think that top secret is more secret than secret. We cannot get more secret than secret. Secret is secret and that is it.

The question is how long should something be kept secret for. Currently, AES128 cannot be broken even by, so called, super computers. Will that be true in ten years' time? Will a document encrypted with AES128 today still be safe in ten years' time? Perhaps not. On the other hand, a document encrypted with AES256 today will still be safe in ten years time. From what I have read, AES256 will be quantum computer resistant. AES128 may be, but if not, we can always increase the number of rounds used by AES128. It is worth reminding ourselves what 256 bit security strength means: 2^256 = 2^128 x 2^128.

So top secret means secret for a few years. Some software outfits use the term 'military grade encryption' for AES256. That is advertising hype, and even they do not understand the difference between secret and top secret.

Some cryptographic functions will not be quantum computer resistant. The public key methods such as RSA and ECDSA will get torn to shreds by quantum computers. My Encrypternet application has a life expectancy of about ten to fifteen years, after which it is 'curtains'.

My guess is that most, if not all, of FB users do not need to use AES256.

With srvaldez's code we have a choice between ECB, CBC, and CTR.

ECB is not safe, so forget that.

Some years ago, Niels Ferguson and Bruce Schneier wrote a book called 'Practical Cryptography'. Of the AES variants, they recommended AES CTR. We need to be careful how AES CTR is implemented. If not implemented correctly, then the encryption can be broken. A few years later a revised version of the book was published but this time AES CBC was recommended; simply because many people were not correctly implementing AES CTR.

Getting AES CTR implemented correctly is powerful because we can decipher sections of the ciphertext without decrypting the whole ciphertext. Imagine how useful that would be extracting patient's history from an encrypted medical database. Sadly, many medical databases are not encrypted in the first place.

My guess is that most, if not all, of FB users do not need to use AES CTR.

So where does that leave us? 128-bit AES CBC for most, if not all, of us.

From the GitHub link in the opening post we are told: “This library is designed for small code size and simplicity, intended for cases where small binary size, low memory footprint and portability is more important than high performance.”

We can expect then tinyAES to be not particularly fast.

I tried it on a 100MiB file, and it took just over three seconds to be encrypted; the 100MiB string - I/O was not timed.

My Encrypternet application, which uses RSA to encrypt a 'password', AES CBC to encrypt a message, SHA256 to hash the message ciphertext, ECDSA to sign the hash, and build a single file in the process took 674ms including I/O time.

So tinyAES is very slow. Of course, speed is not an issue with small messages, so I could see myself using it rather than relying on Windows cryptographic APIs.

On the subject of Windows cryptographic APIs notice that there isn't any used in AES.bi. However, two Windows APIs are used in the last simple test. None are used in the post headed 'another very simple test' so we are now into cross-platform.

Finally, don't misread 'buffer size MUST be multiple of AES_BLOCKLEN'. [mutiple was misspelt]. Note, buffer size and not message length. Encrypting “FreeBASIC”, for example, is not a problem.

Another cracking thread from srvaldez - well done. :)
adeyblue
Posts: 307
Joined: Nov 07, 2019 20:08

Re: tinyAES

Post by adeyblue »

deltarho[1859] wrote: Jun 15, 2024 5:01 Finally, don't misread 'buffer size MUST be multiple of AES_BLOCKLEN'. [mutiple was misspelt]. Note, buffer size and not message length. Encrypting “FreeBASIC”, for example, is not a problem.
As someone who's just spent a day trying to figure out why some larger encryption code someone ported from python to c# was causing the intended recipient to error with 'invalid message', this is very important!
pycryptodome requires you to manually pad the plaintext, C#s built-in AES things don't. So don't manually pad then send it off to something that'll also pad it.
Not that I'm bitter or anything.
----
I wonder how much more minimal/performant it'd get with the AES-NI assembly instructions. I don't know how to use them, but somebody must.
deltarho[1859]
Posts: 4388
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: tinyAES

Post by deltarho[1859] »

adeyblue wrote:I don't know how to use them, but somebody must.
With Windows 7 or later, if our CPU supports AES-NI, then it will be used automatically when we use a encrypt/decrypt API.
srvaldez
Posts: 3425
Joined: Sep 25, 2005 21:54

Re: tinyAES

Post by srvaldez »

thank you deltarho[1859] for your comments and testing 😁
srvaldez
Posts: 3425
Joined: Sep 25, 2005 21:54

Re: tinyAES

Post by srvaldez »

adeyblue wrote: Jun 15, 2024 5:31 ...
I wonder how much more minimal/performant it'd get with the AES-NI assembly instructions. I don't know how to use them, but somebody must.
at least 2 people from the masm forum gave it a go, https://masm32.com/board/index.php?msg=80831 which is based on this C code https://stackoverflow.com/questions/322 ... ns-and-gcc
the masm port shouldn't be too hard to translate to FB, maybe tomorrow I will give a try
adeyblue
Posts: 307
Joined: Nov 07, 2019 20:08

Re: tinyAES

Post by adeyblue »

Oh, don't do any work on my behalf. I don't want it for anything, I was just musing/curious.
deltarho[1859]
Posts: 4388
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: tinyAES

Post by deltarho[1859] »

I don't know what makes tinyAES tiny compared to the standard AES. AES-NI were designed for AES and not tinyAES. If we can use AES-NI in tinyAES AES.bi may be smaller, but the binaries may be much larger, defeating the whole point of tinyAES. My guess is that will probably happen.

For small messages, less than 1MiB for example, the lack of tinyAES performance compared with the standard AES may be of no concern in an application.
srvaldez
Posts: 3425
Joined: Sep 25, 2005 21:54

Re: tinyAES

Post by srvaldez »

I had a look at the assembler code and also at the cpp code, I don't understand either one
the cpp macros with the do{ .... }while(0) are baffling to me, I don't know what they do.
just in case anyone who doesn't know how to use the AES CTR in tinyAES, just take example 2 or 3 above and replace AES_CBC_encrypt_buffer and AES_CBC_decrypt_buffer with AES_CTR_xcrypt_buffer, AES_CTR_xcrypt_buffer encrypts and decrypts
[edit]
chatGPT explained what the do{ }while(0) construct does, it seems that this construct is needed for safe multi-line macros in C++
deltarho[1859]
Posts: 4388
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: tinyAES

Post by deltarho[1859] »

AES CTR comes into its own with large messages where we can decrypt targeted sections of the ciphertext.

With my timing test above it took just over three seconds to encrypt 100MiB. Decrypting a 1MiB section of the ciphertext may then take only 30ms.

So how do we target a section of the ciphertext? “IV is incremented for every block”. So extra work from us is involved with AES CTR.
Post Reply