embedded IPsec source code documentation


esp.c

Go to the documentation of this file.
00001 /*
00002  * embedded IPsec
00003  * Copyright (c) 2003 Niklaus Schild and Christian Scheurer, HTI Biel/Bienne
00004  * All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without modification,
00007  * are permitted provided that the following conditions are met:
00008  *
00009  * 1. Redistributions of source code must retain the above copyright notice,
00010  *    this list of conditions and the following disclaimer.
00011  * 2. Redistributions in binary form must reproduce the above copyright notice,
00012  *    this list of conditions and the following disclaimer in the documentation
00013  *    and/or other materials provided with the distribution.
00014  * 3. The name of the author may not be used to endorse or promote products
00015  *    derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
00018  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00019  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
00020  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00021  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
00022  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00023  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00024  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
00025  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
00026  * OF SUCH DAMAGE.
00027  *
00028  */
00029 
00061 #include <string.h>
00062 
00063 #include "ipsec/ipsec.h"
00064 #include "ipsec/util.h"
00065 #include "ipsec/debug.h"
00066 
00067 #include "ipsec/sa.h"
00068 #include "ipsec/des.h"
00069 #include "ipsec/md5.h"
00070 #include "ipsec/sha1.h"
00071 
00072 #include "ipsec/esp.h"
00073 
00074 
00075 __u32 ipsec_esp_bitmap  = 0;                    
00078 __u32 ipsec_esp_lastSeq = 0;                    
00090 __u8 ipsec_esp_get_padding(int len)
00091 {
00092         int padding ;
00093 
00094         for(padding = 0; padding < 8; padding++)
00095                 if(((len+padding) % 8) == 0)
00096                         break ;
00097         return padding ;
00098 }
00099 
00111 ipsec_status ipsec_esp_decapsulate(ipsec_ip_header *packet, int *offset, int *len, sad_entry *sa)
00112  {
00113         int ret_val = IPSEC_STATUS_NOT_INITIALIZED;                     /* by default, the return value is undefined */
00114         __u8                            ip_header_len ;
00115         int                                     local_len ;
00116         int                                     payload_offset ;
00117         int                                     payload_len ;
00118         ipsec_ip_header         *new_ip_packet ;
00119         esp_packet                      *esp_header ;                   
00120         char                            cbc_iv[IPSEC_ESP_IV_SIZE] ;
00121         unsigned char           digest[IPSEC_MAX_AUTHKEY_LEN];
00122 
00123         IPSEC_LOG_TRC(IPSEC_TRACE_ENTER, 
00124                       "ipsec_esp_decapsulate", 
00125                                   ("packet=%p, *offset=%d, *len=%d sa=%p",
00126                               (void *)packet, *offset, *len, (void *)sa)
00127                                  );
00128         
00129         ip_header_len = (packet->v_hl & 0x0f) * 4 ;
00130         esp_header = (esp_packet*)(((char*)packet)+ip_header_len) ; 
00131         payload_offset = ip_header_len + IPSEC_ESP_SPI_SIZE + IPSEC_ESP_SEQ_SIZE ;
00132         payload_len = ipsec_ntohs(packet->len) - ip_header_len - IPSEC_ESP_HDR_SIZE ;
00133 
00134 
00135         if(sa->auth_alg != 0)
00136         {
00137 
00138                 /* preliminary anti-replay check (without updating the global sequence number window)     */
00139                 /* This check prevents useless ICV calculation if the Sequence Number is obviously wrong  */
00140                 ret_val = ipsec_check_replay_window(ipsec_ntohl(esp_header->sequence), ipsec_esp_lastSeq, ipsec_esp_bitmap);
00141                 if(ret_val != IPSEC_AUDIT_SUCCESS)
00142                 {
00143                         IPSEC_LOG_AUD("ipsec_esp_decapsulate", IPSEC_AUDIT_SEQ_MISMATCH, ("packet rejected by anti-replay check (lastSeq=%08lx, seq=%08lx, window size=%d)", ipsec_esp_lastSeq, ipsec_ntohl(esp_header->sequence), IPSEC_SEQ_MAX_WINDOW) );
00144                         return ret_val;
00145                 }
00146 
00147                 /* recalcualte ICV */
00148                 switch(sa->auth_alg) {
00149 
00150                 case IPSEC_HMAC_MD5: 
00151                         hmac_md5((unsigned char *)esp_header, payload_len-IPSEC_AUTH_ICV+IPSEC_ESP_HDR_SIZE,
00152                                  (unsigned char *)sa->authkey, IPSEC_AUTH_MD5_KEY_LEN, (unsigned char *)&digest);
00153                         ret_val = IPSEC_STATUS_SUCCESS; 
00154                         break;
00155                 case IPSEC_HMAC_SHA1: 
00156                         hmac_sha1((unsigned char *)esp_header, payload_len-IPSEC_AUTH_ICV+IPSEC_ESP_HDR_SIZE,
00157                                   (unsigned char *)sa->authkey, IPSEC_AUTH_SHA1_KEY_LEN, (unsigned char *)&digest);
00158                         ret_val = IPSEC_STATUS_SUCCESS; 
00159                         break;
00160                 default:
00161                         IPSEC_LOG_ERR("ipsec_esp_decapsulate", IPSEC_STATUS_FAILURE, ("unknown HASH algorithm for this ESP")) ;
00162                         IPSEC_LOG_TRC(IPSEC_TRACE_RETURN, "ipsec_esp_decapsulate", ("return = %d", IPSEC_STATUS_FAILURE) );
00163                         return IPSEC_STATUS_FAILURE;
00164                 }
00165                 
00166                 /* compare ICV */
00167                 if(memcmp(((char*)esp_header)+IPSEC_ESP_HDR_SIZE+payload_len-IPSEC_AUTH_ICV, digest, IPSEC_AUTH_ICV) != 0) {
00168                         IPSEC_LOG_ERR("ipsec_esp_decapsulate", IPSEC_STATUS_FAILURE, ("ESP ICV does not match")) ;
00169                         IPSEC_LOG_TRC(IPSEC_TRACE_RETURN, "ipsec_esp_decapsulate", ("return = %d", IPSEC_STATUS_FAILURE) );
00170                         return IPSEC_STATUS_FAILURE;
00171                 }
00172 
00173                 /* reduce payload by ICV */
00174                 payload_len -= IPSEC_AUTH_ICV ;
00175 
00176                 /* post-ICV calculationn anti-replay check (this call will update the global sequence number window) */
00177                 ret_val = ipsec_update_replay_window(ipsec_ntohl(esp_header->sequence), (__u32 *)&ipsec_esp_lastSeq, (__u32 *)&ipsec_esp_bitmap);
00178                 if(ret_val != IPSEC_AUDIT_SUCCESS)
00179                 {
00180                         IPSEC_LOG_AUD("ipsec_esp_decapsulate", IPSEC_AUDIT_SEQ_MISMATCH, ("packet rejected by anti-replay update (lastSeq=%08lx, seq=%08lx, window size=%d)", ipsec_esp_lastSeq, ipsec_ntohl(esp_header->sequence), IPSEC_SEQ_MAX_WINDOW) );
00181                         return ret_val;
00182                 }
00183 
00184         }
00185 
00186 
00187         /* decapsulate the packet according the SA */
00188         if(sa->enc_alg == IPSEC_3DES)
00189         {
00190                 /* copy IV from ESP payload */
00191                 memcpy(cbc_iv, ((char*)packet)+payload_offset, IPSEC_ESP_IV_SIZE);
00192 
00193                 /* decrypt ESP packet */
00194                 cipher_3des_cbc(((char*)packet)+payload_offset + IPSEC_ESP_IV_SIZE, payload_len-IPSEC_ESP_IV_SIZE, (unsigned char *)sa->enckey, (char*)&cbc_iv,
00195                                                  DES_DECRYPT, ((char*)packet)+payload_offset + IPSEC_ESP_IV_SIZE);
00196         }
00197 
00198         *offset = payload_offset+IPSEC_ESP_IV_SIZE ;
00199 
00200         new_ip_packet = (ipsec_ip_header*)(((char*)packet) + payload_offset + IPSEC_ESP_IV_SIZE) ;
00201         local_len = ipsec_ntohs(new_ip_packet->len) ;
00202 
00203         if( (local_len < IPSEC_MIN_IPHDR_SIZE) || (local_len > IPSEC_MTU))
00204         {
00205                 IPSEC_LOG_ERR("ipsec_esp_decapsulate", IPSEC_STATUS_FAILURE, ("decapsulated strange packet")) ;
00206                 IPSEC_LOG_TRC(IPSEC_TRACE_RETURN, "ipsec_esp_decapsulate", ("return = %d", IPSEC_STATUS_BAD_PACKET) );
00207                 return IPSEC_STATUS_BAD_PACKET;
00208         }
00209         *len = local_len ;
00210 
00211         sa->sequence_number++ ;
00212 
00213         IPSEC_LOG_TRC(IPSEC_TRACE_RETURN, "ipsec_esp_decapsulate", ("return = %d", IPSEC_STATUS_SUCCESS) );
00214         return IPSEC_STATUS_SUCCESS;
00215  }
00216 
00230  ipsec_status ipsec_esp_encapsulate(ipsec_ip_header *packet, int *offset, int *len, sad_entry *sa, __u32 src_addr, __u32 dest_addr)
00231  {
00232         int ret_val = IPSEC_STATUS_NOT_INITIALIZED;                     /* by default, the return value is undefined */
00233         __u8                            tos ;
00234         int                                     inner_len ;
00235         int                                     payload_offset ;
00236         int                                     payload_len ;
00237         __u8                            padd_len ;
00238         __u8                            *pos ;
00239         __u8                            padd ;
00240         ipsec_ip_header         *new_ip_header ;
00241         ipsec_esp_header        *new_esp_header ;
00242         unsigned char           iv[IPSEC_ESP_IV_SIZE] = {0xD4, 0xDB, 0xAB, 0x9A, 0x9A, 0xDB, 0xD1, 0x94} ;
00243         unsigned char           cbc_iv[IPSEC_ESP_IV_SIZE] ;
00244         unsigned char           digest[IPSEC_MAX_AUTHKEY_LEN];
00245 
00246         IPSEC_LOG_TRC(IPSEC_TRACE_ENTER, 
00247                       "ipsec_esp_encapsulate", 
00248                                   ("packet=%p, *offset=%d, *len=%d, sa=%p, src_addr=%lu, dest_addr=%lu",
00249                               (void *)packet, *offset, *len, (void *)sa, src_addr, dest_addr)
00250                                  );
00251 
00252         /* set new packet header pointers */
00253         new_ip_header = (ipsec_ip_header*)(((char*)packet) - IPSEC_ESP_IV_SIZE - IPSEC_ESP_HDR_SIZE - IPSEC_MIN_IPHDR_SIZE) ;
00254         new_esp_header = (ipsec_esp_header*)(((char*)packet) - IPSEC_ESP_IV_SIZE - IPSEC_ESP_HDR_SIZE) ;
00255         payload_offset = (((char*)packet) - ((char*)new_ip_header)) ;
00256 
00257         inner_len = ipsec_ntohs(packet->len) ;
00258 
00259         /* save TOS from inner header */
00260         tos = packet->tos ;
00261 
00263         // packet->ttl--;
00264         // packet->chksum = ip_chksum(packet, sizeof(ip_header));
00265         if (packet->ttl == 0)
00266         {
00267                 IPSEC_LOG_TRC(IPSEC_TRACE_RETURN, "ipsec_esp_encapsulate", ("return = %d", IPSEC_STATUS_TTL_EXPIRED) );
00268                 return IPSEC_STATUS_TTL_EXPIRED;
00269         }
00270         
00271         /* add padding if needed */
00272         padd_len = ipsec_esp_get_padding(inner_len+2) ; 
00273         pos = ((char*)packet)+inner_len ;
00274         if(padd_len != 0)
00275         {
00276                 padd = 1 ;
00277                 while(padd <= padd_len)
00278                         *pos++ = padd++ ;
00279         }
00280         
00281         /* append padding length and next protocol field to the payload */
00282         *pos++ = padd_len ;
00283         /* in tunnel mode the next protocol field is always IP */
00284         *pos = 0x04 ; 
00285 
00286         payload_len = inner_len+IPSEC_ESP_HDR_SIZE+IPSEC_ESP_IV_SIZE + padd_len + 2 ;
00287 
00288         /* decapsulate the packet according the SA */
00289         if(sa->enc_alg == IPSEC_3DES)
00290         {
00291                 /* get IV from SA */
00292                 memcpy(cbc_iv, iv, IPSEC_ESP_IV_SIZE);
00293 
00294                 /* encrypt ESP packet */
00295                 cipher_3des_cbc((__u8 *)packet, inner_len+padd_len+2, (__u8 *)sa->enckey, (__u8 *)&cbc_iv,
00296                                                  DES_ENCRYPT, (__u8 *)packet);
00297         }
00298 
00299         /* insert IV in fron of packet */
00300         memcpy( ((char*)packet)-IPSEC_ESP_IV_SIZE, iv, IPSEC_ESP_IV_SIZE) ;
00301 
00302         /* setup ESP header */
00303         new_esp_header->spi = sa->spi;
00305         sa->sequence_number++ ;
00306         new_esp_header->sequence_number = ipsec_htonl(sa->sequence_number) ;
00307 
00308         /* calculate the ICV if needed */
00309         if(sa->auth_alg != 0)
00310         {
00311                 /* recalcualte ICV */
00312                 switch(sa->auth_alg) {
00313 
00314                 case IPSEC_HMAC_MD5: 
00315                         hmac_md5((unsigned char *)new_esp_header, payload_len,
00316                                  (unsigned char *)sa->authkey, IPSEC_AUTH_MD5_KEY_LEN, (unsigned char *)&digest);
00317                         ret_val = IPSEC_STATUS_SUCCESS; 
00318                         break;
00319                 case IPSEC_HMAC_SHA1: 
00320                         hmac_sha1((unsigned char *)new_esp_header, payload_len,
00321                                   (unsigned char *)sa->authkey, IPSEC_AUTH_SHA1_KEY_LEN, (unsigned char *)&digest);
00322                         ret_val = IPSEC_STATUS_SUCCESS; 
00323                         break;
00324                 default:
00325                         IPSEC_LOG_ERR("ipsec_esp_encapsulate", IPSEC_STATUS_FAILURE, ("unknown HASH algorithm for this ESP")) ;
00326                         IPSEC_LOG_TRC(IPSEC_TRACE_RETURN, "ipsec_esp_encapsulate", ("return = %d", IPSEC_STATUS_FAILURE) );
00327                         return IPSEC_STATUS_FAILURE;
00328                 }
00329                 
00330                 /* set ICV */
00331                 memcpy(((char*)new_esp_header)+payload_len, digest, IPSEC_AUTH_ICV);
00332                 
00333                 /* increase payload by ICV */
00334                 payload_len += IPSEC_AUTH_ICV ;
00335         }
00336 
00337         /* setup IP header */
00338         new_ip_header->v_hl = 0x45 ;
00339         new_ip_header->tos = tos ;
00340         //new_ip_header->len = ipsec_htons(inner_len+padd_len+2+IPSEC_ESP_IV_SIZE+IPSEC_ESP_HDR_SIZE+IPSEC_MIN_IPHDR_SIZE) ; /**@todo when auth enables the size changes */
00341         new_ip_header->len = ipsec_htons(payload_len+ IPSEC_MIN_IPHDR_SIZE); 
00342         new_ip_header->id = 1000 ;      
00343         new_ip_header->offset = 0 ;
00344         new_ip_header->ttl = 64 ;
00345         new_ip_header->protocol = IPSEC_PROTO_ESP ;
00346         new_ip_header->chksum = 0 ;
00347         new_ip_header->src = src_addr ;
00348         new_ip_header->dest = dest_addr ;
00349 
00350         /* set checksum */
00351         new_ip_header->chksum = ipsec_ip_chksum(new_ip_header, sizeof(ipsec_ip_header)) ;
00352 
00353         /* setup return values */
00354         *offset = payload_offset*(-1) ;
00355         *len = payload_len + IPSEC_MIN_IPHDR_SIZE ;
00356 
00357         IPSEC_LOG_TRC(IPSEC_TRACE_RETURN, "ipsec_esp_encapsulate", ("return = %d", IPSEC_STATUS_SUCCESS) );
00358         return IPSEC_STATUS_SUCCESS;
00359  }
00360 

Copyright 2003 by Christian Scheurer and Niklaus Schild