require 'openssl' require 'digest' STDOUT.sync = true class OpenSSL::PKey::EC::Point def xy n = to_bn(:uncompressed).to_i mask = (1 << group.degree) - 1 return (n >> group.degree) & mask, n & mask end alias_method :+, :add alias_method :*, :mul end class ECDSA def initialize @curve = OpenSSL::PKey::EC::Group.new('secp256k1') @G = @curve.generator @n = @curve.order.to_i @d = OpenSSL::BN.rand(@curve.degree).to_i @Q = @G * @d end def inv(x) x.pow(@n - 2, @n) end def sign(msg) z = Digest::SHA256.hexdigest(msg).hex k = OpenSSL::BN.rand(@curve.degree / 3).to_s.unpack1('H*').hex x, y = (@G * k).xy # We all like hacks, ain't we? # s = (z + x * @d) * inv(k) % @n s = (z + @d) * inv(k) % @n return x, s end def verify(msg, x, s) return false if x % @n == 0 || s % @n == 0 z = Digest::SHA256.hexdigest(msg).hex # ditto # x2, y2 = (@G * (z * inv(s)) + @Q * (x * inv(s))).xy x2, y2 = (@G * (z * inv(s)) + @Q * inv(s)).xy return x == x2 end end ecdsa = ECDSA.new 5.times do puts <<~EOS 1. Sign 2. Find rule 3. Exit EOS print 'choice? ' case gets.chomp when '1' x, s = ecdsa.sign('Baba') puts 'Baba is:' puts "x = #{x}" puts "s = #{s}" when '2' print 'Which rule do you want to know? '; msg = gets.chomp print 'x? '; x = gets.to_i print 's? '; s = gets.to_i if ecdsa.verify(msg, x, s) if msg == 'Baba' puts 'Baba is you' elsif msg == 'Flag' puts "Flag is #{ENV['FLAG']}" else puts 'Not Found :(' end else puts 'Invalid :(' end else exit end end puts 'You is defeat.'
ただしsignはのように、の計算にを使わなくなっている
このとき何が起こるかというと、からlift_x
するとが復元でき、である。
は知っているはずなのででが導ける
がわかれば署名を偽造できる。を適当に固定してしまえばとなれば良くて、左辺の値は全て知っているから、を求めることができる。
from hashlib import sha256 from ptrlib import Socket p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f K = GF(p) a = K(0x0000000000000000000000000000000000000000000000000000000000000000) b = K(0x0000000000000000000000000000000000000000000000000000000000000007) E = EllipticCurve(K, (a, b)) G = E(0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798, 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8) E.set_order(0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 * 0x1) sock = Socket("nc 34.146.212.53 65434") sock.sendlineafter("choice? ", "1") # -- find Q r = int(sock.recvlineafter("x = ")) s = int(sock.recvlineafter("s = ")) z = int(sha256(b"Baba").hexdigest(), 16) kG = E.lift_x(Integer(r)) Q = s*kG - z*G # -- forgery z = int(sha256(b"Flag").hexdigest(), 16) s = 1 r = (z*G + Q).xy()[0] sock.sendlineafter("choice? ", "2") sock.sendlineafter("know? ", "Flag") sock.sendlineafter("x?", str(r)) sock.sendlineafter("s?", str(s)) sock.interactive()