Search code examples
cperlopensslswigecdsa

Segmentation fault when signing a message using OpenSSL, SWIG, and Perl


We were using SWIG to make a C cryptographic utility library available to Perl. We are able to generate keys, create digests, but the signing code causes a segmentation fault, which we believe may be in the OpenSSL code itself but it's difficult to be sure.

This problem only comes up when running the code with SWIG, the native C code works.

In Perl, we call this:

$signature = key_utils::mysignMessageWithPem($pem, $message);

Which calls this code in the .i file:

%newobject mysignMessageWithPem;                                                                                                                                                                                                                                                                                              

%inline %{                                                                                                                                                                                                                                                                                                                    
  char *mysignMessageWithPem(char *pem, char *message) {                                                                                                                                                                                                                                                                      
    char *ret = malloc(145);                                                                                                                                                                                                                                                                                                  
    char *err = malloc(5);                                                                                                                                                                                                                                                                                                    
    int errorCode;                                                                                                                                                                                                                                                                                                            

    memcpy(err, "ERROR", 5);                                                                                                                                                                                                                                                                                                  

    errorCode = signMessageWithPem(pem, message, &ret);                                                                                                                                                                                                                                                                       
    char *signature = ret;                                                                                                                                                                                                                                                                                                    

    if (errorCode == NOERROR) {                                                                                                                                                                                                                                                                                               
      return signature;                                                                                                                                                                                                                                                                                                       
    } else {                                                                                                                                                                                                                                                                                                                  
      return err;                                                                                                                                                                                                                                                                                                             
    }                                                                                                                                                                                                                                                                                                                         

  }                                                                                                                                                                                                                                                                                                                           
%}                     

Which calls this C code:

int signMessageWithPem(char *message, char *pem, char **signature) {                                                                                                                                                                                                                                                          

unsigned int meslen = strlen(message);                                                                                                                                                                                                                                                                                    
unsigned char *messagebytes = calloc(meslen, sizeof(unsigned char));                                                                                                                                                                                                                                                      
ECDSA_SIG *sig = NULL;                                                                                                                                                                                                                                                                                                    
memcpy(messagebytes, message, meslen);                                                                                                                                                                                                                                                                                    

EC_KEY *key = NULL;                                                                                                                                                                                                                                                                                                       
BIO *in = NULL;                                                                                                                                                                                                                                                                                                           
unsigned char *buffer = NULL;                                                                                                                                                                                                                                                                                             

char *sha256ofMsg = calloc(SHA256_HEX_STRING, sizeof(char));                                                                                                                                                                                                                                                              
unsigned char *outBytesOfsha256ofMsg = calloc(SHA256_STRING, sizeof(unsigned char));                                                                                                                                                                                                                                      

digestOfBytes(messagebytes, &sha256ofMsg, "sha256", meslen);                                                                                                                                                                                                                                                              
sha256ofMsg[64] = '\0';                                                                                                                                                                                                                                                                                                   
createDataWithHexString(sha256ofMsg, &outBytesOfsha256ofMsg);                                                                                                                                                                                                                                                             

in = BIO_new(BIO_s_mem());                                                                                                                                                                                                                                                                                                
BIO_puts(in, pem);                                                                                                                                                                                                                                                                                                        
PEM_read_bio_ECPrivateKey(in, &key, NULL, NULL);                                                                                                                                                                                                                                                                          

sig = ECDSA_do_sign((const unsigned char*)outBytesOfsha256ofMsg, SHA256_DIGEST_LENGTH, key);                                                                                                                                                                                                                              
int verify = ECDSA_do_verify((const unsigned char*)outBytesOfsha256ofMsg, SHA256_DIGEST_LENGTH, sig, key);                                                                                                                                                                                                                

if(verify != 1) {                                                                                                                                                                                                                                                                                                         
    return ERROR;                                                                                                                                                                                                                                                                                                         
}                                                                                                                                                                                                                                                                                                                         

int buflen = ECDSA_size(key);                                                                                                                                                                                                                                                                                             
buffer = OPENSSL_malloc(buflen);                                                                                                                                                                                                                                                                                          

int derSigLen = i2d_ECDSA_SIG(sig, &buffer);                                                                                                                                                                                                                                                                              

char *hexData = calloc(derSigLen, sizeof(char));                                                                                                                                                                                                                                                                          
memcpy(hexData, buffer-derSigLen, derSigLen);                                                                                                                                                                                                                                                                             

char *hexString = calloc(derSigLen*2+1, sizeof(char));                                                                                                                                                                                                                                                                    

hexString[derSigLen * 2] = '\0';                                                                                                                                                                                                                                                                                          
toHexString(hexData, derSigLen, hexString);                                                                                                                                                                                                                                                                               

memcpy(*signature, hexString, derSigLen*2);                                                                                                                                                                                                                                                                               
signature[derSigLen * 2] = '\0';                                                                                                                                                                                                                                                                                          

EC_KEY_free(key);                                                                                                                                                                                                                                                                                                         

BIO_free_all(in);                                                                                                                                                                                                                                                                                                         
free(sha256ofMsg);                                                                                                                                                                                                                                                                                                        
free(outBytesOfsha256ofMsg);                                                                                                                                                                                                                                                                                              
free(hexData);                                                                                                                                                                                                                                                                                                            
free(hexString);                                                                                                                                                                                                                                                                                                          

return NOERROR;
}                  

And returns Segmentation Fault. The most informative error we have gotten is perl crashed with SIGSEGV in EC_KEY_get_key_method_data()

The full code is here: https://github.com/aleitner/bitpay-perl/tree/stack-overflow-question

Is this a bug with SSL, or are we doing this wrong?


Solution

  • The answer to this question is: we were calling the arguments in the wrong order.

    Seriously. The line:

    $signature = key_utils::mysignMessageWithPem($pem, $message);

    needed to be:

    $signature = key_utils::mysignMessageWithPem($message, $pem);

    We were in fact doing something wrong. I was tempted to remove the question, but maybe the answer can serve as a cautionary tale or something.