Résultats (parfois) aléatoires avec CommonCrypto

0x5A0x5A Membre
août 2014 modifié dans Objective-C, Swift, C, C++ #1

Bonjour à  tous,


 


J'ai écris un bout de code en Objective-C & C me calculant le hash suivant:


HMAC-SHA512(apiUrl + SHA256(data), secret)


 


- apiUrl est une chaine de caractères (NSString)


- le + veut dire concaténation


- secret est aussi une chaine de caractères qui doit d'abord être décodée (base64) avant d'être passée en paramètre


 


Le problème c'est que le résultat que me retourne mon programme est parfois juste et parfois faux ... Je redoute que ce soit une fuite de mémoire mais je ne vois pas d'ou ça pourrait venir. Voilà  le bout de code :



#import <Foundation/Foundation.h>

#include <CommonCrypto/CommonDigest.h>
#include <CommonCrypto/CommonHMAC.h>

@interface ObjCUtils : NSObject

+ (unsigned char*) sha256 :(NSString*)string;
+ (const void*) decodeBase64 :(NSString*)string;
+ (char*) hmacSHA512WithKey :(const char*)key andData :(const void *)data;
+ (NSString*) base64EncodeData :(const char*)data ofLength :(NSUInteger)length;
+ (char*) concatString :(NSString*)string withHash :(unsigned char*)hash ofLength:(NSUInteger)length;
+ (NSString*) computeWithSHA256Data:(NSString*)sha256Data apiSecret:(NSString*)apiSecret urlPath :(NSString*)urlPath;

@end

@implementation ObjCUtils

+ (unsigned char*) sha256:(NSString*)string
{
unsigned char* hash = malloc(sizeof(unsigned char) * CC_SHA256_DIGEST_LENGTH);
CC_SHA256([string cStringUsingEncoding:NSUTF8StringEncoding], (unsigned int) [string length], hash);

return hash;
}

+ (const void*) decodeBase64:(NSString*)encodedString
{
return [[[NSData alloc] initWithBase64EncodedString:encodedString options:0] bytes];
}

+ (char *) hmacSHA512WithKey:(const char *)key andData:(const void *)data
{
char* hash = malloc(sizeof(char) * (CC_SHA512_DIGEST_LENGTH));
CCHmac(kCCHmacAlgSHA512, key, strlen(key), data, strlen(data), hash);

return hash;
}

+ (NSString*) base64EncodeData:(const char *)data ofLength:(NSUInteger)length
{
return [[NSData dataWithBytes:data length:length] base64EncodedStringWithOptions:NSUTF8StringEncoding];
}

+ (char *) concatString:(NSString*)string withHash:(unsigned char*)hash ofLength:(NSUInteger)length
{
const char* c_string = [string cStringUsingEncoding:NSUTF8StringEncoding];
char * result = malloc(sizeof(char) * (strlen(c_string) + length));
strcpy(result, c_string);
strncat(result, (char *)hash, length);

return result;
}

+ (NSString*) computeWithSHA256Data:(NSString*)sha256Data apiSecret:(NSString*)apiSecret urlPath:(NSString*)urlPath
{
unsigned char* res_sha256 = [ObjCUtils sha256:sha256Data];
char* message = [ObjCUtils concatString:urlPath withHash:res_sha256 ofLength:CC_SHA256_DIGEST_LENGTH];
const void* decoded_secret = [ObjCUtils decodeBase64:apiSecret];
char* res_hmac = [ObjCUtils hmacSHA512WithKey:decoded_secret andData:message];
NSString* encodedHMAC = [ObjCUtils base64EncodeData:res_hmac ofLength:CC_SHA512_DIGEST_LENGTH];

free(res_sha256);
free(message);
free(res_hmac);

return encodedHMAC;
}

@end


int main(int argc, const char* argv[])
{
@autoreleasepool {

NSString* secret = @YSBxdWl0ZSBsb25nIHN0cmluZyBpc24ndCBpdCBteSBkZWFy;
NSString* apiUrl = @apiUrl;

for (int i = 0; i < 10; i++) {
NSLog(@%i: %@", i, [ObjCUtils computeWithSHA256Data:[[NSNumber numberWithInt:i] stringValue] apiSecret:secret urlPath:apiUrl]);
}
}

return 0;
}

Vous remarquerez que si vous exécuter le programme plusieurs fois certaines lignes varient à  presque chaque exécution.


 


Je vous met aussi le code en Python qui est censé produire des résultats corrects :



import hashlib
import hmac
import base64
import time
import urllib.request, urllib.parse, urllib.error

def compute(data, apiUrl, secret):
encoded = str(data).encode()
message = apiUrl.encode() + hashlib.sha256(encoded).digest()

signature = hmac.new(base64.b64decode(secret), message, hashlib.sha512)
sigdigest = base64.b64encode(signature.digest())

return sigdigest.decode()

for x in range(0, 10):
print(str(x) +": "+ compute(x, "apiUrl", "YSBxdWl0ZSBsb25nIHN0cmluZyBpc24ndCBpdCBteSBkZWFy"))


Merci beaucoup pour votre aide !


Mots clés:

Réponses

  • AliGatorAliGator Membre, Modérateur
    Hello,

    Je n'ai pas tout vérifié de ton code, mais tu sembles utiliser très souvent les "char*" et "const char*" directement. Es-tu bien sûr quand tu manipules des chaà®nes C ("char*") que tu as bien mis le terminateur ("\0") à  la fin de la chaà®ne ? Car si tu ne l'as pas mis, il ne sait pas quand la chaà®ne s'arrête et il va aller continuer à  se balader plus loin dans la mémoire. Et les fonctions comme "strlen(cstr)" vont d'ailleurs aussi retourner n'importe quoi, puisqu'elle retournent la longueur d'une chaà®ne C... en la parcourant jusqu'à  trouver le terminateur de fin de chaà®ne "\0".

    Par exemple, rien que pour ta méthode "+concatString:withHash:ofLength:", qui utilise strncat, je suis en train de me demander s'il rajoute le "\0" de lui-même. Dans le doute, je serais toi je ne manipulerai que des NSString et des NSData jusqu'au bout, ne les convertissant en "const char*" qu'au dernier moment (lorsque tu appelles tes fonctions de CommonCrypto, et pas avant). Donc par exemple, là , passer juste 2 NSString (la chaà®ne et le hash), en NSString et pas en char* donc, et juste utiliser la méthode "-[NSString stringByAppendingString:" et basta, au moins pas de risque de fuite mémoire ou d'oubli de "\0" à  manipuler des données C bas niveau directement.

    Et je te conseille dans la même idée de n'exposer dans tes méthodes de ObjCUtils que des NSString et et NSData, en tout cas éviter d'exposer (en paramètres d'entrée ou en type de retour) directement des "(const) char*" " car en plus dans ce cas en regardant juste l'API on ne sait jamais quand on manipule ce genre de données C bas niveau si on est sensé les libérer avec un free() nous-même ou si l'API s'en charge... la sémantique n'étant pas claire sur le sujet (tu as bien la Create Rule mais là  vu les nommages non conventionnels Apple de tes méthodes de toute façon...).

    Alors qu'au moins si ton API de ta classe ObjCUtils n'expose (en paramètres d'entrée comme en valeurs de retour) que des NSData et NSString tu ne vas manipuler que des objets Objective-C. Et c'est que dans l'implémentation quand tu as besoin de convertir ça en "(const) char*" juste pour le passer aux fonctions de CommonCrypto que tu feras la conversion, en interne uniquement dans l'implémentation des méthodes qui vont bien.

    Ca t'évitera des risques de fuites mémoire ou d'oubli de caractère de terminaison de chaà®ne C ou autres joyeusetés.
  • 0x5A0x5A Membre

    Merci beaucoup pour ta réponse. J'ai suivi tes conseils et maintenant ça à  l'air de marcher correctement. Par contre je pense que le problème se trouvait plus dans +hmacSHA512WithKey:andData: que dans +concatString:withHash:ofLength ou les strlen sont clairement utilisés sur des variables qui ne sont pas chaines de caractères ...  ::)


     


    Voilà  le nouveau code :



    @interface ObjCUtils : NSObject

    + (NSData *) sha256:(NSString *)string;
    + (NSData *) concatString:(NSString *)string withHash:(NSData *)hash;
    + (NSData *) decodeBase64:(NSString *)string;
    + (NSData *) hmacSHA512WithKey:(NSData *)key andData:(NSData *)data;
    + (NSString *) base64EncodeData:(NSData *)data;
    + (NSString *) computeWithSHA256Data:(NSString *)sha256Data apiSecret:(NSString *)apiSecret urlPath:(NSString *)urlPath;

    @end

    @implementation ObjCUtils

    + (NSData *) sha256:(NSString *)string
    {
    unsigned char hash[CC_SHA256_DIGEST_LENGTH];
    CC_SHA256([string cStringUsingEncoding:NSUTF8StringEncoding], (unsigned int) [string length], hash);

    return [NSData dataWithBytes:hash length:CC_SHA256_DIGEST_LENGTH];
    }

    + (NSData *) concatString:(NSString *)string withHash:(NSData *)hash
    {
    NSMutableData* data = [NSMutableData dataWithData:[string dataUsingEncoding:NSUTF8StringEncoding]];
    [data appendData:hash];

    return data;
    }

    + (NSData *) decodeBase64:(NSString *)encodedString
    {
    return [[NSData alloc] initWithBase64EncodedString:encodedString options:0];
    }

    + (NSData *) hmacSHA512WithKey:(NSData *)key andData:(NSData *)data
    {
    char hash[CC_SHA512_DIGEST_LENGTH];
    CCHmac(kCCHmacAlgSHA512, [key bytes], [key length], [data bytes], [data length], hash);

    return [NSData dataWithBytes:hash length:CC_SHA512_DIGEST_LENGTH];
    }

    + (NSString *) base64EncodeData:(NSData *)data
    {
    return [data base64EncodedStringWithOptions:NSUTF8StringEncoding];
    }

    + (NSString*) computeWithSHA256Data:(NSString*)sha256Data apiSecret:(NSString*)apiSecret urlPath:(NSString*)urlPath
    {
    NSData* sha256Hash = [ObjCUtils sha256:sha256Data];
    NSData* message = [ObjCUtils concatString:urlPath withHash:sha256Hash];
    NSData* decodedSecret = [ObjCUtils decodeBase64:apiSecret];
    NSData* hmacHash = [ObjCUtils hmacSHA512WithKey:decodedSecret andData:message];

    return [ObjCUtils base64EncodeData:hmacHash];
    }

    @end
Connectez-vous ou Inscrivez-vous pour répondre.