MENU
  

z3 is satisfiability modulo theories (SMT), docs

solve simple linear equation system like this:

import z3

solver = z3.Solver()
x = z3.Int('x') # 2
y = z3.Int('y') # 3
z = z3.Int('z') # 5

solver.add(x + y == 5)
solver.add(y + z == 8)
solver.add(z + x == 7)

if solver.check() == z3.sat:
    m = solver.model()
    print("x=" + str(m.eval(x).as_long()))
    print("y=" + str(m.eval(y).as_long()))
    print("z=" + str(m.eval(z).as_long()))
else:
    print("No solution!")

It can also solve quadratic equations. If there are several solutions, z3 will just return one of them. If you want to know if there is just a single solution, or if you want to find the other solutions, you can add a further constraint saying that the solution may not be X (where X is the first solution z3 provided)

from z3 import *

x = Real('x')

solver = Solver()
solver.add(x * x - 5*x + 6 == 0)

if solver.check() == sat:
    model = solver.model()
    print("solution=" + str(model[x]))
    solver.add(x != model[x])
    if solver.check() == sat:
        model = solver.model()
        print("another solution x=" + str(model[x]))
    else:
        print("there was just one solution")
else:
    print("no solution")

A variable is called "under-constrained" if there isn't enough constraints. If you eval an under-constrained variable it returns the "symbolic" variable rather than concrete value(s), so you can check if a variable is under-constrained by doing:

    if m.eval(foo, model_completion=False) == foo:
        print("variable foo is under-constrained")

if a cipher text is constructed by xoring a plaintext and a keystream, then z3 can be used to recover the plaintext

import z3

solver = z3.Solver()

#pt = bytes.fromhex("68656c6c6f20776f726c64")
k  = bytes.fromhex("3132333435363738393031")
ct = bytes.fromhex("59575f585a1640574b5c55")
pt = [z3.BitVec(f"pt_{i}", 8) for i in range(len(ct))]
foo = z3.Int("foo")

for idx in range(0, len(ct)):
    solver.add(pt[idx] ^ k[idx] == ct[idx])

if solver.check() == z3.sat:
    m = solver.model()

    if m.eval(foo, model_completion=False) == foo:
        print("variable foo is under-constrained")

    if z3.is_bv_value(m.eval(pt[0], model_completion=False)):
        print("variable pt[0] is NOT under-constrained: " + str(m.eval(pt[0], model_completion=False).as_long()))
    else:
        print("variable pt[0] is under-constrained")

    if z3.is_bv_value(m.eval(pt[1], model_completion=False)):
        print("variable pt[1] is NOT under-constrained: " + str(m.eval(pt[1], model_completion=False).as_long()))
    else:
        print("variable pt[1] is under-constrained")


    pt_str = ''.join([chr(m.eval(pt[idx]).as_long()) for idx in range(0, len(ct))])
    print("pt=" + pt_str + "####")

else:
    print("No solution!")