sqlite3, arm64 et moi

MoKeSMoKeS Membre

Hello tout le monde !!


 


Je me permets de poster une petite question ici parce que Google m'a lâchement abandonné, lui qui est toujours si plein de réponses, de fraicheur et de volupté.


 


Il y a actuellement une application en ligne sur l'Appstore qui a été packagée et publiée il y a un moment. Je pense que vous êtes tous au fait qu'à  partir de février 2015, toutes les applications doivent être compatibles pour les devices en 64 bits, et donc qu'il faut "rajouter" le petit "arm64", aKa $(ARCHS_STANDARD) dans les architectures valides !


Bien évidemment, ce n'était pas le cas lorsque cette application (que je n'ai pas faite) a été publiée.


Je fais donc la mise à  jour, et je teste sur mon device. AUCUN PROBLàˆME tout roule ! 


 


Pourquoi je suis ici alors ? Et bien il y a un cas qui me pose problème. 


C'est quand l'application a été installé via l'app store (compilée en 32bits), puis que la mise à  jour soit faite (avec la nouvelle architecture). Au lancement de l'application : BOUM, un crash direct en bonne et due forme.


 


Voici ce que je découvre dans mes logs : 



SQLite error (26) : file is encrypted or is not a database [0x0000001a]

J'ai un peu creusé (et je continue encore d'ailleurs) et il semblerait que la fonction qui génère cette erreur soit celle ci : 



sqlite3_prepare_v2()

Ma question est la suivante : est-ce que lors du passage sur une architecture 64bits il y a quelque chose à  faire pour que sqlite3 ne pose pas ce genre de problème ? 


 


Je vous montre la fonction qui permet de préparer la requête : 



// Prepare query
- (SwingBool) prepareQuery : (SwingString *)query
{
// Reset columns name dictionary
[self _dictColIndexReset];

// Query format for SQLite
const char *sqliteQuery = [query UTF8String];

// SQLite Error code
SwingInteger sqliteError;

// Reset first fetch flag
_firstFetchDone = NO;

// Prepare query
sqliteError = sqlite3_prepare_v2( _database, sqliteQuery, -1, &_statement, NULL );

if ( sqliteError == SQLITE_OK )
{
return YES;
}
else
{
RAISE_EXCEPTION_SQLITE(sqliteError,sqliteQuery);
return NO;
}
}


La base en elle-même est bien cryptée et j'ai vérifié que je récupère bien la clé "d'encryption". Je ne vois pas ce qui a pu changer juste en changeant l'architecture...


 


Merci d'avance pour toute aide et suggestions que vous pourrez m'apporter !


 


MoKeS 


 


Réponses

  • CéroceCéroce Membre, Modérateur


    Je pense que vous êtes tous au fait qu'à  partir de février 2015, toutes les applications doivent être compatibles pour les devices en 64 bits, et donc qu'il faut "rajouter" le petit "arm64", aKa $(ARCHS_STANDARD) dans les architectures valides !




    Alors non, pas tout à  fait. L'architecture arm64 doit être présente dans les nouvelles soumissions. C'est à  dire que si une application 32 bits est déjà  sur le store, il n'y a pas d'obligation à  la mettre à  jour.


     


    Pour ton problème, c'est un cas très particulier, il va falloir refaire la manip (lancer l'ancienne version, créer une base, tester sur la nouvelle version). Bon courage.


     


    La seule raison évidente est que comme la taille des int change, tu aurais un problème avec un int signé.

  • MoKeSMoKeS Membre

    @Céroce ;


     


    Merci pour ta réponse rapide ! 


    Du coup j'ai voulu faire simple, tout rebasculer en 32, histoire de pouvoir au moins faire cette mise à  jour, mais je me rends compte que même en 32 (armv7) j'ai la même erreur au final.


    La conclusion ici est qu'il y a une différence (quelque part ..) entre la version en ligne, et la version actuelle qui a évolué, qui a été mal maintenue et c'est moi maintenant qui me tape ce genre d'histoires ... 


     


    Je te remercie pour la précision, et je vais continuer mes investigations. Je publierais la solution lorsque je l'aurais trouvé, ça pourra peut-être servir à  quelqu'un d'autre ... un jour ... 


     


     


    MoKeS


  • muqaddarmuqaddar Administrateur
    mars 2015 modifié #4

    Avec quel outil tu cryptes ta base ? SQLCypher ?


  • MoKeSMoKeS Membre

    @muqaddar


     


    Non SQLCypher n'est pas utilisé. De ce que je vois dans le code, ils ont fait une espèce d'encryption "maison" avec sqlite3 directement et un keychain.



    // Set the database key

    sqlite3_exec( database, [[SwingString stringWithFormat:@PRAGMA key = '%@'", databaseEncryptionKey] cStringUsingEncoding:NSASCIIStringEncoding], NULL, NULL, NULL );
  • muqaddarmuqaddar Administrateur

    J'avais eu un problème similaire, mais avec SQLCypher.


     


    Est-ce que le fichier encrypté a changé depuis la dernière version ?


    Est-il copié dans la sandbox ?


     


    Tu n'as pas le problème en debug sur ton iPhone, y compris si tu compiles l'ancienne version puis la nouvelle sans la supprimer avant ? (ce que fait la MAJ AppStore)


  • AliGatorAliGator Membre, Modérateur

    Alors non, pas tout à  fait. L'architecture arm64 doit être présente dans les nouvelles soumissions. C'est à  dire que si une application 32 bits est déjà  sur le store, il n'y a pas d'obligation à  la mettre à  jour.

    Pour compléter et être plus précis :
    • Les nouvelles applications soumises sur le store doivent obligatoirement avoir le 64 bits depuis février 2015
    • Pour les applications qui sont déjà  présentes sur le store et que tu souhaites mettre à  jour, tu auras jusqu'à  juillet (de mémoire) 2015 avant d'être obligé d'y inclure le 64 bits. Autrement dit, si tu mets à  jour avant, ça passe encore si tu n'as pas le 64 bits, mais si tu veux faire une mise à  jour après juillet 2015 là  par contre tu seras obligé que ta mise à  jour intègre le 64 bits.
    • Si tu ne souhaites pas mettre à  jour ton application car tu n'en n'as pas la nécessité, tu n'as pas le besoin de recompiler juste pour ajouter le 64 bits, dans le sens où ton application qui est déjà  sur le store ne va pas être retirée du store sous prétexte qu'elle est encore 32 bits. L'obligation est uniquement au moment où tu soumets une app, pas pour les apps déjà  soumises pour lesquelles tu ne referas pas de soumission.
    Autrement dit si tu as besoin de faire une mise à  jour que tu soumettras avant juillet ça passera si tu n'as pas le 64 bits, mais après c'est quand même mieux de le rajouter quand même, car il faudra sûrement que tu le rajoutes un jour si tu comptes faire des mises à  jour régulières de ton application et que tu continueras d'en faire après juillet 2015. Va bien falloir y passer, et autant le faire au plus tôt que d'attendre le dernier moment.
    Genre on a des clients qui sont parfois en rush pour sortir une mise à  jour de leur app en un temps restreint, parce que la mise à  jour doit être prête pour un évènement à  une date clé donc vite vite vite faut absolument ajouter le truc spécifique pour l'évènement et la sortir, c'est pas méchant comme modif ça devrait pas prendre trop de temps... ah oui mais là  cette mise à  jour vers 64 bits que tu as éternellement repoussée tu ne peux plus reculer devant maintenant et faut la faire, et ça tombe mal car tu es pressé...
    Bref même si ce ne sera, pour les mises à  jour d'apps existantes, obligatoire qu'en juillet, ça reste une bonne idée de le prévoir quand même dès maintenant.
  • MoKeSMoKeS Membre

    @muqaddar


     


    En fait le plan de test est le suivant : 


     


    Cas 1 : 


    1. Installation de la version de l'app store
    2. Premier lancement avec initialisation de la base 
    3. Installation (via Xcode de la nouvelle version) 
    4. Lancement de l'application -> BOUM

    Cas 2 : 


    1. Installation de la nouvelle version de l'application (via Xcode)
    2. Premier lancement, la base s'initialise sans problème

     


    Pour tes autres questions, je ne peux que supputer parce que je ne sais pas quelles ont été les modifications effectuées sur l'application entre Avril 2014 (date de publication sur l'app store) et aujourd'hui.


     


    Ce que j'ai fait au final : 


     


    Lorsque je lance l'application, je teste si la base est accessible ou non erronée. Si ce n'est pas le cas, je supprime le fichier de base de donnée et l'application continue comme une première utilisation. Dans mon cas ça ne pose pas de problème, étant donné que les données sont synchronisées sur nos serveurs (à  la manière d'un cloud) et on peut se permettre de resynchroniser la base "fraichement".


    De cette façon cela est quasi invisible pour l'utilisateur. 


    On est d'accord que dans le cas où l'utilisateur perd toutes ses données, cette solution n'est pas viable pour un sou, mais je n'ai plus vraiment le choix et le temps me manque.


     


    @AliGator


    Merci pour toutes ces précisions !! Je te rejoins la dessus également parce que du coup j'vais faire la mise à  jour avec le support 64bits (comme ça c'est fait) et je suis exactement dans ce cas là  ! Le client voulait une mise à  jour rapide, j'ai perdu 1 jour à  faire la conversion 64 bits (à  cause notamment d'Itunes Connect qui ne me disait pas pourquoi il refusait mon binaire, mais ça c'est une autre histoire)


    Merci encore pour les infos ! 

  • muqaddarmuqaddar Administrateur

    On dirait donc que l'application nouvelle n'arrive pas à  lire l'ancienne base installée.


    Et si tu vires le 64 bits, ça marche ?


     


    --


     


    C'est étrange car j'ai fait de même: j'ai une 32 bits que je mets à  jour avec une 64 bits, et je peux lire les anciens fichiers sans problème. Mais j'utilise SQLCipher...


  • ça a l'air indépendant du 64 ou du 32, parce qu'en ayant rebasculé sur du 32 c'est toujours le même résultat ! 


     


    Je ne sais pas ce qui a changé entre avril et aujourd'hui :-/


  • AliGatorAliGator Membre, Modérateur
    C'est ça d'utiliser sqlite directement au lieu d'utiliser des outils comme CoreData aussi, qui entre autres gère la migration de base de données si le schéma a changé.


    Toi si ton schéma de ta base SQL a changé entre avril et aujourd'hui mais que celui qui a fait le changement n'a pas pris la peine de s'assurer de la retro-compatibilité avec l'ancien schéma tu peux aller le fouetter.


    Bon après tu devrais pouvoir retrouver ce qui a changé entre avril et aujourd'hui avec ton historique GIT. (Tu pourrais meme utiliser "git bisect" pour trouver plus rapidement par dicothomie depuis quel commit ce problème de non-rétro-compatibilité a été introduit)
  • muqaddarmuqaddar Administrateur

    Je ne pense pas que ce soit un problème de schéma.


    Regarde aussi de quand date la dernière modification du fichier, et regarde le comportement avec les anciennes versions.




  • Tu pourrais meme utiliser "git bisect" pour trouver plus rapidement par dicothomie depuis quel commit ce problème de non-rétro-compatibilité a été introduit




     


    ... c'est pas faux.

  • muqaddarmuqaddar Administrateur

    Est-ce que tu as essayé de regénérer le fichier sqlite ?


     


    J'ai eu un problème très con une fois.


    De mémoire: je regénère mon fichier encrypté avec un projet Xcode à  part (Mac app), tout en laissant le projet Xcode principal de l'application ouvert. Les 2 utilisaient la même base SQLite non chiffrée avant chiffrement.


     


    Si le projet principal était ouvert, le chiffrement se faisait, mais "mal". J'avais la même erreur que toi.


    Il fallait que je ferme le projet Xcode principal pour le chiffrer avec l'autre.


  • AliGatorAliGator Membre, Modérateur

    ... c'est pas faux.

    Qu'est ce que tu n'as pas compris ? Dichotomie ou git bisect ?
  • FKDEVFKDEV Membre
    mars 2015 modifié #16


    Non SQLCypher n'est pas utilisé. De ce que je vois dans le code, ils ont fait une espèce d'encryption "maison" avec sqlite3 directement et un keychain.

    // Set the database keysqlite3_exec( database, [[SwingString stringWithFormat:@PRAGMA key = '%@'", databaseEncryptionKey] cStringUsingEncoding:NSASCIIStringEncoding], NULL, NULL, NULL );

    Es tu certain d'avoir la bonne clé ? Il y a peut-être une clé en debug et une autre en release.

    Pour en avoir le coeur net, si tu n'es pas certain d'avoir les sources, il va falloir chercher la clé dans le binaire de l'ancienne version.


    Mais, en fait, si la clé est dans le keychain d'iOS, tu dois pouvoir la récupérer avec la nouvelle version.
  • muqaddarmuqaddar Administrateur


    Mais, en fait, si la clé est dans le keychain d'iOS, tu dois pouvoir la récupérer avec la nouvelle version.




     


    Je vois bien l'intérêt de la mettre dans le keychain d'iOS, sauf que pour la mettre, il faut bien qu'elle soit copiée depuis un autre fichier au premier lancement de l'app par exemple ? Je vois donc mal où on gagne en sécurité.


     


    Quelle est la bonne pratique ? Dans quel fichier mettre cette clé ?



  • Je vois bien l'intérêt de la mettre dans le keychain d'iOS, sauf que pour la mettre, il faut bien qu'elle soit copiée depuis un autre fichier au premier lancement de l'app par exemple ? Je vois donc mal où on gagne en sécurité.


     


    Quelle est la bonne pratique ? Dans quel fichier mettre cette clé ?




     


    Je ne suis pas un spécialiste de ces questions là , mais effectivement si la clé est stockée dans le binaire de l'app, ce n'est pas idéal.


    Je ne crois pas qu'il puisse y avoir de bonne méthode à  partir du moment où la base chiffrée est fournie avec l'app, et donc toujours chiffrée avec la même clé.


    Eventuellement, en téléchargeant la clé au premier lancement (via un protocol sécurisé) puis en la stockant directement dans le keychain, mais du coup cela oblige à  avoir une connexion réseau au moment du premier lancement.

  • zoczoc Membre
    mars 2015 modifié #19
    Moi j'imaginerais bien une clé générée aléatoirement au premier démarrage de l'app puis stockée dans le keychain pour les accès ultérieurs.

    C'est évidemment possible uniquement si l'application démarre avec une base entièrement vide.
  • muqaddarmuqaddar Administrateur


    C'est évidemment possible uniquement si l'application démarre avec une base entièrement vide.




     


    Voilà  !


    Moi j'ai 15 tables pleines au démarrage. :)

  • muqaddarmuqaddar Administrateur


    Eventuellement, en téléchargeant la clé au premier lancement (via un protocol sécurisé) puis en la stockant directement dans le keychain, mais du coup cela oblige à  avoir une connexion réseau au moment du premier lancement.




     


    Exactement. Donc pas idéal non plus.



  • Exactement. Donc pas idéal non plus.




     


    En fait il faudrait pouvoir intervenir au niveau de l'app store pour avoir un téléchargement personnalisé avec une clé généré pour l'occasion et préchargée dans le keychain.


    Un système un peu similaire à  ce qu'il faut mettre en place pour décoder les receipts mais avec les données de l'application.

  • AliGatorAliGator Membre, Modérateur

    En fait il faudrait pouvoir intervenir au niveau de l'app store pour avoir un téléchargement personnalisé avec une clé généré pour l'occasion et préchargée dans le keychain.
    Un système un peu similaire à  ce qu'il faut mettre en place pour décoder les receipts mais avec les données de l'application.

    Ca ça ne servirait à  rien. Autant la générer aléatoirement au premier lancement et la stocker dans le keychain la toute première fois (comme font des libs de type BPXLUUIDHandler pour avoir un UUID unique généré), ça ne t'avance à  rien que ce soit l'AppStore qui te la génère.
    Ca ne servirait à  quelquechose que si en plus de ça, l'AppStore chiffrait aussi les données de ta base dynamiquement avec cette clé avant de te la télécharger. Ce qui violerait la signature de l'application en modifiant le contenu du bundle.


  • Ca ça ne servirait à  rien. (...)

    Ca ne servirait à  quelquechose que si en plus de ça, l'AppStore chiffrait aussi les données de ta base dynamiquement avec cette clé avant de te la télécharger. Ce qui violerait la signature de l'application en modifiant le contenu du bundle.




     


    Oui c'est ce que je voulais dire. Il faudrait que les données soit chiffrés par l'app store, ce qui parait très compliqué à  mettre en place.


     


    Par contre, si on revient à  une seule clé de chiffrage pour tous les utilisateurs, on pourrait avoir un système où Apple nous permettrait de placer cette clé dans le keychain de l'app dès l'installation.


    Le développeur fournirait un plist contenant les clés à  inclure dans le keychain au moment de l'installation.


    Cela pourrait aussi être utile pour toutes les apps qui utilisent des API HTTPS qui ont besoin d'un APP TOKEN.

  • AliGatorAliGator Membre, Modérateur
    Moi pour ça j'utilise cocoapods-keys. Ou alors un appel à  une API sécurisée pour récupérer la config.

    Mais bon comme tout problème de sécurité de ce genre, quand on pense avoir bouché un trou dans la chaà®ne de traitement, souvent on l'a fait en introduisant une autre faille autre part, et on n'a fait que repousser le problème... il n'y a pas de solution miracle.
Connectez-vous ou Inscrivez-vous pour répondre.