Génération de l'UUID v5. Qu'est-ce que le nom et l'espace de noms?

125

J'ai lu la manpage, mais je ne sais pas ce que undestand nameet namespacesont pour.

Pour les UUID de la version 3 et de la version 5, l'espace de nom et le nom des arguments de ligne de commande supplémentaires doivent être indiqués. L'espace de noms est soit un UUID dans une représentation sous forme de chaîne, soit un identificateur pour les UUID d'espace de noms prédéfinis en interne (actuellement connus sont «ns: DNS», «ns: URL», «ns: OID» et «ns: X500»). Le nom est une chaîne de longueur arbitraire.

L'espace de noms:

L'espace de noms est soit un UUID en représentation sous forme de chaîne, soit un

Cela signifie-t-il que je dois le stocker (UUID v4) quelque part en relation avec l'UUID v5 généré? Dans les deux cas, pourquoi n'est-ce pas fait automatiquement?

Le nom est une chaîne de longueur arbitraire.

nameune chaîne complètement aléatoire? Quel en est le but alors? Peut-il être décodé à partir de l'UUID v5?

Gajus
la source

Réponses:

106

Le nom et l'espace de noms peuvent être utilisés pour créer une hiérarchie d'UUID uniques (très probablement).

En gros, un UUID de type 3 ou de type 5 est généré en hachant ensemble un identifiant d'espace de noms avec un nom. Les UUID de type 3 utilisent MD5 et les UUID de type 5 utilisent SHA1. Seuls 128 bits sont disponibles et 5 bits sont utilisés pour spécifier le type, donc tous les bits de hachage ne sont pas inclus dans l'UUID. (MD5 est également considéré comme cryptographiquement cassé, et SHA1 est sur ses dernières jambes, alors ne l'utilisez pas pour vérifier les données qui doivent être "très sécurisées"). Cela dit, cela vous donne un moyen de créer une fonction de "hachage" répétable / vérifiable mappant un nom éventuellement hiérarchique sur une valeur probabiliste unique de 128 bits, agissant potentiellement comme un hachage hiérarchique ou MAC.

Supposons que vous ayez un magasin (clé, valeur), mais qu'il ne prend en charge qu'un seul espace de noms. Vous pouvez générer un grand nombre d'espaces de noms logiques distincts à l'aide des UUID de type 3 ou 5. Commencez par créer un UUID racine pour chaque espace de noms. Cela peut être un UUID de type 1 (hôte + horodatage) ou de type 4 (aléatoire) tant que vous le rangez quelque part. Vous pouvez également créer un UUID aléatoire pour votre racine (ou utiliser l' nullUUID: en 00000000-0000-0000-0000-000000000000tant que racine), puis créer un UUID reproductible pour chaque espace de noms en utilisant " uuid -v5 $ROOTUUID $NAMESPACENAME". Vous pouvez maintenant créer des UUID uniques pour les clés dans un espace de noms en utilisant "uuid -v5 $NAMESPACEUUID $KEY". Ces UUID peuvent être jetés dans un seul magasin clé-valeur avec une forte probabilité d'éviter les collisions. Ce processus peut être répété de manière récursive, de sorte que si, par exemple, la" valeur "associée à une clé UUID représente à son tour une sorte d'espace de nom logique" "comme un bucket, un conteneur ou un répertoire, son UUID peut être utilisé à son tour pour générer des UUID plus hiérarchiques.

L'UUID de type 3 ou de type 5 généré contient un hachage (partiel) de l'ID de l'espace de noms et du nom dans l'espace de noms (clé). Il ne contient pas plus l'UUID de l'espace de noms qu'un message MAC ne contient le contenu du message à partir duquel il est encodé. Le nom est une chaîne "arbitraire" (octet) du point de vue de l'algorithme uuid. Sa signification dépend cependant de votre application. Il peut s'agir d'un nom de fichier dans un répertoire logique, d'un identifiant d'objet dans un magasin d'objets, etc.

Bien que cela fonctionne bien pour un nombre modérément grand d'espaces de noms et de clés, cela finit par s'essouffler si vous visez un très grand nombre de clés uniques avec une probabilité très élevée. L'entrée Wikipedia pour le problème d'anniversaire (aka Birthday Paradox) comprend un tableau qui donne les probabilités d'au moins une collision pour différents nombres de clés et tailles de table. Pour 128 bits, le hachage de 26 milliards de clés de cette manière a une probabilité de collision de p=10^-18(négligeable), mais 26 trillions de clés, augmente la probabilité d'au moins une collision à p=10^-12(un sur un trillion), et le hachage de 26*10^15clés, augmente la probabilité de au moins une collision avecp=10^-6(un sur un million). En ajustant pour 5 bits qui encodent le type UUID, il s'épuisera un peu plus vite, donc un billion de clés ont environ 1 chance sur un billion d'avoir une seule collision.

Voir http://en.wikipedia.org/wiki/Birthday_problem#Probability_table pour la table de probabilité.

Voir http://www.ietf.org/rfc/rfc4122.txt pour plus de détails sur les encodages UUID.

Jeff Anderson-Lee
la source
2
À un certain niveau dans la hiérarchie, puis-je utiliser un UUIDv5 comme espace de noms et UUIDv4 comme clé aléatoire pour m'assurer que les collisions dans les données elles-mêmes (qui sont identifiées par ce GUID) n'augmentent pas les chances de collision d'UUID? Y a-t-il des problèmes de performances que je devrais connaître?
ermik
Je suis nouveau dans le concept et j'étais perplexe quant à la hiérarchie dont vous parlez. Où puis - je voir etc ... Un peu de clarté est venu une fois que je coincé sur l' explication que cela pourrait être utilisé pour créer un UUID reproductible pour l' espace de noms . Je me demande s'il existe un moyen de vérifier qu'un UUID donné (de type 3 ou 5) a été généré en utilisant un espace de noms particulier (son UUID)?
msciwoj
213

Les UUID de type 3 et de type 5 ne sont qu'une technique de remplissage d'un hachage dans un UUID.

  • Type 1: remplit l'adresse MAC + datetime en 128 bits
  • Type 3 : bourre un hachage MD5 en 128 bits
  • Type 4: remplit les données aléatoires en 128 bits
  • Type 5 : remplit un hachage SHA1 en 128 bits
  • Type 6: idée non officielle pour les UUID séquentiels

Un hachage SHA1 produit 160 bits (20 octets); le résultat du hachage est converti en UUID.

Avec le hachage de 20 octets de SHA1:

SHA1 Digest:   74738ff5 5367 e958 9aee 98fffdcd1876 94028007
UUID (v5):     74738ff5-5367-5958-9aee-98fffdcd1876
                             ^_low nibble is set to 5, to indicate type 5
                                  ^_first two bits set to 1 and 0, respectively

(Notez que les deux premiers bits de «9» sont déjà 1 et 0, respectivement, donc cela n'a aucun effet).

Que dois-je hacher?

Vous vous demandez probablement ce que je suis censé hacher. En gros, vous hachez la concaténation de:

sha1([NamespaceUUID]+[AnyString]);

Vous préfixez votre chaîne avec un soi-disant espace de noms pour éviter les conflits de noms.

La RFC UUID prédéfinit quatre espaces de noms pour vous:

  • NameSpace_DNS: {6ba7b810-9dad-11d1-80b4-00c04fd430c8}
  • NameSpace_URL: {6ba7b811-9dad-11d1-80b4-00c04fd430c8}
  • NameSpace_OID: {6ba7b812-9dad-11d1-80b4-00c04fd430c8}
  • NameSpace_X500: {6ba7b814-9dad-11d1-80b4-00c04fd430c8}

Donc, vous pouvez hacher ensemble:

StackOverflowDnsUUID = sha1(Namespace_DNS + "stackoverflow.com");
StackOverflowUrlUUID = sha1(Namespace_URL + "stackoverflow.com");

La RFC définit ensuite comment:

  • prenez les 160 bits de SHA1
  • et le convertir en 128 bits d'un UUID

L'essentiel de base est de ne prendre que les 128 premiers bits, de placer un 5dans l' enregistrement de type , puis de définir les deux premiers bits de la clock_seq_hi_and_reservedsection sur 1 et 0, respectivement.

Plus d'exemples

Maintenant que vous avez une fonction qui génère un soi-disant Nom , vous pouvez avoir la fonction (en pseudo-code):

UUID NameToUUID(UUID NamespaceUUID, String Name)
{
    byte[] hash = sha1(NamespaceUUID.ToBytes() + Name.ToBytes());
    UUID result;
    Copy(hash, result, 16);
    result[6] &= 0x0F; 
    result[6] |= 0x50;
    result[8] &= 0x3F; 
    result[8] |= 0x80;
    return result;
}

(Notez que l'endian-ness de votre système peut affecter les indices des octets ci-dessus)

Vous pouvez avoir des appels:

uuid = NameToUUID(Namespace_DNS, 'www.stackoverflow.com');
uuid = NameToUUID(Namespace_DNS, 'www.google.com');
uuid = NameToUUID(Namespace_URL, 'http://www.stackoverflow.com');
uuid = NameToUUID(Namespace_URL, 'http://www.google.com/search&q=rfc+4112');
uuid = NameToUUID(Namespace_URL, 'http://stackoverflow.com/questions/5515880/test-vectors-for-uuid-version-5-converting-hash-into-guid-algorithm');

Revenons maintenant à votre question

Pour les UUID de la version 3 et de la version 5, l'espace de nom et le nom des arguments de ligne de commande supplémentaires doivent être indiqués. L'espace de noms est soit un UUID dans une représentation sous forme de chaîne, soit un identificateur pour les UUID d'espace de noms prédéfinis en interne (actuellement connus sont «ns: DNS», «ns: URL», «ns: OID» et «ns: X500»). Le nom est une chaîne de longueur arbitraire.

L' espace de noms correspond à l'UUID que vous souhaitez. Cela peut être l'un des prédéfinis, ou vous pouvez créer le vôtre, par exemple:

UUID Namespace_RectalForeignExtractedObject = '8e884ace-bee4-11e4-8dfc-aa07a5b093db'

Le nom est une chaîne de longueur arbitraire.

Le nom est juste le texte que vous souhaitez ajouter à l'espace de noms, puis haché et inséré dans un UUID:

uuid = NameToUUID('8e884ace-bee4-11e4-8dfc-aa07a5b093db', 'screwdriver');
uuid = NameToUUID('8e884ace-bee4-11e4-8dfc-aa07a5b093db', 'toothbrush');
uuid = NameToUUID('8e884ace-bee4-11e4-8dfc-aa07a5b093db', 'broomstick');
uuid = NameToUUID('8e884ace-bee4-11e4-8dfc-aa07a5b093db', 'orange');
uuid = NameToUUID('8e884ace-bee4-11e4-8dfc-aa07a5b093db', 'axe handle');
uuid = NameToUUID('8e884ace-bee4-11e4-8dfc-aa07a5b093db', 'impulse body spray');
uuid = NameToUUID('8e884ace-bee4-11e4-8dfc-aa07a5b093db', 'iPod Touch');

Remarque : tout code publié dans le domaine public. Aucune attribution requise.

Ian Boyd
la source
45
Merci pour l'explication approfondie. Si je pouvais donner des points bonus, Namespace_RectalForeignExtractedObjectje le ferais.
boodle
Est-il possible de décoder le nom ou l'espace de noms décodé à partir de l'UUID?
Sathesh
4
@Sathesh Non, il n'est pas possible de décoder un hachage; les hachages sont des fonctions à sens unique. Par exemple, toute la collection de disques Blu-Ray Star Trek TNG fait 81 Go et possède un hachage de C5740BBBF2429115276D4AB60A020ED3ADE01192 . Il n'y a aucun moyen de décoder ce hachage de 20 octets en 81 Go. Si vous en aviez vraiment besoin, vous pouvez essayer de hacher tous les GUID et chaînes possibles jusqu'à ce que vous trouviez la combinaison qui donne le même résultat. Avec n'importe quel moment, vous le trouverez quelque part entre l'éternité et l'éternité.
Ian Boyd
22

Un nom n'est rien de plus qu'un identifiant unique dans un espace de noms. Le problème est que les espaces de noms sont souvent assez petits et que les noms dans l'un entrent souvent en conflit avec les noms d'autres. Par exemple, le numéro de plaque d'immatriculation (nom) de ma voiture est unique dans l'espace de noms de mon état DMV, mais il n'est probablement pas unique au monde; d'autres DMV d'état peuvent avoir utilisé le même nom dans leurs propres espaces de noms. Heck, quelqu'un d'autre peut avoir un numéro de téléphone (nom) qui correspond également parce que c'est encore un autre espace de noms, etc.

Les UUID peuvent être considérés comme habitant un seul espace de noms si vaste qu'il peut fournir un nom unique pour tout ; c'est ce que signifie «l'universel». Mais comment mappez-vous les noms existants dans d'autres espaces de noms à un UUID?

Une solution évidente consiste à générer un UUID (V1 ou V4) pour chaque élément afin de remplacer les anciens noms dans leurs espaces de noms disjoints. L'inconvénient est qu'ils sont beaucoup plus gros, vous devez communiquer tous les nouveaux noms à tous ceux qui ont une copie de votre ensemble de données, mettre à jour toutes vos API, etc. Il y a de fortes chances que vous ne puissiez pas vous débarrasser complètement des anciens noms Quoi qu'il en soit, ce qui signifie que maintenant chaque élément a deux noms, alors avez-vous amélioré ou pire les choses?

C'est là que V3 / V5 entre en jeu. Les UUID semblent tout aussi aléatoires que V4 mais sont en fait déterministes; toute personne ayant le bon UUID pour un espace de noms peut ensuite générer indépendamment le même UUID pour tout nom donné dans cet espace de noms. Vous n'avez pas du tout besoin de les publier ni même de les pré-générer puisque n'importe qui peut les créer à la volée au besoin!

Les noms DNS et les URL sont des espaces de noms très couramment utilisés, donc des UUID standard ont été publiés pour ceux-ci; Les OID ASN.1 et les noms X.500 ne sont pas aussi courants, mais les organismes de normalisation les adorent, ils ont donc publié des UUID d'espaces de noms standard pour eux aussi.

Pour tous les autres espaces de noms, vous devez générer votre propre UUID d'espace de noms (V1 ou V4) et le communiquer à toute personne qui en a besoin. Si vous avez plusieurs espaces de noms, il n'est clairement pas idéal de publier l'UUID pour chacun d'eux.

C'est là que la hiérarchie entre en jeu: vous créez un UUID "de base" (de n'importe quel type), puis vous l'utilisez comme espace de noms pour nommer vos autres espaces de noms! De cette façon, vous n'avez qu'à publier l'UUID de base (ou en utiliser un évident), et tout le monde peut calculer le reste.

Par exemple, restons nous voulions créer des UUID pour StackOverflow; qui a un nom évident dans l'espace de noms DNS, donc la base est évidente:

uuid ns_dns = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
uuid ns_base = uuidv5(ns_dns, 'stackoverflow.com');

StackOverflow lui-même a des espaces de noms séparés pour les utilisateurs, les questions, les réponses, les commentaires, etc., mais ceux-ci sont également assez évidents:

uuid ns_user = uuidv5(ns_base, 'user');
uuid ns_question = uuidv5(ns_base, 'question');
uuid ns_answer = uuidv5(ns_base, 'answer');
uuid ns_comment = uuidv5(ns_base, 'comment');

Cette question particulière est # 10867405, donc son UUID serait:

uuid here = uuidv5(ns_question, '10867405');

Notez qu'il n'y a rien d' aléatoire dans ce processus, donc quiconque suit la même logique obtiendra la même réponse, mais l'espace de noms UUID est si vaste qu'il (effectivement, étant donné la sécurité d'un hachage cryptographique de 122 bits) ne heurtera jamais un UUID généré à partir de toute autre paire d'espace de nom / nom.

StephenS
la source
Je me demande pourquoi stackoverflow doit mapper un grand entier généré de manière unique à un UUID étant donné que ses API ne renvoient apparemment que le grand entier sous forme de chaîne. Où l'UUID serait-il utilisé sinon dans l'API? Il semble que nous devrions soit sélectionner un UUID ou BIGINT? Pourquoi faire cette stratégie hybride. Pourtant +1 pour l'explication claire dans votre réponse.
nishant
4
Les UUID V3 / V5 ont été conçus lorsque vous devez convertir de manière déterministe des espaces de noms existants (et probablement en collision) en un espace de noms UUID, ce qui est souvent utile lors de la fusion d'ensembles de données. Si cela ne s'applique pas à ce que vous faites, optez pour la V1 / V4.
StephenS