Difference between revisions of "Cryptogams AES"
Line 191: | Line 191: | ||
==Create C Header== | ==Create C Header== | ||
− | The fifth step creates a C header file based on information from [[#Determine_API|Determine API]] | + | The fifth step creates a C header file based on information from [[#Determine_API|Determine API]]. The header file is needed for two reasons. First, it removes the OpenSSL dependency from your project. Second, it avoids OpenSSL licensing violations. |
Below is the C Header file you can use. | Below is the C Header file you can use. |
Revision as of 10:43, 7 July 2018
Cryptogams is Andy Polyakov's project used to develop high speed cryptographic primitives and share them with other developers. This wiki article will show you how to use Cryptogams ARMv4 AES implementation. The ARMv4 implementation runs around 20 to 25 cycles per byte (cpb). Typical C/C++ implementations run around 40 to 80 cpb and Andy's hand tuned ASM should outperform all of them.
Andy's Cryptogam implementations are provided by OpenSSL, but they are also available stand alone under a BSD license. The BSD style license is permissive and allows more developers to use Andy's high speed cryptography without an OpenSSL dependency.
There are 6 steps to the process. The first step obtains the sources. The second step creates an ASM source file. The third step compiles and assembles the source file into an object file. The fourth steps determines the API. The fifth step creates a C header file. The final step integrates the object file into a program.
Obtain Source Files
There are two source files you need for Cryptogams AES. The first is arm-xlate.pl and the second is aes-armv4.pl. They are available in the OpenSSL sources. The following commands fetch OpenSSL and then peels off the two Cryptogams files of interest.
# Clone OpenSSL for the latest Cryptogams sources git clone https://github.com/openssl/openssl.git mkdir cryptogams/ cp ./openssl/crypto/perlasm/arm-xlate.pl ./cryptogams/ cp ./openssl/crypto/aes/asm/aes-armv4.pl ./cryptogams/ cd cryptogams/ chmod +x *.pl
Create ASM File
The second step is to run aes-armv4.pl to produce an assembly language source file that can be consumed by GCC. aes-armv4.pl internally calls arm-xlate.pl. linux32 is the flavor used by the translate program. aes-armv4.S is the output filename. In the command below note the *.S file extension, which is a capitol S. Do not use a lowercase s because GCC must drive the compile and assemble step.
./aes-armv4.pl linux32 aes-armv4.S
GCC is needed to drive the process because there are C macros in the source file:
$ cat aes-armv4.S @ Copyright 2007-2018 The OpenSSL Project Authors. All Rights Reserved. ... #ifndef __KERNEL__ # include "arm_arch.h" #else # define __ARM_ARCH__ __LINUX_ARM_ARCH__ #endif ...
At this point there is an ASM file but it needs two small fixups. First, arm_arch.h is an OpenSSL source file so the dependency must be removed. Second, GCC defines __ARM_ARCH instead of __ARM_ARCH__ so a sed is needed.
To fixup the source files executes the following two commands:
# Remove OpenSSL include sed -i 's/# include "arm_arch.h"//g' aes-armv4.S # Fix GCC defines sed -i 's/__ARM_ARCH__/__ARM_ARCH/g' aes-armv4.S
After the two fixups aes-armv4.S is ready to be compiled by GCC.
Compile Source File
The source file is ready to be compiled and assembled. At this point there are two choices. First, you can use ARMv5t or higher which includes Thumb instructions. The following compiles the source file with ARMv5t.
$ gcc -march=armv5t -c aes-armv4.S
The second choice uses ARMv4 and avoids Thumb instructions. If you want to avoid Thumb then add -marm to you compile command.
$ gcc -march=armv4 -marm -c aes-armv4.S
Using ARMv5t as an example we now have an object file with the following symbols:
$ gcc -march=armv5t -c aes-armv4.S $ nm aes-armv4.o 000011c0 T AES_decrypt 00000540 T AES_encrypt 00000b60 T AES_set_decrypt_key 00000b80 T AES_set_enc2dec_key 00000820 T AES_set_encrypt_key 00000cc0 t AES_Td 00000000 t AES_Te 000012c0 t _armv4_AES_decrypt 00000640 t _armv4_AES_encrypt 00000b80 t _armv4_AES_set_enc2dec_key 00000820 t _armv4_AES_set_encrypt_key
And you can inspect the generated code with objdump.
$ objdump --disassemble aes-armv4.o aes-armv4.o: file format elf32-littlearm ... 00000b60 <AES_set_decrypt_key>: b60: e52de004 push {lr} ; (str lr, [sp, #-4]!) b64: ebffff2d bl 820 <AES_set_encrypt_key> b68: e3300000 teq r0, #0 b6c: e49de004 pop {lr} ; (ldr lr, [sp], #4) b70: 1afffff9 bne b5c <AES_set_encrypt_key+0x33c> b74: e1a00002 mov r0, r2 b78: e1a01002 mov r1, r2 b7c: eaffffff b b80 <AES_set_enc2dec_key> ...
And trace it back to the source code in aes-armv4.S.
.globl AES_set_decrypt_key .type AES_set_decrypt_key,%function .align 5 AES_set_decrypt_key: str lr,[sp,#-4]! @ push lr bl _armv4_AES_set_encrypt_key teq r0,#0 ldr lr,[sp],#4 @ pop lr bne .Labrt mov r0,r2 @ AES_set_encrypt_key preserves r2, mov r1,r2 @ which is AES_KEY *key b _armv4_AES_set_enc2dec_key .size AES_set_decrypt_key,.-AES_set_decrypt_key
Determine API
The next step is determine the API so you can call it from a C program. Unfortunately the API is not documented and you have to dig around the OpenSSL sources. The functions od interest are AES_set_encrypt_key, AES_set_decrypt_key, AES_encrypt and AES_decrypt.
A quick grep of OpenSSL sources reveals the following for AES_set_encrypt_key.
openssl$ grep -nIR AES_set_encrypt_key | grep '\.c' ... crypto/aes/aes_core.c:632:int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
Examining aes_core.c:632 reveals the following.
$ cat -n crypto/aes/aes_core.c ... 632 int AES_set_encrypt_key(const unsigned char *userKey, const int bits, 633 AES_KEY *key) 634 { ... 728 return 0; 729 }
The next piece of information to discover is AES_KEY. Again a quick grep leads you to aes_key_st.
$ grep -nIR AES_KEY | grep typedef include/openssl/aes.h:39:typedef struct aes_key_st AES_KEY; $ cat -n include/openssl/aes.h ... 31 struct aes_key_st { 32 # ifdef AES_LONG 33 unsigned long rd_key[4 * (AES_MAXNR + 1)]; 34 # else 35 unsigned int rd_key[4 * (AES_MAXNR + 1)]; 36 # endif 37 int rounds; 38 }; 39 typedef struct aes_key_st AES_KEY;
Finally, we need AES_MAXNR from aes.h.
$ grep -IR AES_MAXNR | grep define include/openssl/aes.h:# define AES_MAXNR 14
Lather, rinse repeat for AES_set_decrypt_key, AES_encrypt and AES_decrypt. AES_encrypt can be found at crypto/aes/aes_core.c:787.
openssl$ grep -nIR AES_encrypt | grep '\.c' ... crypto/aes/aes_core.c:787:void AES_encrypt(...) openssl$ cat -n crypto/aes/aes_core.c ... 783 /* 784 * Encrypt a single block 785 * in and out can overlap 786 */ 787 void AES_encrypt(const unsigned char *in, unsigned char *out, 788 const AES_KEY *key) {
And AES_decrypt can be found at aes_core.c:978.
openssl$ grep -nIR AES_decrypt | grep '\.c' ... crypto/aes/aes_core.c:978:void AES_decrypt(...) openssl$ cat -n crypto/aes/aes_core.c 974 /* 975 * Decrypt a single block 976 * in and out can overlap 977 */ 978 void AES_decrypt(const unsigned char *in, unsigned char *out, 979 const AES_KEY *key) 980 {
Create C Header
The fifth step creates a C header file based on information from Determine API. The header file is needed for two reasons. First, it removes the OpenSSL dependency from your project. Second, it avoids OpenSSL licensing violations.
Below is the C Header file you can use.
/* Header file for use with Cryptogam's ARMv4 AES. */ /* Also see http://www.openssl.org/~appro/cryptogams/ and */ /* https://wiki.openssl.org/index.php?title=Cryptogams_AES */ #ifndef CRYPTOGAMS_AES_ARMV4_H #define CRYPTOGAMS_AES_ARMV4_H #define AES_MAXNR 14 typedef struct AES_KEY_st { unsigned int rd_key[4 * (AES_MAXNR + 1)]; int rounds; } AES_KEY; int AES_set_encrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key); int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key); void AES_encrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key); void AES_decrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key); #endif /* CRYPTOGAMS_AES_ARMV4_H */
Test Program
The final step is to test the integration of Cryptogam's AES with your program.
$ gcc -std=c99 aes-armv4-test.c ./aes-armv4.o -o aes-armv4-test.exe $ ./aes-armv4-test.exe Encrypted plaintext! Decrypted ciphertext!
And the test program is shown below.
#include <stdio.h> #include <string.h> #include <assert.h> #include "aes-armv4.h" typedef unsigned char byte; int main(int argc, char* argv[]) { /* Test key from FIPS 197 */ const byte kb[] = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }; const byte pb[] = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a }; const byte cb[] = { 0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, 0xa8, 0x9e, 0xca, 0xf3, 0x24, 0x66, 0xef, 0x97 }; /* Scratch */ byte buf[16]; int result; /********************************************/ AES_KEY ekey; result = AES_set_encrypt_key(kb, sizeof(kb)*8, & ekey); assert(result == 0); AES_encrypt(pb, buf, &ekey); if (memcmp(cb, buf, 16) == 0) printf("Encrypted plaintext!\n"); else printf("Failed to encrypt plaintext!\n"); /********************************************/ AES_KEY dkey; result = AES_set_decrypt_key(kb, sizeof(kb)*8, & dkey); assert(result == 0); AES_decrypt(cb, buf, &dkey); if (memcmp(pb, buf, 16) == 0) printf("Decrypted ciphertext!\n"); else printf("Failed to decrypt ciphertext!\n"); return 0; }