Score:2

เหตุใดคีย์ส่วนตัว X25519 จึงไม่ตรงกันโดยส่วนใหญ่ขึ้นอยู่กับฟังก์ชันที่ใช้ แต่คีย์สาธารณะจะตรงกันเสมอ (seed เดียวกันสำหรับทั้งคู่)

ธง cn
mkl

ฉันพยายามที่จะคาดหัวของฉันไปรอบ ๆ จาก เมล็ดพันธุ์ ถึง ก เซ็นคีย์และยังได้รับ คีย์ส่วนตัว (คีย์เข้ารหัส). ฉันใช้ NaCl / libsodium

ฉันสร้างรหัสด้านล่างและผลลัพธ์ก็น่าสนใจ เปิดออก pk1.private_key และ pk2.private_key จับคู่ประมาณ 3% ของเวลา อย่างไรก็ตามคีย์สาธารณะตรงกัน 100% ทั้งหมดจะถูกสร้างขึ้นโดยขึ้นต้นด้วยค่าเดียวกัน เมล็ดพันธุ์. เกิดขึ้นที่นี่คืออะไร?

  • ฉันได้รับ pk1 โดยใช้ PrivateKey.from_seed(เมล็ดพันธุ์)

  • ฉันได้รับ pk2 โดยใช้ SigningKey(seed).to_curve25519_private_key()

ตัวอย่างของความไม่ตรงกัน (ใกล้เคียงแต่ไม่เท่ากัน):

# ไบต์ที่ 1 ไม่ตรงกันโดย 0x01
เมล็ดพันธุ์: f8d9e54a23971beebf2552c1a50ade6150cd051321398394f515e8d4b1ba0404
เอกชน1: c1fd4612ee8ef24d295210a277e196e6bb4a9ae6b93f98d93f197860fe5dc048
เอกชน2: c0fd4612ee8ef24d295210a277e196e6bb4a9ae6b93f98d93f197860fe5dc048

# ไบต์ที่ 1 ไม่ตรงกันโดย 0x03
เมล็ดพันธุ์: d612a66f92ee2f42ab1f7ea9a712a47c815843d21fc988b1d202459f235b6410
เอกชน1: f33d5e80bb556333e2961c9868b1dc7e548836ee56808689ca022f1a19fe86bb
priv2: f03d5e80bb556333e2961c9868b1dc7e548836ee56808689ca022f1a19fe867b

# ไบต์ที่ 1 ไม่ตรงกันโดย 0x01, ไบต์สุดท้ายไม่ตรงกันโดย 0x40
เมล็ดพันธุ์: 10b7e1c66cf08005a22289158a088e028160f892dc6c20d43025be4690aaed85
priv1: 194898f65d117579d50e80a9b7e07bd048bfd1300d55561dac9dfaed4ef02109
priv2: 184898f65d117579d50e80a9b7e07bd048bfd1300d55561dac9dfaed4ef02149
จาก nacl.signing นำเข้า SigningKey
จาก nacl.public นำเข้า PrivateKey, PublicKey, Box, SealedBox
จาก nacl.bindings นำเข้า crypto_sign_SEEDBYTES
จาก nacl.utils นำเข้า StringFixer สุ่ม

def รัน (ดีบัก = เท็จ):
    เมล็ด = สุ่ม (crypto_sign_SEEDBYTES)
    pk1 = PrivateKey.from_seed(เมล็ด)
    pk2 = SigningKey(seed).to_curve25519_private_key()
    หากแก้ไขข้อบกพร่อง:
        พิมพ์ ('seed: ', seed.hex ()) 
        พิมพ์ ('priv1: ', pk1._private_key.hex())
        พิมพ์ ('priv2: ', pk2._private_key.hex())
        พิมพ์ ('pub1: ', ไบต์ (pk1.public_key).hex ())
        พิมพ์ ('pub2: ', ไบต์ (pk2.public_key).hex ())
    เมล็ดกลับ, pk1, pk2

วิ่ง = 10,000
private_key_match = 0
public_key_match = 0
ทั้งสอง_match = 0

สำหรับฉันในช่วง (วิ่ง):
    ถ้าฉัน % 500 == 0:
        พิมพ์ (i, 'ของ', วิ่ง)
    เมล็ด, pk1, pk2 = เรียกใช้ ()
    x = pk1._private_key == pk2._private_key
    y = ไบต์ (pk1.public_key) == ไบต์ (pk2.public_key)
    ถ้า x:
        private_key_match += 1
    ถ้า y:
        public_key_match += 1
    ถ้า x และ y:
        ทั้งสอง_match += 1

พิมพ์ ('การจับคู่คีย์ส่วนตัว:', private_key_match)
พิมพ์ ('การจับคู่คีย์สาธารณะ:', public_key_match)
พิมพ์ ('ทั้งคู่ตรงกัน:', both_match)
Maarten Bodewes avatar
in flag
คีย์ส่วนตัวควรมีบิตไม่กี่บิตที่ตั้งค่าเฉพาะ หากตั้งค่าไว้แล้วโดยไม่ได้ตั้งใจ คุณจะได้สิ่งที่ได้รับ
cn flag
mkl
@MaartenBodewes ฉันไม่แน่ใจว่าฉันทำตามหรือไม่ คีย์ส่วนตัวไม่แสดงจำนวนเต็มที่ใช้ในการคำนวณคีย์สาธารณะรวมถึงการถอดรหัส ทำไมหลายบิตถึงไม่สำคัญ? มีข้อมูลจำเพาะที่ฉันขาดหายไปหรือไม่?
Maarten Bodewes avatar
in flag
ฉันต้องค้นหาสมการสำหรับสิ่งนั้น แต่ฉันสงสัยว่าความแตกต่างนั้นไม่ใช่เมื่อใช้มาสก์บิตหรือไม่ ฉันหวังว่าจะได้รับความช่วยเหลือจากเพื่อนนักเข้ารหัสของฉันที่นี่
Score:2
ธง cn

'curve25519' (แต่เดิม Bernstein เรียกมันว่า ตอนนี้เปลี่ยนชื่อเป็น X25519 หรือ XDH25519 เพื่อความชัดเจนเพราะเหมือนกัน เส้นโค้ง ในรูปแบบที่แตกต่างกันใช้สำหรับ Ed25519[ph]) กำหนดให้คีย์ส่วนตัว (ตัวคูณ) เป็นทวีคูณของ 8 เพื่อหลีกเลี่ยงการโจมตีกลุ่มย่อยขนาดเล็ก และตามอัตภาพจะแสดงเป็นลิตเติ้ลเอนด์ ดังนั้น 3 บิตต่ำของไบต์แรกจึงถูกบังคับให้ 0.

X25519 กำหนดให้บิตสูงเป็น 0 ด้วย (เนื่องจากฟิลด์พื้นฐานเป็นเพียงเกี่ยวกับ $2^{255}$) และ Bernstein ระบุบิตถัดไปถึงสูงเป็น 1 (เพื่อป้องกัน 'การเพิ่มประสิทธิภาพ' ที่จะอนุญาตให้มีการโจมตีตามเวลา) สิ่งเหล่านี้ส่งผลกระทบต่อ ล่าสุด ไบต์ในรูปแบบ little-endian ซึ่งคุณไม่ได้สังเกตเห็นในตัวอย่างของคุณก็เปลี่ยน bb - 7b และ 09 - 49 ด้วย

การยึดสอง (หรือสาม) ที่รวมกันนี้จะเปลี่ยนค่าที่เลือกแบบสุ่มทั้งหมด ยกเว้น (100/32)% ของเวลา ซึ่งใกล้เคียงพอกับ 3% สำหรับงานของรัฐบาล

Ed25519 มีความต้องการเหมือนกันสำหรับตัวคูณส่วนตัวที่จะเป็นทวีคูณของ 8 แต่ไม่ได้ใช้ไพรเวตคีย์โดยตรงสำหรับตัวคูณ แต่จะรันไพรเวตคีย์ผ่านแฮชเพื่อขยายและใช้สำหรับทั้งตัวคูณและค่าลับที่ทำให้ข้อความไม่บังตา และยึดเฉพาะส่วนตัวคูณไว้ภายใน โดยไม่แก้ไขสิ่งที่ผู้ใช้มองว่าเป็นไพรเวตคีย์

หลอก เหตุใดคีย์ลับ 3 บิตล่างของ curve25519/ed25519 จึงถูกล้างในระหว่างการสร้าง
และ โครงสร้างคีย์ Curve25519
และดู/เปรียบเทียบ https://datatracker.ietf.org/doc/html/rfc7748#page-8
เทียบกับ https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5 .

knaccc avatar
es flag
โปรดทราบว่าไม่จำเป็นว่าคีย์ส่วนตัวบนตัวแปรของเส้นโค้ง 25519 จะต้องเป็นทวีคูณของ 8 การใช้ลายเซ็นหรือ DH บางอย่างอาจทำเช่นนี้ได้ แต่นั่นเป็นเพียงวิธีเดียวในการป้องกันการโจมตีกลุ่มย่อยขนาดเล็ก คุณยังสามารถป้องกันการโจมตีได้ด้วยการคูณจุด EC ตามคำสั่งของกลุ่ม และตรวจสอบว่าผลลัพธ์คือจุดที่ไม่มีที่สิ้นสุด การดำเนินการนี้จะตรวจสอบว่าจุด EC อยู่ในกลุ่มย่อยที่ใหญ่กว่าที่ถูกต้อง ซึ่งป้องกันการโจมตีประเภทอื่นๆ เช่น การโจมตีเอกลักษณ์ของภาพคีย์ลายเซ็นรูปวงแหวนที่เชื่อมโยงได้
cn flag
mkl
@dave_thompson_085 มันมีประโยชน์มาก ขอบคุณดังนั้นดูเหมือนว่ามีการใช้งานฟังก์ชั่นอย่างใดอย่างหนึ่งไม่สมบูรณ์? แม้ว่าคีย์ส่วนตัวอย่างใดอย่างหนึ่งจะสร้างคีย์สาธารณะเดียวกัน (ฉันเข้าใจว่ามีการตรวจสอบ / ล้างข้อมูลบางอย่างเกิดขึ้นกับการสร้างคีย์สาธารณะ)
dave_thompson_085 avatar
cn flag
@miketery ไม่มีอะไรไม่สมบูรณ์ ดังที่ฉันได้กล่าวไปแล้วว่าโครงร่างมีการกำหนดแตกต่างกัน X25519 ต้องการการบีบคีย์ส่วนตัวที่เห็นภายนอก ขณะที่ Ed25519 ใช้ภายใน ดังนั้นไพรเวตคีย์ที่เห็นจะไม่ถูกหนีบ แต่การคูณเส้นโค้งวงรีภายในคือ
Score:2

ทางคณิตศาสตร์ ก Curve25519 รหัสส่วนตัวเป็นองค์ประกอบของ $\mathbb{F}_{2^{255}-19}$เช่น โมดูโลจำนวนเต็ม $2^{255}-19$. สิ่งนี้สามารถแสดงเป็นจำนวนเต็มระหว่าง $0$ และ $2^{255}-19-1$ซึ่งตัวมันเองสามารถแสดงได้โดยใช้ 255 บิต เนื่องจากคอมพิวเตอร์ที่ใช้งานได้จริงทำงานกับไบต์ 8 บิต ดังนั้นการแสดงจริงที่เล็กที่สุดจึงเป็นสตริง 32 ไบต์

มีรูปแบบมาตรฐานสำหรับการแสดงคีย์ส่วนตัวของ Curve25519 ซึ่งอธิบายไว้ใน RFC 7748 §5. รูปแบบนี้เข้ารหัสจำนวนเต็มระหว่าง $0$ และ $2^{255}-19-1$ เป็นสตริง 32 ไบต์ (256 บิต) ในลำดับ little-endian

สตริงขนาด 32 ไบต์สามารถแสดงตัวเลขในช่วง $[0,2^{256}-1]$ซึ่งมีขนาดใหญ่กว่า $[0,2^{255}-19-1]$. นอกจากนี้ ด้วยเหตุผลหลายประการ ไม่ใช่ตัวเลขทั้งหมดในช่วง $[0,2^{255}-19-1]$ เป็นสิ่งที่ดี (ดู โครงสร้างคีย์ Curve25519 และ “ขอให้วันที่สี่อยู่กับคุณ: การโจมตีช่องด้านข้างสถาปัตยกรรมขนาดเล็กบนแอปพลิเคชั่น Curve25519 ในโลกแห่งความเป็นจริงหลายรายการ” โดย Genkin และคณะ). ข้อจำกัดคือ:

  • จำนวนต้องอยู่ระหว่าง $2^{254}$ และ $2^{255}-19$ดังนั้นสองบิตที่มีนัยสำคัญที่สุดของตัวเลข 256 บิตต้องเป็น 0 และ 1
  • จำนวนต้องเป็นผลคูณของ 8 ดังนั้นสามบิตที่มีนัยสำคัญน้อยที่สุดต้องเป็น 0

RFC 7748 §5 ระบุว่าเมื่อสร้างคีย์ส่วนตัว ต้องใช้สตริง 32 ไบต์แบบสุ่ม (หรือสุ่มเทียม แล้วแต่กรณี) และบังคับให้ห้าบิตที่กล่าวถึงข้างต้นเป็นค่าบังคับ การใช้งาน Curve25519 ที่ใช้คีย์ส่วนตัวเป็นอินพุตในรูปแบบของสตริง 32 ไบต์จะต้องใช้การกำบังนี้ก่อนที่จะเริ่มทำการคำนวณกับตัวเลขที่คีย์นั้นแสดง

nacl.public.PrivateKey.from_seed ส่งคืนสตริงดิบ 32 ไบต์ nacl.signing.SigningKey(เมล็ด) ทำการกำบังดังนั้น to_curve25519_private_key ส่งออกค่าในรูปแบบมาตรฐานและปิดบัง

การกำบังเกี่ยวข้องกับ 5 บิต ดังนั้นเมื่อได้รับเมล็ดแบบสุ่มจึงมี a $1/2^5 = 1/32 \ประมาณ 3\%$ โอกาสที่มันมีค่าที่ถูกต้องสำหรับ 5 บิตนั้นแล้ว

cn flag
mkl
คำตอบที่ยอดเยี่ยม ขอบคุณ!

โพสต์คำตอบ

คนส่วนใหญ่ไม่เข้าใจว่าการถามคำถามมากมายจะปลดล็อกการเรียนรู้และปรับปรุงความสัมพันธ์ระหว่างบุคคล ตัวอย่างเช่น ในการศึกษาของ Alison แม้ว่าผู้คนจะจำได้อย่างแม่นยำว่ามีคำถามกี่ข้อที่ถูกถามในการสนทนา แต่พวกเขาไม่เข้าใจความเชื่อมโยงระหว่างคำถามและความชอบ จากการศึกษาทั้ง 4 เรื่องที่ผู้เข้าร่วมมีส่วนร่วมในการสนทนาด้วยตนเองหรืออ่านบันทึกการสนทนาของผู้อื่น ผู้คนมักไม่ตระหนักว่าการถามคำถามจะมีอิทธิพลหรือมีอิทธิพลต่อระดับมิตรภาพระหว่างผู้สนทนา