Menghasilkan UUID v5. Apa itu nama dan namespace?

125

Saya sudah membaca manhalamannya, tapi saya tidak mengerti apa namedan namespaceuntuk apa.

Untuk UUID versi 3 dan versi 5, namespace argumen baris perintah tambahan dan nama harus diberikan. Namespace adalah UUID dalam representasi string atau pengenal untuk UUID namespace yang telah ditentukan sebelumnya (saat ini dikenal adalah "ns: DNS", "ns: URL", "ns: OID", dan "ns: X500"). Nama adalah string dengan panjang sembarang.

Namespace:

Namespace adalah UUID dalam representasi string atau

Apakah ini berarti bahwa saya perlu menyimpannya (UUID v4) di suatu tempat terkait dengan UUID v5 yang dihasilkan? Dalam kedua kasus tersebut, mengapa ini tidak dilakukan secara otomatis?

Nama adalah string dengan panjang sembarang.

namestring yang benar-benar acak? Lalu apa tujuannya? Bisakah itu diterjemahkan dari UUID v5?

Gajus
sumber

Jawaban:

106

Nama dan namespace dapat digunakan untuk membuat hierarki (sangat mungkin) UUID unik.

Secara kasar, UUID tipe 3 atau tipe 5 dihasilkan dengan melakukan hashing bersama pengenal namespace dengan sebuah nama. UUID tipe 3 menggunakan MD5 dan UUID tipe 5 menggunakan SHA1. Hanya 128-bit yang tersedia dan 5 bit digunakan untuk menentukan jenisnya, jadi semua bit hash tidak membuatnya menjadi UUID. (Juga MD5 dianggap rusak secara kriptografis, dan SHA1 berada pada tahap terakhirnya, jadi jangan gunakan ini untuk memverifikasi data yang harus "sangat aman"). Yang mengatakan, ini memberi Anda cara untuk membuat fungsi "hash" yang dapat diulang / diverifikasi yang memetakan nama yang mungkin hierarkis ke nilai 128-bit yang mungkin unik, berpotensi bertindak seperti hash hierarkis atau MAC.

Misalkan Anda memiliki penyimpanan (key, value), tetapi hanya mendukung satu namespace. Anda dapat membuat banyak ruang nama logis berbeda menggunakan tipe 3 atau tipe 5 UUID. Pertama, buat UUID root untuk setiap namespace. Ini bisa berupa UUID tipe 1 (host + timestamp) atau tipe 4 (acak) selama Anda menyimpannya di suatu tempat. Cara lainnya, Anda dapat membuat satu UUID acak untuk root Anda (atau menggunakan nullUUID: 00000000-0000-0000-0000-000000000000sebagai root) dan kemudian membuat UUID yang dapat direproduksi untuk setiap namespace menggunakan " uuid -v5 $ROOTUUID $NAMESPACENAME". Sekarang Anda dapat membuat UUID unik untuk kunci dalam namespace menggunakan "uuid -v5 $NAMESPACEUUID $KEY". UUID ini dapat dilemparkan ke penyimpanan nilai kunci tunggal dengan probabilitas tinggi untuk menghindari tabrakan. Proses ini dapat diulangi secara rekursif sehingga jika misalnya," nilai "yang terkait dengan kunci UUID pada gilirannya mewakili semacam ruang nama logis" "seperti bucket, container, atau direktori, UUID-nya dapat digunakan secara bergantian untuk menghasilkan UUID yang lebih hierarkis.

UUID tipe 3 atau tipe 5 yang dihasilkan menyimpan hash (sebagian) dari id namespace dan nama-dalam-namespace (kunci). Itu tidak lebih memegang UUID namespace daripada pesan MAC menyimpan konten pesan yang dikodekannya. Namanya adalah string "arbitrary" (oktet) dari perspektif algoritme uuid. Namun artinya tergantung pada aplikasi Anda. Ini bisa berupa nama file dalam direktori logis, id objek dalam penyimpanan objek, dan sebagainya.

Meskipun ini bekerja dengan baik untuk sejumlah besar ruang nama dan kunci, pada akhirnya akan kehabisan tenaga jika Anda membidik sejumlah besar kunci yang unik dengan probabilitas sangat tinggi. Entri Wikipedia untuk Masalah Ulang Tahun (alias Paradoks Ulang Tahun) menyertakan tabel yang memberikan kemungkinan setidaknya satu tabrakan untuk berbagai nomor kunci dan ukuran tabel. Untuk 128-bit, hashing 26 miliar kunci dengan cara ini memiliki kemungkinan tabrakan p=10^-18(dapat diabaikan), tetapi 26 triliun kunci, meningkatkan kemungkinan setidaknya satu tabrakan menjadi p=10^-12(satu dalam satu triliun), dan hashing 26*10^15kunci, meningkatkan kemungkinan setidaknya satu tabrakan kep=10^-6(satu dari sejuta). Menyesuaikan 5 bit yang menyandikan jenis UUID, itu akan habis lebih cepat, sehingga satu triliun kunci memiliki kira-kira peluang 1 dalam satu triliun untuk mengalami satu tabrakan.

Lihat http://en.wikipedia.org/wiki/Birthday_problem#Probability_table untuk tabel probabilitas.

Lihat http://www.ietf.org/rfc/rfc4122.txt untuk detail lebih lanjut tentang pengkodean UUID.

Jeff Anderson-Lee
sumber
2
Pada tingkat tertentu di bawah hierarki, dapatkah saya menggunakan UUIDv5 sebagai namespace dan UUIDv4 sebagai kunci acak untuk memastikan tabrakan dalam data itu sendiri (yang diidentifikasi oleh GUID ini) tidak meningkatkan kemungkinan benturan UUID? Ada masalah kinerja yang harus saya ketahui?
ermik
Saya baru mengenal konsep tersebut dan bingung dengan hierarki apa yang Anda bicarakan itu. Di mana saya bisa melihatnya dll ... Beberapa kejelasan datang setelah saya terjebak pada penjelasan ini mungkin digunakan untuk membuat UUID yang dapat direproduksi untuk namespace . Saya bertanya-tanya apakah ada cara untuk memverifikasi bahwa UUID tertentu (tipe 3 atau 5) telah dibuat menggunakan namespace tertentu (UUID-nya)?
msciwoj
213

UUID Tipe 3 dan Tipe 5 hanyalah teknik memasukkan hash ke dalam UUID.

  • Tipe 1: isi alamat MAC + tanggal waktu menjadi 128 bit
  • Tipe 3 : memasukkan hash MD5 ke dalam 128 bit
  • Tipe 4: memasukkan data acak ke dalam 128 bit
  • Tipe 5 : isi hash SHA1 menjadi 128 bit
  • Jenis 6: ide tidak resmi untuk UUID berurutan

Sebuah hash SHA1 menghasilkan 160 bit (20 byte); hasil hash diubah menjadi UUID.

Dengan hash 20-byte dari 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

(Perhatikan bahwa dua bit pertama dari '9' masing-masing sudah menjadi 1 dan 0, jadi ini tidak berpengaruh).

Apa yang saya hash?

Anda mungkin bertanya-tanya apa yang harus saya hash. Pada dasarnya, Anda mencirikan rangkaian:

sha1([NamespaceUUID]+[AnyString]);

Anda memberi awalan string Anda dengan apa yang disebut namespace untuk mencegah konflik nama.

The UUID RFC pra-mendefinisikan empat ruang nama untuk Anda:

  • 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}

Jadi, Anda bisa melakukan hash bersama:

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

RFC kemudian menjelaskan cara:

  • ambil 160 bit dari SHA1
  • dan mengubahnya menjadi 128 bit UUID

Inti dasarnya adalah hanya mengambil 128 bit pertama, memasukkan a ke 5dalam tipe record, dan kemudian menetapkan dua bit pertama dari clock_seq_hi_and_reservedbagian tersebut masing-masing ke 1 dan 0.

Lebih banyak contoh

Sekarang Anda memiliki fungsi yang menghasilkan apa yang disebut Nama , Anda dapat memiliki fungsi tersebut (dalam 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;
}

(Perhatikan bahwa endian-ness sistem Anda dapat memengaruhi indeks dari byte di atas)

Anda dapat melakukan panggilan:

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');

Sekarang kembali ke pertanyaan Anda

Untuk UUID versi 3 dan versi 5, namespace argumen baris perintah tambahan dan nama harus diberikan. Namespace adalah UUID dalam representasi string atau pengenal untuk UUID namespace yang telah ditentukan sebelumnya (saat ini dikenal adalah "ns: DNS", "ns: URL", "ns: OID", dan "ns: X500"). Nama adalah string dengan panjang sembarang.

The namespace adalah apa pun UUID Anda seperti. Ini bisa menjadi salah satu yang ditentukan sebelumnya, atau Anda bisa membuatnya sendiri, misalnya:

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

Nama adalah string dengan panjang sembarang.

Namanya hanyalah teks yang ingin Anda tambahkan ke namespace, lalu di-hash, dan dimasukkan ke dalam 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');

Catatan : Kode apa pun yang dirilis ke domain publik. Tidak diperlukan atribusi.

Ian Boyd
sumber
45
Terima kasih atas penjelasan lengkapnya. Jika saya bisa memberikan poin bonus, Namespace_RectalForeignExtractedObjectsaya akan melakukannya.
boodle
Apakah mungkin untuk memecahkan kode nama atau namespace yang didekodekan dari UUID?
Sathesh
4
@Sathesh Tidak, tidak mungkin untuk memecahkan kode hash; hash adalah fungsi satu arah. Misalnya, seluruh koleksi Star Trek TNG Blu-Ray berukuran 81 GB, dan memiliki hash C5740BBBF2429115276D4AB60A020ED3ADE01192 . Tidak ada cara untuk memecahkan kode hash 20-byte itu kembali menjadi 81 GB. Jika Anda benar-benar perlu, Anda dapat mencoba mencirikan semua kemungkinan GUID dan string yang mungkin sampai Anda menemukan kombinasi yang memberikan hasil yang sama. Dengan luch apa pun, Anda akan menemukannya di suatu tempat antara selamanya dan keabadian.
Ian Boyd
22

Nama tidak lebih dari pengenal yang unik dalam beberapa namespace. Masalahnya adalah bahwa namespace seringkali cukup kecil dan nama-nama di satu sering bertabrakan dengan nama di tempat lain. Misalnya, nomor pelat (nama) mobil saya unik di dalam ruang nama DMV negara bagian saya, tetapi mungkin tidak unik di dunia; DMV negara bagian lain mungkin telah menggunakan nama yang sama di namespace mereka sendiri. Heck, orang lain mungkin memiliki nomor telepon (nama) yang juga cocok karena itu adalah namespace lain, dll.

UUID dapat dilihat sebagai menempati satu namespace yang sangat luas sehingga dapat memberikan nama yang unik untuk semuanya ; itulah yang dimaksud dengan "universal". Tapi bagaimana Anda memetakan nama yang ada di namespace lain ke UUID?

Salah satu solusi yang jelas adalah membuat UUID (V1 atau V4) untuk setiap item untuk menggantikan nama lama di ruang nama yang saling terpisah. Kelemahannya adalah mereka jauh lebih besar, Anda harus mengkomunikasikan semua nama baru kepada semua orang yang memiliki salinan dataset Anda, memperbarui semua API Anda, dll. Kemungkinannya Anda tidak bisa benar-benar menyingkirkan nama lama seluruhnya Lagi pula, yang berarti sekarang setiap item memiliki dua nama, jadi apakah Anda membuat semuanya lebih baik atau lebih buruk?

Di sinilah peran V3 / V5. UUID terlihat sama acaknya dengan V4 tetapi sebenarnya deterministik; siapa pun yang memiliki UUID yang tepat untuk namespace kemudian dapat secara mandiri menghasilkan UUID yang sama untuk nama tertentu dalam namespace tersebut. Anda tidak perlu mempublikasikannya sama sekali atau bahkan membuatnya terlebih dahulu karena siapa pun dapat membuatnya dengan cepat sesuai kebutuhan!

Nama DNS dan URL adalah ruang nama yang sangat umum digunakan, jadi UUID standar diterbitkan untuk itu; ASN.1 OID dan nama X.500 tidak umum, tetapi badan standar menyukainya, jadi mereka menerbitkan UUID namespace standar untuk mereka juga.

Untuk semua namespace lainnya, Anda harus membuat UUID namespace Anda sendiri (V1 atau V4) dan mengomunikasikannya kepada siapa pun yang membutuhkannya. Jika Anda memiliki beberapa ruang nama, harus menerbitkan UUID untuk masing-masing ruang nama jelas tidak ideal.

Di sinilah hierarki berperan: Anda membuat satu UUID "dasar" (jenis apa pun), lalu menggunakannya sebagai namespace untuk menamai namespace Anda yang lain! Dengan begitu, Anda hanya perlu mempublikasikan UUID dasar (atau menggunakan yang sudah jelas), dan semua orang dapat menghitung sisanya.

Sebagai contoh, mari tetap kita ingin membuat beberapa UUID untuk StackOverflow; yang memiliki nama yang jelas dalam namespace DNS, jadi dasarnya jelas:

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

StackOverflow sendiri memiliki ruang nama terpisah untuk pengguna, pertanyaan, jawaban, komentar, dll., Tetapi itu juga cukup jelas:

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');

Pertanyaan khusus ini adalah # 10867405, jadi UUID-nya adalah:

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

Perhatikan bahwa tidak ada yang acak dalam proses ini, jadi siapa pun yang mengikuti logika yang sama akan mendapatkan jawaban yang sama, namun namespace UUID sangat luas sehingga (secara efektif, mengingat keamanan hash kriptografi 122-bit) tidak pernah bertabrakan dengan sebuah UUID dihasilkan dari namespace / pasangan nama lainnya.

StephenS
sumber
Saya bertanya-tanya mengapa stackoverflow perlu memetakan bilangan bulat besar yang dihasilkan secara unik ke UUID mengingat bahwa API-nya tampaknya hanya mengembalikan bilangan bulat besar sebagai string. Di mana UUID akan digunakan jika tidak ada di API. Sepertinya kita harus memilih UUID atau BIGINT? Mengapa strategi hybrid ini. Namun +1 untuk penjelasan yang jelas dalam jawaban Anda.
nishant
4
UUID V3 / V5 dirancang untuk saat Anda perlu mengonversi namespace yang ada (dan kemungkinan bertabrakan) secara deterministik ke satu namespace UUID, yang sering kali berguna saat menggabungkan set data. Jika itu tidak berlaku untuk apa yang Anda lakukan, gunakan V1 / V4.
StephenS