Skip to content

Commit

Permalink
Version 0.12.0 (#100)
Browse files Browse the repository at this point in the history
* Version 0.12.0

Signed-off-by: Vlad Gheorghiu <[email protected]>

* update

* Update

Signed-off-by: Vlad Gheorghiu <[email protected]>

* Update

Signed-off-by: Vlad Gheorghiu <[email protected]>

* Update

Signed-off-by: Vlad Gheorghiu <[email protected]>

* update

Signed-off-by: Vlad Gheorghiu <[email protected]>

* Update

Signed-off-by: Vlad Gheorghiu <[email protected]>

* update

Signed-off-by: Vlad Gheorghiu <[email protected]>

* Update

Signed-off-by: Vlad Gheorghiu <[email protected]>

* Update

Signed-off-by: Vlad Gheorghiu <[email protected]>

* Updates

Signed-off-by: Vlad Gheorghiu <[email protected]>

* c_int->c_size_t

Signed-off-by: Vlad Gheorghiu <[email protected]>

* Fixes

Signed-off-by: Vlad Gheorghiu <[email protected]>

* Update

Signed-off-by: Vlad Gheorghiu <[email protected]>

* Version 0.12

Signed-off-by: Vlad Gheorghiu <[email protected]>

* Update

Signed-off-by: Vlad Gheorghiu <[email protected]>

* Updated CHANGES

Signed-off-by: Vlad Gheorghiu <[email protected]>

---------

Signed-off-by: Vlad Gheorghiu <[email protected]>
  • Loading branch information
vsoftco authored Jan 15, 2025
1 parent 921e5ad commit 41f7bcb
Show file tree
Hide file tree
Showing 11 changed files with 188 additions and 66 deletions.
16 changes: 16 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
# Version 0.12.0 - January 15, 2025

- Fixes https://github.com/open-quantum-safe/liboqs-python/issues/98. The API
that NIST has introduced in
[FIPS 204](https://csrc.nist.gov/pubs/fips/204/final)
for ML-DSA includes a context string of length >= 0. Added new API for
signing with a context string
- `Signature.sign_with_ctx_str(self, message, context)`
- `Signature.verify_with_ctx_str(self, message, signature, context,
public_key)`
- When operations fail (i.e., `OQS_SUCCESS != 0`) in functions returning
non-boolean objects, a `RuntimeError` is now raised, instead of returning 0
- Bugfix on Linux, `c_int` -> `c_size_t` for buffer sizes
- Pyright type checking fixes
- Updated examples to use `ML-KEM` and `ML-DSA` as the defaults

# Version 0.10.0 - April 1, 2024

- Replaced CHANGES by
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2018-2024 Open Quantum Safe
Copyright (c) 2018-2025 Open Quantum Safe

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ you have access to a Python 3 interpreter. liboqs-python has been extensively
tested on Linux, macOS and Windows platforms. Continuous integration is
provided via GitHub actions.

The project contains the following files and directories:
The project contains the following files and directories

- **`oqs/oqs.py`: a Python 3 module wrapper for the liboqs C library.**
- `oqs/rand.py`: a Python 3 module supporting RNGs from `<oqs/rand.h>`
Expand Down Expand Up @@ -84,7 +84,8 @@ an alternative path, e.g., `C:\liboqs`, by passing the
cmake -S liboqs -B liboqs/build -DCMAKE_INSTALL_PREFIX="C:\liboqs" -DCMAKE_WINDOWS_EXPORT_ALL_SYMBOLS=TRUE -DBUILD_SHARED_LIBS=ON
```

Alternatively you can set the `OQS_INSTALL_PATH` environment variable to point to the installation directory, e.g., on a UNIX-like system execute:
Alternatively, you can set the `OQS_INSTALL_PATH` environment variable to point
to the installation directory, e.g., on a UNIX-like system, execute

```shell
export OQS_INSTALL_PATH=/path/to/liboqs
Expand Down Expand Up @@ -148,7 +149,7 @@ python3 liboqs-python/examples/rand.py
Execute

```shell
nose2 --verbose liboqs-python
nose2 --verbose
```

---
Expand Down Expand Up @@ -205,7 +206,7 @@ docker run -it oqs-python sh -c ". venv/bin/activate && python liboqs-python/exa
Or, run the unit tests with

```shell
docker run -it oqs-python sh -c ". venv/bin/activate && nose2 --verbose liboqs-python"
docker run -it oqs-python sh -c ". venv/bin/activate && nose2 --verbose"
```

In case you want to use the Docker container as a development environment,
Expand Down Expand Up @@ -265,7 +266,7 @@ Waterloo.

### Contributors

Contributors to the liboqs-python wrapper include:
Contributors to the liboqs-python wrapper include

- Ben Davies (University of Waterloo)
- Vlad Gheorghiu ([softwareQ Inc.](https://www.softwareq.ca) and the University
Expand Down
8 changes: 4 additions & 4 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# liboqs-python version 0.10.0
# liboqs-python version 0.12.0

---

Expand All @@ -24,13 +24,13 @@ See in particular limitations on intended use.

## Release notes

This release of liboqs-python was released on April 1, 2024. Its release
This release of liboqs-python was released on January 15, 2025. Its release
page on GitHub is
https://github.com/open-quantum-safe/liboqs-python/releases/tag/0.10.0.
https://github.com/open-quantum-safe/liboqs-python/releases/tag/0.12.0.

---

## What's New

This is the 10th release of liboqs-python. For a list of changes see
This is the 11th release of liboqs-python. For a list of changes see
[CHANGES.md](https://github.com/open-quantum-safe/liboqs-python/blob/main/CHANGES.md).
2 changes: 1 addition & 1 deletion examples/kem.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
pprint(kems, compact=True)

# Create client and server with sample KEM mechanisms
kemalg = "Kyber512"
kemalg = "ML-KEM-512"
with oqs.KeyEncapsulation(kemalg) as client:
with oqs.KeyEncapsulation(kemalg) as server:
print("\nKey encapsulation details:")
Expand Down
2 changes: 1 addition & 1 deletion examples/sig.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
message = "This is the message to sign".encode()

# Create signer and verifier with sample signature mechanisms
sigalg = "Dilithium2"
sigalg = "ML-DSA-44"
with oqs.Signature(sigalg) as signer:
with oqs.Signature(sigalg) as verifier:
print("\nSignature details:")
Expand Down
150 changes: 117 additions & 33 deletions oqs/oqs.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def _countdown(seconds):
def _load_shared_obj(name, additional_searching_paths=None):
"""Attempts to load shared library."""
paths = []
dll = ct.windll if platform.system() == "Windows" else ct.cdll
dll = ct.windll if platform.system() == "Windows" else ct.cdll # type: ignore

# Search additional path, if any
if additional_searching_paths:
Expand Down Expand Up @@ -138,7 +138,6 @@ def _load_liboqs():
assert _liboqs
except RuntimeError:
sys.exit("Could not load liboqs shared library")

return _liboqs


Expand All @@ -162,7 +161,7 @@ def native():
def oqs_version():
"""liboqs version string."""
native().OQS_version.restype = ct.c_char_p
return ct.c_char_p(native().OQS_version()).value.decode("UTF-8")
return ct.c_char_p(native().OQS_version()).value.decode() # type: ignore


# Warn the user if the liboqs version differs from liboqs-python version
Expand Down Expand Up @@ -262,7 +261,7 @@ def __init__(self, alg_name, secret_key=None):
def __enter__(self):
return self

def __exit__(self, ctx_type, ctx_value, ctx_traceback):
def __exit__(self, _ctx_type, _ctx_value, _ctx_traceback):
self.free()

def generate_keypair(self):
Expand All @@ -276,7 +275,10 @@ def generate_keypair(self):
rv = native().OQS_KEM_keypair(
self._kem, ct.byref(public_key), ct.byref(self.secret_key)
)
return bytes(public_key) if rv == OQS_SUCCESS else 0
if rv == OQS_SUCCESS:
return bytes(public_key)
else:
raise RuntimeError("Can not generate keypair")

def export_secret_key(self):
"""Exports the secret key."""
Expand All @@ -288,30 +290,36 @@ def encap_secret(self, public_key):
:param public_key: the peer's public key.
"""
my_public_key = ct.create_string_buffer(
c_public_key = ct.create_string_buffer(
public_key, self._kem.contents.length_public_key
)
ciphertext = ct.create_string_buffer(self._kem.contents.length_ciphertext)
shared_secret = ct.create_string_buffer(self._kem.contents.length_shared_secret)
rv = native().OQS_KEM_encaps(
self._kem, ct.byref(ciphertext), ct.byref(shared_secret), my_public_key
self._kem, ct.byref(ciphertext), ct.byref(shared_secret), c_public_key
)
return bytes(ciphertext), bytes(shared_secret) if rv == OQS_SUCCESS else 0
if rv == OQS_SUCCESS:
return bytes(ciphertext), bytes(shared_secret)
else:
raise RuntimeError("Can not encapsulate secret")

def decap_secret(self, ciphertext):
"""
Decapsulates the ciphertext and returns the secret.
:param ciphertext: the ciphertext received from the peer.
"""
my_ciphertext = ct.create_string_buffer(
c_ciphertext = ct.create_string_buffer(
ciphertext, self._kem.contents.length_ciphertext
)
shared_secret = ct.create_string_buffer(self._kem.contents.length_shared_secret)
rv = native().OQS_KEM_decaps(
self._kem, ct.byref(shared_secret), my_ciphertext, self.secret_key
self._kem, ct.byref(shared_secret), c_ciphertext, self.secret_key
)
return bytes(shared_secret) if rv == OQS_SUCCESS else 0
if rv == OQS_SUCCESS:
return bytes(shared_secret)
else:
raise RuntimeError("Can not decapsulate secret")

def free(self):
"""Releases the native resources."""
Expand Down Expand Up @@ -374,6 +382,7 @@ class Signature(ct.Structure):
("alg_version", ct.c_char_p),
("claimed_nist_level", ct.c_ubyte),
("euf_cma", ct.c_ubyte),
("sig_with_ctx_support", ct.c_ubyte),
("length_public_key", ct.c_size_t),
("length_secret_key", ct.c_size_t),
("length_signature", ct.c_size_t),
Expand Down Expand Up @@ -404,6 +413,7 @@ def __init__(self, alg_name, secret_key=None):
"version": self._sig.contents.alg_version.decode(),
"claimed_nist_level": int(self._sig.contents.claimed_nist_level),
"is_euf_cma": bool(self._sig.contents.euf_cma),
"sig_with_ctx_support": bool(self._sig.contents.sig_with_ctx_support),
"length_public_key": int(self._sig.contents.length_public_key),
"length_secret_key": int(self._sig.contents.length_secret_key),
"length_signature": int(self._sig.contents.length_signature),
Expand All @@ -417,7 +427,7 @@ def __init__(self, alg_name, secret_key=None):
def __enter__(self):
return self

def __exit__(self, ctx_type, ctx_value, ctx_traceback):
def __exit__(self, _ctx_type, _ctx_value, _ctx_traceback):
self.free()

def generate_keypair(self):
Expand All @@ -431,7 +441,10 @@ def generate_keypair(self):
rv = native().OQS_SIG_keypair(
self._sig, ct.byref(public_key), ct.byref(self.secret_key)
)
return bytes(public_key) if rv == OQS_SUCCESS else 0
if rv == OQS_SUCCESS:
return bytes(public_key)
else:
raise RuntimeError("Can not generate keypair")

def export_secret_key(self):
"""Exports the secret key."""
Expand All @@ -444,22 +457,25 @@ def sign(self, message):
:param message: the message to sign.
"""
# Provide length to avoid extra null char
my_message = ct.create_string_buffer(message, len(message))
message_len = ct.c_int(len(my_message))
signature = ct.create_string_buffer(self._sig.contents.length_signature)
sig_len = ct.c_int(
self._sig.contents.length_signature
) # initialize to maximum signature size
c_message = ct.create_string_buffer(message, len(message))
c_message_len = ct.c_size_t(len(c_message))
c_signature = ct.create_string_buffer(self._sig.contents.length_signature)

# Initialize to maximum signature size
signature_len = ct.c_size_t(self._sig.contents.length_signature)

rv = native().OQS_SIG_sign(
self._sig,
ct.byref(signature),
ct.byref(sig_len),
my_message,
message_len,
ct.byref(c_signature),
ct.byref(signature_len),
c_message,
c_message_len,
self.secret_key,
)

return bytes(signature[: sig_len.value]) if rv == OQS_SUCCESS else 0
if rv == OQS_SUCCESS:
return bytes(c_signature[: signature_len.value])
else:
raise RuntimeError("Can not sign message")

def verify(self, message, signature, public_key):
"""
Expand All @@ -470,17 +486,85 @@ def verify(self, message, signature, public_key):
:param public_key: the signer's public key.
"""
# Provide length to avoid extra null char
my_message = ct.create_string_buffer(message, len(message))
message_len = ct.c_int(len(my_message))

# Provide length to avoid extra null char in sig
my_signature = ct.create_string_buffer(signature, len(signature))
sig_len = ct.c_int(len(my_signature))
my_public_key = ct.create_string_buffer(
c_message = ct.create_string_buffer(message, len(message))
c_message_len = ct.c_size_t(len(c_message))
c_signature = ct.create_string_buffer(signature, len(signature))
signature_len = ct.c_size_t(len(c_signature))
c_public_key = ct.create_string_buffer(
public_key, self._sig.contents.length_public_key
)

rv = native().OQS_SIG_verify(
self._sig, my_message, message_len, my_signature, sig_len, my_public_key
self._sig,
c_message,
c_message_len,
c_signature,
signature_len,
c_public_key,
)
return True if rv == OQS_SUCCESS else False

def sign_with_ctx_str(self, message, context):
"""
Signs the provided message with context string and returns the signature.
:param context: the context string.
:param message: the message to sign.
"""
# Provide length to avoid extra null char
c_message = ct.create_string_buffer(message, len(message))
c_message_len = ct.c_size_t(len(c_message))
c_context = ct.create_string_buffer(context, len(context))
context_len = ct.c_size_t(len(c_context))
c_signature = ct.create_string_buffer(self._sig.contents.length_signature)

# Initialize to maximum signature size
c_signature_len = ct.c_size_t(self._sig.contents.length_signature)

rv = native().OQS_SIG_sign_with_ctx_str(
self._sig,
ct.byref(c_signature),
ct.byref(c_signature_len),
c_message,
c_message_len,
c_context,
context_len,
self.secret_key,
)
if rv == OQS_SUCCESS:
return bytes(c_signature[: c_signature_len.value])
else:
raise RuntimeError("Can not sign message with context string")

def verify_with_ctx_str(self, message, signature, context, public_key):
"""
Verifies the provided signature on the message with context string; returns True if valid.
:param message: the signed message.
:param signature: the signature on the message.
:param context: the context string.
:param public_key: the signer's public key.
"""
# Provide length to avoid extra null char
c_message = ct.create_string_buffer(message, len(message))
c_message_len = ct.c_size_t(len(c_message))
c_signature = ct.create_string_buffer(signature, len(signature))
c_signature_len = ct.c_size_t(len(c_signature))
c_context = ct.create_string_buffer(context, len(context))
c_context_len = ct.c_size_t(len(c_context))
c_public_key = ct.create_string_buffer(
public_key, self._sig.contents.length_public_key
)

rv = native().OQS_SIG_verify_with_ctx_str(
self._sig,
c_message,
c_message_len,
c_signature,
c_signature_len,
c_context,
c_context_len,
c_public_key,
)
return True if rv == OQS_SUCCESS else False

Expand Down
2 changes: 1 addition & 1 deletion oqs/rand.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def randombytes(bytes_to_read):
:return: random bytes.
"""
result = oqs.ct.create_string_buffer(bytes_to_read)
oqs.native().OQS_randombytes(result, oqs.ct.c_int(bytes_to_read))
oqs.native().OQS_randombytes(result, oqs.ct.c_size_t(bytes_to_read))
return bytes(result)


Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "liboqs-python"
requires-python = ">=3.8"
version = "0.10.0"
version = "0.12.0"
description = "Python bindings for liboqs, providing post-quantum public key cryptography algorithms"
authors = [
{ name = "Open Quantum Safe project", email = "[email protected]" },
Expand Down
Loading

0 comments on commit 41f7bcb

Please sign in to comment.