12
votes

Fonction de cartographie entier d'une-à-une

Nous utilisons MySQL et nous développons une application où nous aimerions que la séquence d'identification ne soit pas rendue publique ... Les ID sont à peine supérieure secrète et il n'y a pas de problème significatif si quelqu'un a effectivement pu les décoder.

Donc, un hachage est bien sûr la solution évidente, nous utilisons actuellement des entiers MD5 ... 32 bits entrez et nous coupons le MD5 à 64bits, puis le stocker. Cependant, nous n'avons aucune idée de la manière dont les collisions probables sont lorsque vous découpez-la comme celle-ci (surtout que tous les chiffres proviennent de l'autocrampe ou de l'heure actuelle). Nous vérifions actuellement les collisions, mais car nous pouvons insérer 100 000 lignes à une fois, la performance est terrible (ne peut pas insertion en vrac).

Mais à la fin, nous n'avons vraiment pas besoin de la sécurité offerte par les hachages et ils consomment des espaces inutiles et nécessitent également un index supplémentaire ... Donc, y a-t-il une fonction / algorithme assez bonne et suffisante qui garantit Mappage individuel pour n'importe quel nombre sans motifs visuels évidents pour des nombres séquentiels?

Edit: J'utilise PHP qui ne prend pas en charge l'arithmétique entier par défaut, mais après avoir regardé autour, j'ai constaté que cela pouvait être répliqué à moindre coût avec des opérateurs bitwises. Code de la multiplication en integer 32bit peut être trouvé ici: http://pastebin.com/np28xhqf


4 commentaires

Il existe infiniment de nombreuses fonctions qui garantissent une cartographie 1: 1.


@Wooble, bien alors vous devriez être capable de répondre à la question assez facilement, je suppose ;-)


La partie délicate est que, maintenant, lorsque vous avez posé la question ici, quelles que soient les réponses que nous fournissons des besoins pour être résister aussi modulo les réponses que nous donnons ici ;-)


@WOOBLE Nous parlons d'entiers 32 bits, il y a donc seulement (2 ^ 32)! 1: 1 mappages.


5 Réponses :


9
votes

Vous pourriez simplement xor avec 0xDeadbeef, si c'est assez bon.

Alternativement multiplier par un nombre impair mod 2 ^ 32. Pour la cartographie inverse, il suffit de multiplier par le inverse multiplicative

Exemple: n = 2345678901; Inverse multiplicative (mod 2 ^ 32): 2313902621 Pour la cartographie, multipliez par 2345678901 (MOD 2 ^ 32):

1 -> 2345678901 2 -> 396390506

pour la cartographie inverse, multipliez par 2313902621.


15 commentaires

Ces deux approches ne donnent-elles que ces deux approches donnent un modèle évident juste en regardant f (0) , f (1) , f (2) f (2) ?


@aioOBE vrai, mais je comprends la sécurité n'est pas une préoccupation ici. Seul l'OP peut décider, que ce soit «assez bon».


@aioobe: Oui, mais comme l'a dit OP, il n'est pas nécessaire d'être imbattable.


Et la deuxième approche ne donne pas de modèle évident si le nombre est assez grand.


XOR donne des schémas plutôt prévisibles, je crois, bien que cela puisse aider à "randomiser", je suppose. Quoi qu'il en soit, en ce qui concerne la multiplcation par un nombre impair ... cela devrait fonctionner à un très grand nombre, je suppose que ... mais cela impliquerait également de travailler avec des nombres vraiment gros (et rend la reprise un peu plus coûteuse? Ou?). Bien que je pense, une solution simple pourrait peut-être être de diviser la valeur dans 4x 1byte ... puis comporter 4 tableaux distincts qui viennent de brouiller les valeurs séparément.


@Andreas choisissez simplement un entier 32 bits et calculez son inverse une fois. Au moment de l'exécution, vous devez seulement faire une multiplication 32 bits.


@Henrik Ah bien sûr, totalement oublié ça ... Bien que je ne sois pas exactement sûr de savoir comment on calcule l'inverse, à moins que je vous ai mal compris (de sorte que l'on puisse également récupérer l'entier d'origine).


@Henrik ah, merci beaucoup et cela ferait probablement parfaitement ... bien que j'avais totalement oublié (et donc malheureusement non mentionné) que PHP ne supporte pas les calculs entier, il revient toujours à les flotte. Donc, alors que votre méthode fonctionne absolument, la précision le brise pour PHP.


@Nicholas Wilson, je suppose que même les requêtes mathématiques sont envoyées au serveur ... Donc, alors que vous avez raison dans le cas général, faire 100 000+ requêtes de base de base ne sonnent pas si agréable (même si on pouvait les gêner tous ensemble).


Vous pourriez. Lorsque vous insérez, réglez la colonne HASH sur MOD (ID * Num). Cela fera le calcul pour vous à l'heure d'insertion.


Ah très vrai Nicholas, bien qu'il soit tout à fait plus lourd de travailler avec. De toute façon! J'ai travaillé autour du problème dans PHP et mis en œuvre la multiplication entière avec les opérateurs bitwises et j'ai maintenant une implémentation raisonnablement rapide qui fonctionne assez bien pour mes besoins (le code source est dans le message principal). Je fais aussi un Xor simple pour une bonne mesure.


Afaik, cela ne donnera pas nécessairement une bijection car 32 n'est pas premier.


@Alexei Je ne comprends pas ton point. Bien sûr 32 n'est pas premier, alors quoi? Tout nombre impair a un MOD inverse multiplicatif (2 ^ 32) qui donne une cartographie inverse.


@Henrik, désolé, j'ai raté cette partie étrange :)


Qu'est-ce qui est si spécial avec 0xDeadbeef?



1
votes

Vous pouvez utiliser le mode Mod pour un grand nombre de nombres premiers.

Votre numéro * Grand numéro principal 1 / Big Prime Numéro 2.

Le numéro principal 1 doit être plus grand que seconde. Les secondes doivent être proches de 2 ^ 32 mais moins que cela. Que ce sera difficile à remplacer.

Prime 1 et premier 2 devraient être des constantes.


4 commentaires

Y a-t-il un moyen de récupérer à un entier original?


Vous n'aurez jamais le même repos pour 1..big Prime numéro 2, car dans le cas opposé N2 - N1 doit être divisé par Prime 2, puis votre numéro 2 - votre numéro 1 doit également être divisé, mais cela est impossible dans le jeu 1. .big Prime numéro 2.


Vous pouvez garder les deux dans votre table


Je ne connais pas la fonction pour restaurer la valeur d'origine et je ne suis pas sûr que ce soit possible.



2
votes

Faites ce que Henrik a dit dans sa deuxième suggestion. Mais comme ces valeurs semblent être utilisées par les gens (sinon, vous ne voudriez pas les randiriser). Prendre une étape supplémentaire. Multipliez le nombre séquentiel par un grand nombre de primes et réduisez le mod N où n est une puissance de 2. mais choisissez N d'avoir 2 bits plus petits que vous ne pouvez stocker. Ensuite, multipliez le résultat de 11 et utilisez cela. Nous avons donc:

hash = ((((nombre * grand_prime)% 536870912) * 11

La multiplication par 11 protège contre la plupart des erreurs de saisie de données - si un chiffre est mal saisi, le résultat ne sera pas un multiple de 11. Si des chiffres sont transposés, le résultat ne sera pas un multiple de 11. Une vérification préliminaire de toute valeur saisie, vous vérifiez si elle est divisible par 11 avant même de regarder dans la base de données.


1 commentaires

C'est toutes des trucs backend, il n'ya donc aucune possibilité d'erreurs de saisie de données. Quoi qu'il en soit, PHP ne prend pas en charge les calculs entier, alors que la méthode est excellente, elle n'est tristement pas utilisable dans mon cas.



5
votes

Si vous souhaitez assurer un mappage 1: 1, utilisez un cryptage (c'est-à-dire une permutation), pas un hachage. Le cryptage doit être 1: 1 car il peut être déchiffré.

Si vous voulez des numéros 32 bits, utilisez hasté Pudding Cypher ou écrivez simplement un simple CYPHER Feistel Feistel simple. P>

Voici celui que j'ai préparé précédemment: P>

import java.util.Random;

/**
 * IntegerPerm is a reversible keyed permutation of the integers.
 * This class is not cryptographically secure as the F function
 * is too simple and there are not enough rounds.
 *
 * @author Martin Ross
 */
public final class IntegerPerm {
    //////////////////
    // Private Data //
    //////////////////

    /** Non-zero default key, from www.random.org */
    private final static int DEFAULT_KEY = 0x6CFB18E2;

    private final static int LOW_16_MASK = 0xFFFF;
    private final static int HALF_SHIFT = 16;
    private final static int NUM_ROUNDS = 4;

    /** Permutation key */
    private int mKey;

    /** Round key schedule */
    private int[] mRoundKeys = new int[NUM_ROUNDS];

    //////////////////
    // Constructors //
    //////////////////

    public IntegerPerm() { this(DEFAULT_KEY); }

    public IntegerPerm(int key) { setKey(key); }

    ////////////////////
    // Public Methods //
    ////////////////////

    /** Sets a new value for the key and key schedule. */
    public void setKey(int newKey) {
        assert (NUM_ROUNDS == 4) : "NUM_ROUNDS is not 4";
        mKey = newKey;

        mRoundKeys[0] = mKey & LOW_16_MASK;
        mRoundKeys[1] = ~(mKey & LOW_16_MASK);
        mRoundKeys[2] = mKey >>> HALF_SHIFT;
        mRoundKeys[3] = ~(mKey >>> HALF_SHIFT);
    } // end setKey()

    /** Returns the current value of the key. */
    public int getKey() { return mKey; }

    /**
     * Calculates the enciphered (i.e. permuted) value of the given integer
     * under the current key.
     *
     * @param plain the integer to encipher.
     *
     * @return the enciphered (permuted) value.
     */
    public int encipher(int plain) {
        // 1 Split into two halves.
        int rhs = plain & LOW_16_MASK;
        int lhs = plain >>> HALF_SHIFT;

        // 2 Do NUM_ROUNDS simple Feistel rounds.
        for (int i = 0; i < NUM_ROUNDS; ++i) {
            if (i > 0) {
                // Swap lhs <-> rhs
                final int temp = lhs;
                lhs = rhs;
                rhs = temp;
            } // end if
            // Apply Feistel round function F().
            rhs ^= F(lhs, i);
        } // end for

        // 3 Recombine the two halves and return.
        return (lhs << HALF_SHIFT) + (rhs & LOW_16_MASK);
    } // end encipher()

    /**
     * Calculates the deciphered (i.e. inverse permuted) value of the given
     * integer under the current key.
     *
     * @param cypher the integer to decipher.
     *
     * @return the deciphered (inverse permuted) value.
     */
    public int decipher(int cypher) {
        // 1 Split into two halves.
        int rhs = cypher & LOW_16_MASK;
        int lhs = cypher >>> HALF_SHIFT;

        // 2 Do NUM_ROUNDS simple Feistel rounds.
        for (int i = 0; i < NUM_ROUNDS; ++i) {
            if (i > 0) {
                // Swap lhs <-> rhs
                final int temp = lhs;
                lhs = rhs;
                rhs = temp;
            } // end if
            // Apply Feistel round function F().
            rhs ^= F(lhs, NUM_ROUNDS - 1 - i);
        } // end for

        // 4 Recombine the two halves and return.
        return (lhs << HALF_SHIFT) + (rhs & LOW_16_MASK);
    } // end decipher()

    /////////////////////
    // Private Methods //
    /////////////////////

    // The F function for the Feistel rounds.
    private int F(int num, int round) {
        // XOR with round key.
        num ^= mRoundKeys[round];
        // Square, then XOR the high and low parts.
        num *= num;
        return (num >>> HALF_SHIFT) ^ (num & LOW_16_MASK);
    } // end F()

} // end class IntegerPerm


1 commentaires

Très vrai, cependant, la solution de Henrik est suffisante pour mes besoins et aussi raisonnablement rapide dans PHP, donc c'est ce que je vais aller avec. Mais en effet, le cryptage aurait été la meilleure solution.



1
votes

Pour notre application, nous utilisons Bit Shuffle pour générer l'ID. Il est très facile de retourner à l'ID d'origine.

func (m Meeting) MeetingCode() uint {
    hashed := (m.ID + 10000000) & 0x00FFFFFF
    chunks := [24]uint{}
    for i := 0; i < 24; i++ {
        chunks[i] = hashed >> i & 0x1
    }
    shuffle := [24]uint{14, 1, 15, 21, 0, 6, 5, 10, 4, 3, 20, 22, 2, 23, 8, 13, 19, 9, 18, 12, 7, 11, 16, 17}
    result := uint(0)
    for i := 0; i < 24; i++ {
        result = result | (chunks[shuffle[i]] << i)
    }
    return result
}


0 commentaires