Docker image creation (#47)
* OpenSSL enablement
* added python API sample and documentation
diff --git a/.circleci/config.yml b/.circleci/config.yml
index e3fd03e..691522f 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -80,9 +80,39 @@
source venv/bin/activate && pip install nose rednose
python -m nose --rednose --verbose
+ dockerbuild:
+ description: Building and pushing oqs-enabled python image
+ docker:
+ - image: openquantumsafe/ci-ubuntu-bionic-x86_64:latest
+ auth:
+ username: $DOCKER_LOGIN
+ password: $DOCKER_PASSWORD
+ steps:
+ - checkout # change this from "checkout" to "*localCheckout" when running CircleCI locally
+ - setup_remote_docker
+ - run:
+ name: Authenticate to Docker
+ command: |
+ echo $DOCKER_PASSWORD | docker login --username $DOCKER_LOGIN --password-stdin
+ - run:
+ name: Build docker image
+ command: |
+ docker build --build-arg MAKE_DEFINES="-j 18" -t oqs-python .
+ working_directory: docker
+ - run:
+ name: Test docker image
+ command: |
+ docker run -e SHORT_TEST="Yes" oqs-python
+ - run:
+ name: Push docker image
+ command: |
+ docker tag oqs-python $TARGETNAME/python:latest && docker push $TARGETNAME/python:latest
+
workflows:
build:
jobs:
+ - dockerbuild:
+ context: openquantumsafe
- ubuntu_bionic:
name: ubuntu-bionic
context: openquantumsafe
diff --git a/docker/Dockerfile b/docker/Dockerfile
new file mode 100644
index 0000000..0bc9191
--- /dev/null
+++ b/docker/Dockerfile
@@ -0,0 +1,63 @@
+# Multi-stage build: First the full builder image:
+
+# liboqs build type variant; maximum portability of image; no openssl dependency:
+ARG LIBOQS_BUILD_DEFINES="-DOQS_DIST_BUILD=ON -DBUILD_SHARED_LIBS=ON -DOQS_USE_OPENSSL=OFF"
+
+# make build arguments: Adding -j here speeds up build but may tax hardware
+ARG MAKE_DEFINES="-j 2"
+
+FROM alpine:3.11 as intermediate
+# Take in all global args
+ARG LIBOQS_BUILD_DEFINES
+ARG MAKE_DEFINES
+
+LABEL version="2"
+
+ENV DEBIAN_FRONTEND noninteractive
+
+RUN apk update && apk upgrade
+
+# Get all software packages required for builing all components:
+RUN apk add build-base linux-headers cmake ninja git
+
+# get all sources
+WORKDIR /opt
+RUN git clone --depth 1 --branch main https://github.com/open-quantum-safe/liboqs && \
+ git clone --depth 1 --branch main https://github.com/open-quantum-safe/liboqs-python.git
+
+# build liboqs
+WORKDIR /opt/liboqs
+RUN mkdir build && cd build && cmake -GNinja .. ${LIBOQS_BUILD_DEFINES} && ninja install
+
+WORKDIR /opt
+RUN git clone --depth 1 --branch OQS-OpenSSL_1_1_1-stable https://github.com/open-quantum-safe/openssl.git && cd liboqs && mkdir build-openssl && cd build-openssl && cmake -G"Ninja" .. ${LIBOQS_BUILD_DEFINES} -DCMAKE_INSTALL_PREFIX=/opt/openssl/oqs && ninja install
+
+RUN apk add automake autoconf && cd /opt/openssl && LDFLAGS="-Wl,-rpath -Wl,/usr/local/lib" ./Configure shared linux-x86_64 -lm && make ${MAKE_DEFINES} && make install_sw
+
+# Get LetsEncrypt root
+RUN wget https://letsencrypt.org/certs/isrgrootx1.pem
+
+## second stage: Only create minimal image without build tooling and intermediate build results generated above:
+FROM alpine:3.11
+
+# Get all software packages required for running all components:
+RUN apk update && apk upgrade && apk add python3
+
+# Only retain the binary contents in the final image
+COPY --from=intermediate /usr/local /usr/local
+COPY --from=intermediate /opt/liboqs-python /opt/liboqs-python
+
+# Replace standard OpenSSL libs:
+RUN rm /lib/libssl.so.1.1 /lib/libcrypto.so.1.1 && ln -s /usr/local/lib64/libcrypto.so.1.1 /lib && ln -s /usr/local/lib64/libssl.so.1.1 /lib && ln -s /usr/local/lib64/liboqs.so /lib && ln -s /usr/local/lib64/liboqs.so.0 /lib
+
+ENV PYTHONPATH=/opt/liboqs-python
+
+# Enable a normal user
+RUN addgroup -g 1000 -S oqs && adduser --uid 1000 -S oqs -G oqs
+
+USER oqs
+WORKDIR /home/oqs
+COPY minitest.py /home/oqs/minitest.py
+COPY --from=intermediate /opt/isrgrootx1.pem /home/oqs/isrgrootx1.pem
+
+CMD ["python3", "minitest.py"]
diff --git a/Dockerfile b/docker/Dockerfile-simple
similarity index 100%
rename from Dockerfile
rename to docker/Dockerfile-simple
diff --git a/docker/USAGE.md b/docker/USAGE.md
new file mode 100644
index 0000000..143b2a1
--- /dev/null
+++ b/docker/USAGE.md
@@ -0,0 +1,16 @@
+# OQS-python
+
+This docker image contains python3 with library support for quantum-safe crypto (QSC) operations.
+
+To this end, it contains [liboqs](https://github.com/open-quantum-safe/liboqs) as well as [OQS-OpenSSL](https://github.com/open-quantum-safe/openssl) from the [OpenQuantumSafe](https://openquantumsafe.org) project all wrapped up in Python APIs using [liboqs-python](https://github.com/open-quantum-safe/liboqs-python).
+
+## Quick start
+
+- Executing `docker run -it openquantumsafe/python` tests all QSC algorithms against the interop server at https://test.openquantumsafe.org.
+- Executing `docker run -it openquantumsafe/python sh` provides a shell environment where liboqs and QSC-enabled SSL/TLS is available for use. See the included file `minitest.py` for sample code exercizing this functionality.
+
+## Further examples
+
+More samples are available at [liboqs-python examples](https://github.com/open-quantum-safe/liboqs-python/tree/main/examples).
+
+
diff --git a/docker/minitest.py b/docker/minitest.py
new file mode 100644
index 0000000..62e68d9
--- /dev/null
+++ b/docker/minitest.py
@@ -0,0 +1,58 @@
+import ssl
+import urllib.request
+import json
+import os
+import oqs
+
+# Example code testing oqs signature functionality. See more example code at
+# https://github.com/open-quantum-safe/liboqs-python/tree/main/examples
+
+message = "This is the message to sign".encode()
+
+# create signer and verifier with sample signature mechanisms
+sigalg = "Dilithium2"
+with oqs.Signature(sigalg) as signer:
+ with oqs.Signature(sigalg) as verifier:
+ signer_public_key = signer.generate_keypair()
+ signature = signer.sign(message)
+ is_valid = verifier.verify(message, signature, signer_public_key)
+
+if (not is_valid):
+ print("Failed to validate signature. Exiting.")
+ exit(1)
+else:
+ print("Validated signature for OQS algorithm %s" % (sigalg))
+
+# Example code iterating over all supported OQS algorithms integrated into TLS
+
+sslContext= ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
+sslContext.verify_mode = ssl.CERT_REQUIRED
+# Trust LetsEncrypt root CA:
+sslContext.load_verify_locations(cafile="isrgrootx1.pem")
+
+# Retrieve interop test server root CA
+with urllib.request.urlopen('https://test.openquantumsafe.org/CA.crt', context=sslContext) as response:
+ data=response.read()
+ with open("CA.crt", "w+b") as f:
+ f.write(data)
+
+# Retrieve JSON structure of all alg/port combinations:
+with urllib.request.urlopen('https://test.openquantumsafe.org/assignments.json', context=sslContext) as response:
+ assignments=json.loads(response.read())
+
+# Trust test.openquantumsafe.org root CA:
+sslContext.load_verify_locations(cafile="CA.crt")
+
+# Iterate over all algorithm/port combinations:
+for sigs, kexs in assignments.items():
+ for kex, port in kexs.items():
+ if (kex != "*"): # '*' denoting any classic KEX alg
+ # Enable use of the specific QSC KEX algorithm
+ os.environ["TLS_DEFAULT_GROUPS"]=kex
+ with urllib.request.urlopen('https://test.openquantumsafe.org:'+str(port), context=sslContext) as response:
+ if response.getcode() != 200:
+ print("Failed to test %s successfully" % (kex))
+ else:
+ print("Success testing %s at port %d" % (kex, port))
+ if "SHORT_TEST" in os.environ:
+ exit(0)