Aibek Prenov Aibek Prenov - 1 month ago 12
Swift Question

One function gives several results in swift

I have a method in objective-C which I call from swift. It worked pretty well in swift 2, but in swift 3 the behaviour has changed. It gives me 3 different results, even though I send the same parameters.

Sometimes it doesnt find pfile, sometimes it fails on pin checking, sometimes works good and gives me x509.

char* ParsePKCS12(unsigned char* pkcs12_path, unsigned char * pin) {
printf("PARSE PATH: %s\n", pkcs12_path);
printf("PASSWORD: %s\n", pin);

NSString *pfile = [NSString stringWithUTF8String:pkcs12_path];
FILE *fp;
PKCS12 *p12;
EVP_PKEY *pkey;
X509 *cert;

BIO *databio = BIO_new(BIO_s_mem());
STACK_OF(X509) *ca = NULL;

if([[NSFileManager defaultManager] fileExistsAtPath:pfile]) {
NSLog(@"ok, pfile exists!");
} else {
NSLog(@"error, pfile does not exists!");
return "-1";
}
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
fp = fopen([pfile UTF8String], "rb");
p12 = d2i_PKCS12_fp(fp, NULL);
fclose (fp);
if (!p12) {
fprintf(stderr, "Error reading PKCS#12 file\n");
ERR_print_errors_fp(stderr);
return "-1";
}

if (!PKCS12_parse(p12, (const char *)pin, &pkey, &cert, &ca)) { //Error at parsing or pin error
fprintf(stderr, "Error parsing PKCS#12 file\n");
ERR_print_errors_fp(stderr);
ERR_print_errors(databio);
return "-1";
}

BIO *bio = NULL;
char *pem = NULL;

if (NULL == cert) {
//return NULL;
return "-1";
}

bio = BIO_new(BIO_s_mem());
if (NULL == bio) {
return "-1";
}

if (0 == PEM_write_bio_X509(bio, cert)) {
BIO_free(bio);
//return NULL;
}

pem = (char *) malloc(bio->num_write + 1);
if (NULL == pem) {
BIO_free(bio);
return "-1";
}

memset(pem, 0, bio->num_write + 1);
BIO_read(bio, pem, bio->num_write);
BIO_free(bio);

PKCS12_free(p12);

return pem;
}


this code I call in swift like this:

self.x509 = String(cString:ParsePKCS12(UnsafeMutablePointer<UInt8>(mutating: self.path),
UnsafeMutablePointer<UInt8>(mutating: "123456"))!)

Answer

Your call

self.x509 = String(cString:ParsePKCS12(UnsafeMutablePointer<UInt8>(mutating: self.path),
                                           UnsafeMutablePointer<UInt8>(mutating: "123456"))!)

does not work reliably because in both

UnsafeMutablePointer<UInt8>(mutating: someSwiftString)

calls, the compiler creates a temporary C string representation of the Swift string and passes that to the function. But that C string is only valid until the UnsafeMutablePointer constructor returns, which means that the second string conversion can overwrite the first, or any other undefined behaviour.

The simplest solution would be to change the C function to take constant C strings (and use the default signedness):

char* ParsePKCS12(const char * pkcs12_path, const char * pin)

Then you can simply call it as

self.x509 = String(cString: ParsePKCS12(self.path, "123456"))

and the compiler creates temporary C strings which are valid during the call of ParsePKCS12().

Comments