记录下在docker上部署sage的密码学交互题目的过程。
以下是maple佬出的一道题的deploy文件,给选手的py附件和deploy的py附件是同一个文件,非常方便,这也是我选它做模板的理由:
from typing import Tuple
from Crypto.Util.number import *
from gmpy2 import powmod
import os
flag = os.environb.get(b"FLAG", b"TSJ{test_flag}")
def pow(a: int, b: int, c: int) -> int:
# gmpy2.powmod is much faster than pow
return int(powmod(a, b, c))
def getPrimeOrderGroup(bits) -> Tuple[int, int, int]:
"""
Generate a prime p with large prime factor q and a generator g
"""
while True:
q = getPrime(bits)
for i in range(2, 257, 2):
p = q * i + 1
if isPrime(p):
g = pow(getRandomRange(2, p), i, p)
if g != 1:
assert pow(g, q, p) == 1
return p, q, g
class RSA:
def __init__(self, bits):
self.p = getPrime(bits // 2)
self.q = getPrime(bits // 2)
self.n = self.p * self.q
self.e = 65537
self.d = pow(self.e, -1, (self.p - 1) * (self.q - 1))
def encrypt(self, m: int) -> int:
return pow(m, self.e, self.n)
def decrypt(self, c: int) -> int:
return pow(c, self.d, self.n)
def __str__(self) -> str:
e = self.e
n = self.n
return f"RSA({n}, {e})"
class ElGamal:
def __init__(self, bits):
self.p, self.q, self.g = getPrimeOrderGroup(bits)
self.x = getRandomRange(2, self.q)
self.y = pow(self.g, self.x, self.p)
def encrypt(self, m: int) -> Tuple[int, int]:
r = getRandomRange(2, self.q)
s = pow(self.y, r, self.p)
c1 = pow(self.g, r, self.p)
c2 = (s * m) % self.p
return c1, c2
def decrypt(self, c1: int, c2: int) -> int:
s = pow(c1, self.x, self.p)
m = (pow(s, -1, self.p) * c2) % self.p
return m
def __str__(self) -> str:
p = self.p
g = self.g
y = self.y
return f"ElGamal({p}, {g}, {y})"
elg = ElGamal(1024)
rsa = RSA(1024)
print("Welcome to Cipher Switching Service!")
print()
print("This is our public keys:")
print(rsa)
print(elg)
print()
print("And this is some information about the encrypted flag:")
print(f"{len(flag) = }")
flag += os.urandom(96 - len(flag)) # random padding
flagenc = rsa.encrypt(bytes_to_long(flag))
print(f"{flagenc = }")
print()
for _ in range(1337):
print("1. RSA to ElGamal")
print("2. ElGamal to RSA")
print("3. Quit")
choice = int(input("> "))
if choice == 1:
c = int(input("c = "))
print(elg.encrypt(rsa.decrypt(c)))
elif choice == 2:
c1 = int(input("c1 = "))
c2 = int(input("c2 = "))
print(rsa.encrypt(elg.decrypt(c1, c2)))
elif choice == 3:
print("Bye")
break
else:
print(f"Unknown choice: {choice}")
else:
print("To prevent abuse, we limit each session to have only 1337 attempts at most.")
Dockerfile
FROM python:slim
RUN apt-get update && apt-get install -yqq socat
RUN pip install gmpy2 pycryptodome
RUN mkdir /app
WORKDIR /app
COPY challenge.py .
CMD ["/bin/sh", "-c", "socat TCP-LISTEN:8763,fork,reuseaddr EXEC:'python challenge.py',stderr"]
docker-compose.yml
version: '3.9'
services:
chall:
build:
context: .
init: true
environment:
- FLAG=TSJ{a_weird_oracle?}
ports:
- 8763:8763
在这个基础上修改成支持sage的python,首先就要解决安装sage的问题。主要遇到的问题有二:
嘛,解决起来也简单,前者把基础镜像改成debian
FROM debian:stable-slim
后者换源
RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/debian.sources
就能非常爽滑的操作apt-get update -y sagemath了。