aboutsummaryrefslogblamecommitdiffstatshomepage
path: root/src/box.c
blob: c7e1e9076f7115207e0ccb3c02eefcdb12e07b2c (plain) (tree)
1
2
3
4
5
6
7
8
9





                                                             
 
                    
 
                   
                   
 
                                                              
 


                              

  






                                  
 
                                        
 
                                                   
 
                                                              


                                  
                          
                      
 






                      




                                                
 
                                         
                                                           

 




                                                
 

                                        
 
                                                        
                                                  

                      
                                                            
                                                                       





                      




                                                
 
                                        
 
                                                            
                                                                   

                      
                                                        
                                         





                      




                                                
 
                                        
 
                                                            
                                                                     



                      
                                                        
                                         




                      




                                                
 
                                        
 
                                                            
                                                                   

                      
                                                        
                                         

                      
                                                        



                                    

 




                                                    
 
                                                                  
 




                               

  




                                     
 
                                                    
                                         

 




                                                
 
                                         
                                                           

 




                                                
 
                                        
 
                                                            
                                                                

                      
                                                        
                                            





                      




                                                
 
                                        
 
                                                            
                                                                   

                      
                                                        
                                         


                      


                  
                                                            
 
                                                                  
 




                               

  




                                     
 
                                                    
                                         
 
 

                                                        


                         
                                  
 
                                        


                      

                     


                              
                                                                     







                                                               


                     




                                  
                                             
                                                              


     

                                                   

                    
                                  
 
                                        
 
                           
 
                                                         


                                           
                            
 
                                                    


                                                        
                                                          





                                             

                                                                        



                        
                                  
 
                                        


                      
                                                             



                                             
 
                              
                                                                 


                                         

                                                    




                                             
                                                           








                                                    

                                                           


                    
                                  
 
                                        





                      
                                                             



                                             
 
                              
                                                                 




                                              
 

                               
                                                          


                                
                                                           













                                                              

                                      



                      
                                  
 
                                        
 
                    
                                                           
                          
                                                                 
 

                            
                                                                



                                                                   
                           
                                     
                                                           


                      
                                                             




                                                 

                                        
                                                               


                                              

                                         

     
                          
                                                    
                                                                
        
                                                                 
                                                                              

 

                                                        


                             
                                  
 
                                        


                      

                     


                              
                                                                     


                                               
                                                            
                                                              





                                           


                     




                                  
                                             
                                                              


     

                                                   

                    
                                  
 
                                        
 
                            
 
                                                         


                                            
                           
 
                                                    


                                                        
                                                          





                                            

                                                           


                    
                                  
 
                                        





                      
                                                             



                                              
 
                               
                                                                 




                                             
 

                              
                                                          


                                 
                                                           













                                                              

                                      



                      
                                  

                    
                                                           
                          
                                                                 
 
                                        
                                
 
                                                                
                                                                       


                      
                           
                    
                                                           


                      
                                                             




                                                 

                                        
                                                               


                                              

                                         

     
                              
     
                                                    
                                                                


        

                            

                                                           




                                          

                                   
                      
     
 
 


                                   
                    
                                  

                     
                                                            
                    
                                                           
 
                                                      


                                


                                 
                    
                                  

                     
                                                            
                    
                                                           
 
                                                    


                                




                                    

                     
                                                            
                    
                                                           
 
                                                       


                                




                                  

                     
                                                            
                    
                                                           
 
                                                     

                                
/*
 * Copyright (c) 2015 Egor Tensin <Egor.Tensin@gmail.com>
 * This file is part of the "AES tools" project.
 * For details, see https://github.com/egor-tensin/aes-tools.
 * Distributed under the MIT License.
 */

#include <aes/all.h>

#include <stdlib.h>
#include <string.h>

static const AES_BoxAlgorithmInterface* aes_box_algorithms[] =
{
    &aes_box_algorithm_aes128,
    &aes_box_algorithm_aes192,
    &aes_box_algorithm_aes256,
};

AES_StatusCode aes_box_init(
    AES_Box* box,
    AES_Algorithm algorithm,
    const AES_BoxKey* box_key,
    AES_Mode mode,
    const AES_BoxBlock* iv,
    AES_ErrorDetails* err_details)
{
    AES_StatusCode status = AES_SUCCESS;

    box->algorithm = aes_box_algorithms[algorithm];

    if (aes_is_error(status = box->algorithm->calc_round_keys(
            box_key,
            &box->encryption_keys,
            &box->decryption_keys,
            err_details)))
        return status;

    box->mode = mode;
    if (iv != NULL)
        box->iv = *iv;

    return status;
}

static AES_StatusCode aes_box_encrypt_block_ecb(
    AES_Box* box,
    const AES_BoxBlock* input,
    AES_BoxBlock* output,
    AES_ErrorDetails* err_details)
{
    return box->algorithm->encrypt_block(
        input, &box->encryption_keys, output, err_details);
}

static AES_StatusCode aes_box_encrypt_block_cbc(
    AES_Box* box,
    const AES_BoxBlock* input,
    AES_BoxBlock* output,
    AES_ErrorDetails* err_details)
{
    AES_StatusCode status = AES_SUCCESS;
    AES_BoxBlock xored_input = *input;

    if (aes_is_error(status = box->algorithm->xor_block(
            &xored_input, &box->iv, err_details)))
        return status;

    if (aes_is_error(status = box->algorithm->encrypt_block(
            &xored_input, &box->encryption_keys, output, err_details)))
        return status;

    box->iv = *output;
    return status;
}

static AES_StatusCode aes_box_encrypt_block_cfb(
    AES_Box* box,
    const AES_BoxBlock* input,
    AES_BoxBlock* output,
    AES_ErrorDetails* err_details)
{
    AES_StatusCode status = AES_SUCCESS;

    if (aes_is_error(status = box->algorithm->encrypt_block(
            &box->iv, &box->encryption_keys, output, err_details)))
        return status;

    if (aes_is_error(status = box->algorithm->xor_block(
            output, input, err_details)))
        return status;

    box->iv = *output;
    return status;
}

static AES_StatusCode aes_box_encrypt_block_ofb(
    AES_Box* box,
    const AES_BoxBlock* input,
    AES_BoxBlock* output,
    AES_ErrorDetails* err_details)
{
    AES_StatusCode status = AES_SUCCESS;

    if (aes_is_error(status = box->algorithm->encrypt_block(
            &box->iv, &box->encryption_keys, &box->iv, err_details)))
        return status;

    *output = box->iv;

    if (aes_is_error(status = box->algorithm->xor_block(
            output, input, err_details)))
        return status;

    return status;
}

static AES_StatusCode aes_box_encrypt_block_ctr(
    AES_Box* box,
    const AES_BoxBlock* input,
    AES_BoxBlock* output,
    AES_ErrorDetails* err_details)
{
    AES_StatusCode status = AES_SUCCESS;

    if (aes_is_error(status = box->algorithm->encrypt_block(
            &box->iv, &box->encryption_keys, output, err_details)))
        return status;

    if (aes_is_error(status = box->algorithm->xor_block(
            output, input, err_details)))
        return status;

    if (aes_is_error(status = box->algorithm->inc_block(
            &box->iv, err_details)))
        return status;

    return status;
}

typedef AES_StatusCode (*AES_BoxEncryptBlockInMode)(
    AES_Box*,
    const AES_BoxBlock*,
    AES_BoxBlock*,
    AES_ErrorDetails*);

static AES_BoxEncryptBlockInMode aes_box_encrypt_block_in_mode[] =
{
    &aes_box_encrypt_block_ecb,
    &aes_box_encrypt_block_cbc,
    &aes_box_encrypt_block_cfb,
    &aes_box_encrypt_block_ofb,
    &aes_box_encrypt_block_ctr,
};

AES_StatusCode aes_box_encrypt_block(
    AES_Box* box,
    const AES_BoxBlock* input,
    AES_BoxBlock* output,
    AES_ErrorDetails* err_details)
{
    return aes_box_encrypt_block_in_mode[box->mode](
        box, input, output, err_details);
}

static AES_StatusCode aes_box_decrypt_block_ecb(
    AES_Box* box,
    const AES_BoxBlock* input,
    AES_BoxBlock* output,
    AES_ErrorDetails* err_details)
{
    return box->algorithm->decrypt_block(
        input, &box->decryption_keys, output, err_details);
}

static AES_StatusCode aes_box_decrypt_block_cbc(
    AES_Box* box,
    const AES_BoxBlock* input,
    AES_BoxBlock* output,
    AES_ErrorDetails* err_details)
{
    AES_StatusCode status = AES_SUCCESS;

    if (aes_is_error(status = box->algorithm->decrypt_block(
            input, &box->decryption_keys, output, err_details)))
        return status;

    if (aes_is_error(status = box->algorithm->xor_block(
            output, &box->iv, err_details)))
        return status;

    box->iv = *input;
    return status;
}

static AES_StatusCode aes_box_decrypt_block_cfb(
    AES_Box* box,
    const AES_BoxBlock* input,
    AES_BoxBlock* output,
    AES_ErrorDetails* err_details)
{
    AES_StatusCode status = AES_SUCCESS;

    if (aes_is_error(status = box->algorithm->encrypt_block(
            &box->iv, &box->encryption_keys, output, err_details)))
        return status;

    if (aes_is_error(status = box->algorithm->xor_block(
            output, input, err_details)))
        return status;

    box->iv = *input;
    return status;
}

typedef AES_BoxEncryptBlockInMode AES_BoxDecryptBlockInMode;

static AES_BoxDecryptBlockInMode aes_box_decrypt_block_in_mode[] =
{
    &aes_box_decrypt_block_ecb,
    &aes_box_decrypt_block_cbc,
    &aes_box_decrypt_block_cfb,
    &aes_box_encrypt_block_ofb,
    &aes_box_encrypt_block_ctr,
};

AES_StatusCode aes_box_decrypt_block(
    AES_Box* box,
    const AES_BoxBlock* input,
    AES_BoxBlock* output,
    AES_ErrorDetails* err_details)
{
    return aes_box_decrypt_block_in_mode[box->mode](
        box, input, output, err_details);
}

static AES_StatusCode aes_box_get_encrypted_buffer_size(
    AES_Box* box,
    size_t src_size,
    size_t* dest_size,
    size_t* padding_size,
    AES_ErrorDetails* err_details)
{
    AES_StatusCode status = AES_SUCCESS;

    switch (box->mode)
    {
        case AES_ECB:
        case AES_CBC:
        {
            size_t block_size;

            if (aes_is_error(status = box->algorithm->get_block_size(
                    &block_size, err_details)))
                return status;

            *padding_size = block_size - src_size % block_size;
            *dest_size = src_size + *padding_size;
            return status;
        }

        case AES_CFB:
        case AES_OFB:
        case AES_CTR:
            *dest_size = src_size;
            *padding_size = 0;
            return status;

        default:
            return aes_error_not_implemented(
                err_details, "unsupported mode of operation");
    }
}

static AES_StatusCode aes_box_encrypt_buffer_block(
    AES_Box* box,
    const void* src,
    void* dest,
    AES_ErrorDetails* err_details)
{
    AES_StatusCode status = AES_SUCCESS;

    AES_BoxBlock plaintext;

    if (aes_is_error(status = box->algorithm->load_block(
            &plaintext, src, err_details)))
        return status;

    AES_BoxBlock ciphertext;

    if (aes_is_error(status = aes_box_encrypt_block(
            box, &plaintext, &ciphertext, err_details)))
        return status;

    if (aes_is_error(status = box->algorithm->store_block(
            dest, &ciphertext, err_details)))
        return status;

    return status;
}

static AES_StatusCode aes_box_encrypt_buffer_partial_block_with_padding(
    AES_Box* box,
    const void* src,
    size_t src_size,
    void* dest,
    size_t padding_size,
    AES_ErrorDetails* err_details)
{
    AES_StatusCode status = AES_SUCCESS;

    size_t block_size;

    if (aes_is_error(status = box->algorithm->get_block_size(
            &block_size, err_details)))
        return status;

    void* plaintext_buf = malloc(block_size);

    if (plaintext_buf == NULL)
        return status = aes_error_memory_allocation(err_details);

    memcpy(plaintext_buf, src, src_size);

    if (aes_is_error(status = aes_fill_with_padding(
            AES_PADDING_PKCS7,
            (char*) plaintext_buf + src_size,
            padding_size,
            err_details)))
        goto FREE_PLAINTEXT_BUF;

    if (aes_is_error(status = aes_box_encrypt_buffer_block(
            box, plaintext_buf, dest, err_details)))
        goto FREE_PLAINTEXT_BUF;

FREE_PLAINTEXT_BUF:
    free(plaintext_buf);

    return status;
}

static AES_StatusCode aes_box_encrypt_buffer_partial_block(
    AES_Box* box,
    const void* src,
    size_t src_size,
    void* dest,
    AES_ErrorDetails* err_details)
{
    AES_StatusCode status = AES_SUCCESS;

    if (src_size == 0)
        return status;

    size_t block_size;

    if (aes_is_error(status = box->algorithm->get_block_size(
            &block_size, err_details)))
        return status;

    void* plaintext_buf = malloc(block_size);

    if (plaintext_buf == NULL)
        return status = aes_error_memory_allocation(err_details);

    memset(plaintext_buf, 0x00, block_size);
    memcpy(plaintext_buf, src, src_size);

    void* ciphertext_buf = malloc(block_size);

    if (ciphertext_buf == NULL)
    {
        status = aes_error_memory_allocation(err_details);
        goto FREE_PLAINTEXT_BUF;
    }

    if (aes_is_error(status = aes_box_encrypt_buffer_block(
            box, plaintext_buf, ciphertext_buf, err_details)))
        goto FREE_CIPHERTEXT_BUF;

    memcpy(dest, ciphertext_buf, src_size);

FREE_CIPHERTEXT_BUF:
    free(ciphertext_buf);

FREE_PLAINTEXT_BUF:
    free(plaintext_buf);

    return status;
}

AES_StatusCode aes_box_encrypt_buffer(
    AES_Box* box,
    const void* src,
    size_t src_size,
    void* dest,
    size_t* dest_size,
    AES_ErrorDetails* err_details)
{
    AES_StatusCode status = AES_SUCCESS;

    if (box == NULL)
        return aes_error_null_argument(err_details, "box");
    if (dest_size == NULL)
        return aes_error_null_argument(err_details, "dest_size");

    size_t padding_size = 0;

    if (aes_is_error(status = aes_box_get_encrypted_buffer_size(
            box, src_size, dest_size, &padding_size, err_details)))
        return status;

    if (dest == NULL)
        return AES_SUCCESS;
    if (src == NULL && src_size != 0)
        return aes_error_null_argument(err_details, "src");

    size_t block_size;

    if (aes_is_error(status = box->algorithm->get_block_size(
            &block_size, err_details)))
        return status;

    const size_t src_len = src_size / block_size;

    for (size_t i = 0; i < src_len; ++i)
    {
        if (aes_is_error(status = aes_box_encrypt_buffer_block(
                box, src, dest, err_details)))
            return status;

        src = (char*) src + block_size;
        dest = (char*) dest + block_size;
    }

    if (padding_size == 0)
        return aes_box_encrypt_buffer_partial_block(
            box, src, src_size % block_size, dest, err_details);
    else
        return aes_box_encrypt_buffer_partial_block_with_padding(
            box, src, src_size % block_size, dest, padding_size, err_details);
}

static AES_StatusCode aes_box_get_decrypted_buffer_size(
    AES_Box* box,
    size_t src_size,
    size_t* dest_size,
    size_t* max_padding_size,
    AES_ErrorDetails* err_details)
{
    AES_StatusCode status = AES_SUCCESS;

    switch (box->mode)
    {
        case AES_ECB:
        case AES_CBC:
        {
            size_t block_size;

            if (aes_is_error(status = box->algorithm->get_block_size(
                    &block_size, err_details)))
                return status;

            if (src_size == 0 || src_size % block_size != 0)
                return aes_error_missing_padding(err_details);

            *dest_size = src_size;
            *max_padding_size = block_size;
            return status;
        }

        case AES_CFB:
        case AES_OFB:
        case AES_CTR:
            *dest_size = src_size;
            *max_padding_size = 0;
            return status;

        default:
            return aes_error_not_implemented(
                err_details, "unsupported mode of operation");
    }
}

static AES_StatusCode aes_box_decrypt_buffer_block(
    AES_Box* box,
    const void* src,
    void* dest,
    AES_ErrorDetails* err_details)
{
    AES_StatusCode status = AES_SUCCESS;

    AES_BoxBlock ciphertext;

    if (aes_is_error(status = box->algorithm->load_block(
            &ciphertext, src, err_details)))
        return status;

    AES_BoxBlock plaintext;

    if (aes_is_error(status = aes_box_decrypt_block(
            box, &ciphertext, &plaintext, err_details)))
        return status;

    if (aes_is_error(status = box->algorithm->store_block(
            dest, &plaintext, err_details)))
        return status;

    return status;
}

static AES_StatusCode aes_box_decrypt_buffer_partial_block(
    AES_Box* box,
    const void* src,
    size_t src_size,
    void* dest,
    AES_ErrorDetails* err_details)
{
    AES_StatusCode status = AES_SUCCESS;

    if (src_size == 0)
        return status;

    size_t block_size;

    if (aes_is_error(status = box->algorithm->get_block_size(
            &block_size, err_details)))
        return status;

    void* ciphertext_buf = malloc(block_size);

    if (ciphertext_buf == NULL)
        return status = aes_error_memory_allocation(err_details);

    memset(ciphertext_buf, 0x00, block_size);
    memcpy(ciphertext_buf, src, src_size);

    void* plaintext_buf = malloc(block_size);

    if (plaintext_buf == NULL)
    {
        status = aes_error_memory_allocation(err_details);
        goto FREE_CIPHERTEXT_BUF;
    }

    if (aes_is_error(status = aes_box_decrypt_buffer_block(
            box, ciphertext_buf, plaintext_buf, err_details)))
        goto FREE_PLAINTEXT_BUF;

    memcpy(dest, plaintext_buf, src_size);

FREE_PLAINTEXT_BUF:
    free(plaintext_buf);

FREE_CIPHERTEXT_BUF:
    free(ciphertext_buf);

    return status;
}

AES_StatusCode aes_box_decrypt_buffer(
    AES_Box* box,
    const void* src,
    size_t src_size,
    void* dest,
    size_t* dest_size,
    AES_ErrorDetails* err_details)
{
    if (box == NULL)
        return aes_error_null_argument(err_details, "box");
    if (dest_size == NULL)
        return aes_error_null_argument(err_details, "dest_size");

    AES_StatusCode status = AES_SUCCESS;
    size_t max_padding_size = 0;

    if (aes_is_error(status = aes_box_get_decrypted_buffer_size(
            box, src_size, dest_size, &max_padding_size, err_details)))
        return status;

    if (dest == NULL)
        return AES_SUCCESS;
    if (src == NULL)
        return aes_error_null_argument(err_details, "src");

    size_t block_size;

    if (aes_is_error(status = box->algorithm->get_block_size(
            &block_size, err_details)))
        return status;

    const size_t src_len = src_size / block_size;

    for (size_t i = 0; i < src_len; ++i)
    {
        if (aes_is_error(status = aes_box_decrypt_buffer_block(
                box, src, dest, err_details)))
            return status;

        src = (char*) src + block_size;
        dest = (char*) dest + block_size;
    }

    if (max_padding_size == 0)
    {
        return aes_box_decrypt_buffer_partial_block(
            box, src, src_size % block_size, dest, err_details);
    }
    else
    {
        size_t padding_size;

        if (aes_is_error(status = aes_extract_padding_size(
                AES_PADDING_PKCS7,
                (char*) dest - block_size,
                block_size,
                &padding_size,
                err_details)))
            return status;

        *dest_size -= padding_size;
        return status;
    }
}

AES_StatusCode aes_box_parse_block(
    AES_BoxBlock* dest,
    AES_Algorithm algorithm,
    const char* src,
    AES_ErrorDetails* err_details)
{
    if (dest == NULL)
        return aes_error_null_argument(err_details, "dest");
    if (src == NULL)
        return aes_error_null_argument(err_details, "src");

    return aes_box_algorithms[algorithm]->parse_block(
        dest, src, err_details);
}

AES_StatusCode aes_box_parse_key(
    AES_BoxKey* dest,
    AES_Algorithm algorithm,
    const char* src,
    AES_ErrorDetails* err_details)
{
    if (dest == NULL)
        return aes_error_null_argument(err_details, "dest");
    if (src == NULL)
        return aes_error_null_argument(err_details, "src");

    return aes_box_algorithms[algorithm]->parse_key(
        dest, src, err_details);
}

AES_StatusCode aes_box_format_block(
    AES_BoxBlockString* dest,
    AES_Algorithm algorithm,
    const AES_BoxBlock* src,
    AES_ErrorDetails* err_details)
{
    if (dest == NULL)
        return aes_error_null_argument(err_details, "dest");
    if (src == NULL)
        return aes_error_null_argument(err_details, "src");

    return aes_box_algorithms[algorithm]->format_block(
        dest, src, err_details);
}

AES_StatusCode aes_box_format_key(
    AES_BoxKeyString* dest,
    AES_Algorithm algorithm,
    const AES_BoxKey* src,
    AES_ErrorDetails* err_details)
{
    if (dest == NULL)
        return aes_error_null_argument(err_details, "dest");
    if (src == NULL)
        return aes_error_null_argument(err_details, "src");

    return aes_box_algorithms[algorithm]->format_key(
        dest, src, err_details);
}