File: gnu-asm-aesni.c

package info (click to toggle)
frama-c 20161101%2Bsilicon%2Bdfsg-5
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 42,324 kB
  • ctags: 35,695
  • sloc: ml: 200,142; ansic: 31,465; makefile: 2,334; sh: 1,643; lisp: 259; python: 85; asm: 26
file content (167 lines) | stat: -rw-r--r-- 6,849 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/* run.config
   COMMENT: FILE_TITLE: AES-NI - Instructions en assembleur inline
   COMMENT: FILE_DESCRIPTION: Certains microprocesseurs sont dotés de fonctions cryptographiques directement accessibles à l'aide d'un jeu d'instructions étendu. C'est le cas par exemple de microprocesseurs Intel et AMD qui reconnaissent l'extension AES-NI.
   COMMENT: FILE_DESCRIPTION: L'exemple ci-dessous appelle une fonction extraite de la bibliothèque open source libgcrypt qui implémente des opérations cryptographiques faisant appel aux instructions AES-NI via de l'assembleur inline.
   COMMENT: FILE_DESCRIPTION: La syntaxe Extended Asm de GCC, utilisée ici, permet au programmeur de spécifier les variables C d'entrée et de sortie de son code assembleur. Bien que l'analyse du code assembleur ne soit pas dans le cadre de Frama-C ne gère, il peut se révéler intéressant de prendre en compte la spécification des entrées/sorties donnée par le programmeur.
   -------------------------
   COMMENT: TEST_TITLE: Chiffrement via AES-NI
   COMMENT: TEST_MAIN: encrypt_aesni
   COMMENT: TEST_DESCRIPTION: Un message de 64 octets est initialisé à une valeur précise. Le nombre de tours est fixé à 12 et la clé de chiffrement étendue est initialisée à une valeur abstraite. L'appel à la fonction do_aesni_enc effectue le chiffrement et place le résultat à l'adresse mémoire pointée par le paramètre b. On vérifie ensuite que les cases du tableau b ont bien été initialisées et que le tableau a n'a pas été modifié.
   OPT: -cpp-extra-args='-DUSE_AESNI' -print
   -------------------------
*/
#include <__fc_builtin.h>
#include <string.h>
typedef unsigned short int byte;

/*
  The following code is extracted from the LGPL project libgcrypt.
*/

#define MAXROUNDS		14

/* Helper macro to force alignment to 16 bytes.  */
#ifdef HAVE_GCC_ATTRIBUTE_ALIGNED
# define ATTR_ALIGNED_16  __attribute__ ((aligned (16)))
#else
# define ATTR_ALIGNED_16
#endif

typedef union {
    int a;
    short b;
    char c[1];
    long d;
#ifdef HAVE_U64_TYPEDEF
    u64 e;
#endif
    float f;
    double g;
} PROPERLY_ALIGNED_TYPE;

/* Our context object.  */
typedef struct
{
  /* The first fields are the keyschedule arrays.  This is so that
     they are aligned on a 16 byte boundary if using gcc.  This
     alignment is required for the AES-NI code and a good idea in any
     case.  The alignment is guaranteed due to the way cipher.c
     allocates the space for the context.  The PROPERLY_ALIGNED_TYPE
     hack is used to force a minimal alignment if not using gcc of if
     the alignment requirement is higher that 16 bytes.  */
  union
  {
    PROPERLY_ALIGNED_TYPE dummy;
    byte keyschedule[MAXROUNDS+1][4][4];
  } u1;
  union
  {
    PROPERLY_ALIGNED_TYPE dummy;
    byte keyschedule[MAXROUNDS+1][4][4];
  } u2;
  int rounds;                /* Key-length-dependent number of rounds.  */
  unsigned int decryption_prepared:1; /* The decryption key schedule is available.  */
#ifdef USE_AESNI
  unsigned int use_aesni:1;           /* AES-NI shall be used.  */
#endif /*USE_AESNI*/
} RIJNDAEL_context ATTR_ALIGNED_16;

/* Macros defining alias for the keyschedules.  */
#define keyschenc  u1.keyschedule
#define keyschdec  u2.keyschedule

static inline void
do_aesni_enc (const RIJNDAEL_context *ctx, unsigned char *b,
              const unsigned char *a)
{
#define aesenc_xmm1_xmm0      ".byte 0x66, 0x0f, 0x38, 0xdc, 0xc1\n\t"
#define aesenclast_xmm1_xmm0  ".byte 0x66, 0x0f, 0x38, 0xdd, 0xc1\n\t"
  /* Note: For now we relax the alignment requirement for A and B: It
     does not make much difference because in many case we would need
     to memcpy them to an extra buffer; using the movdqu is much faster
     that memcpy and movdqa.  For CFB we know that the IV is properly
     aligned but that is a special case.  We should better implement
     CFB direct in asm.  */
  asm volatile ("movdqu %[src], %%xmm0\n\t"     /* xmm0 := *a     */
                "movdqa (%[key]), %%xmm1\n\t"    /* xmm1 := key[0] */
                "pxor   %%xmm1, %%xmm0\n\t"     /* xmm0 ^= key[0] */
                "movdqa 0x10(%[key]), %%xmm1\n\t"
                aesenc_xmm1_xmm0
                "movdqa 0x20(%[key]), %%xmm1\n\t"
                aesenc_xmm1_xmm0
                "movdqa 0x30(%[key]), %%xmm1\n\t"
                aesenc_xmm1_xmm0
                "movdqa 0x40(%[key]), %%xmm1\n\t"
                aesenc_xmm1_xmm0
                "movdqa 0x50(%[key]), %%xmm1\n\t"
                aesenc_xmm1_xmm0
                "movdqa 0x60(%[key]), %%xmm1\n\t"
                aesenc_xmm1_xmm0
                "movdqa 0x70(%[key]), %%xmm1\n\t"
                aesenc_xmm1_xmm0
                "movdqa 0x80(%[key]), %%xmm1\n\t"
                aesenc_xmm1_xmm0
                "movdqa 0x90(%[key]), %%xmm1\n\t"
                aesenc_xmm1_xmm0
                "movdqa 0xa0(%[key]), %%xmm1\n\t"
                "cmpl $10, %[rounds]\n\t"
                "jz .Lenclast%=\n\t"
                aesenc_xmm1_xmm0
                "movdqa 0xb0(%[key]), %%xmm1\n\t"
                aesenc_xmm1_xmm0
                "movdqa 0xc0(%[key]), %%xmm1\n\t"
                "cmpl $12, %[rounds]\n\t"
                "jz .Lenclast%=\n\t"
                aesenc_xmm1_xmm0
                "movdqa 0xd0(%[key]), %%xmm1\n\t"
                aesenc_xmm1_xmm0
                "movdqa 0xe0(%[key]), %%xmm1\n"

                ".Lenclast%=:\n\t"
                aesenclast_xmm1_xmm0
                "movdqu %%xmm0, %[dst]\n"
                : [dst] "=m" (*b)
                : [src] "m" (*a),
                  [key] "r" (ctx->keyschenc),
                  [rounds] "r" (ctx->rounds)
                : "cc", "memory");
#undef aesenc_xmm1_xmm0
#undef aesenclast_xmm1_xmm0
}


//-----main: encrypt_aesni
void encrypt_aesni(void)
{
  RIJNDAEL_context ctx;
  unsigned char b[64];
  unsigned char a_init[64] = {0x85,0x50,0x43,0xda, 0x06,0x99,0xd8,0x3b,
                              0x65,0xf7,0x1d,0xf7, 0x95,0xd4,0x34,0x5d,
                              0x6e,0x21,0x01,0xba, 0x2a,0xbd,0x7f,0xab,
                              0xa7,0x6d,0xe7,0xcd, 0x72,0xcf,0xce,0xa1,
                              0xa7,0x4a,0xb8,0x12, 0xef,0x2d,0x6b,0xd5,
                              0xdc,0x09,0xb9,0xdd, 0x09,0x27,0x7c,0x86,
                              0x35,0x60,0x99,0xea, 0x72,0xbb,0x93,0x9e,
                              0x9e,0x16,0x7b,0xd4, 0x8c,0x81,0x8a,0x53};
  unsigned char a[64];
  unsigned char test;
  int i,j,k;

  memcpy(a, a_init, 64 * sizeof(unsigned char));

  for(i=0; i<MAXROUNDS+1; i++)
    for(j=0; j<4; j++)
      for(k=0; k<4; k++){
        Frama_C_make_unknown(&ctx.keyschenc[i][j][k], sizeof(byte));
      }
  ctx.rounds = 12;
  ctx.use_aesni = 1;

  do_aesni_enc(&ctx, b, a);

  test = 0x00U;
  for(i=0; i<64; i++)
    test ^= b[i];

  Frama_C_dump_each();
}