From 910380ce6d123d147e904d254c25f5d20e9fca10 Mon Sep 17 00:00:00 2001 From: poing Date: Mon, 22 Jan 2024 12:56:11 +0900 Subject: [PATCH] More readme and some endianness testing --- README.md | 91 +++++++++++++++++++++++++++++++++++++++- gems/example1.py | 38 +++++++++++++++++ gems/example2.py | 36 ++++++++++++++++ js2pysecrets/__init__.py | 1 + js2pysecrets/base.py | 1 + 5 files changed, 165 insertions(+), 2 deletions(-) create mode 100755 gems/example1.py create mode 100755 gems/example2.py diff --git a/README.md b/README.md index bde8bcc..3565d73 100644 --- a/README.md +++ b/README.md @@ -3,15 +3,102 @@ [![codecov](https://codecov.io/gh/poing/JS2PySecrets/branch/main/graph/badge.svg?token=JS2PySecrets_token_here)](https://codecov.io/gh/poing/JS2PySecrets) [![CI](https://github.com/poing/JS2PySecrets/actions/workflows/main.yml/badge.svg)](https://github.com/poing/JS2PySecrets/actions/workflows/main.yml) -Awesome js2pysecrets created by poing + +This is a `Python` implementation of [Shamir's threshold secret sharing scheme](http://en.wikipedia.org/wiki/Shamir's_Secret_Sharing), based **and compatible with** the `JavaScript` fork of `secrets.js` [*maintained by `grempe`*](https://github.com/grempe/secrets.js). Which is orginally based on the code created by `amper5and` on Github. The [original secrets.js can be found there](https://github.com/amper5and/secrets.js/). ## Status The project is intended to create a `Python` version that is compatible with `secrets.js`. It's currently in the **DEVELOPMENT** stage and the framework is being built to *effectively* test and run the `JavaScript` from within the `Python` environment. +All of the `JavaScript` functions can be called from *within* the `Python` environment. However, **there are limitations**! Most notably, you can **ONLY** call a single function, so some of the utility provided by the `JavaScript` version is not available. *In many cases, isn't even necessary*. + +Combing of shares is not working *yet*... + ### Requirements -The use this project in it's current state **and for testing**, `Node` is required on the system. This is used to run the `JavaScript`. +**`Node`** is **required**. + +To use this project in it's current state **and for testing**, `Node` is required on the system. `Node` will always bee required for testing. It's used to run the `JavaScript` in the local environment. + +## Examples + +Divide a 512-bit key, expressed in hexadecimal form, into 10 shares, requiring that any 5 of them are necessary to reconstruct the original key: + +**Not everything is working yet...** + +```python +import json +import js2pysecrets + +# generate a 512-bit key +# key = js2pysecrets.random(512) // => key is a hex string +key = js2pysecrets.random(512) +print(key) + +# split into 10 shares with a threshold of 5 +# shares = js2pysecrets.share(key, 10, 5) +# => shares = ['801xxx...xxx','802xxx...xxx','803xxx...xxx','804xxx...xxx','805xxx...xxx'] +shares = js2pysecrets.share(key, 10, 5) +print(shares) + +# // combine 4 shares +# var comb = secrets.combine(shares.slice(0, 4)) +# console.log(comb === key) // => false +# +# // combine 5 shares +# comb = secrets.combine(shares.slice(4, 9)) +# console.log(comb === key) // => true +# +# // combine ALL shares +# comb = secrets.combine(shares) +# console.log(comb === key) // => true +# +# // create another share with id 8 +# var newShare = secrets.newShare(8, shares) // => newShare = '808xxx...xxx' +# +# // reconstruct using 4 original shares and the new share: +# comb = secrets.combine(shares.slice(1, 5).concat(newShare)) +# console.log(comb === key) // => true +``` + +Divide a password containing a mix of numbers, letters, and other characters, requiring that any 3 shares must be present to reconstruct the original password: + +**Things really start to break here...** +*Big and reversed endianness for the `JS` str2hex* + +```python +import json +import js2pysecrets + +# var pw = "<>" +pw = "<>" + +# convert the text into a hex string +# jsHex = secrets.str2hex(pw) // => hex string +jsHex = js2pysecrets.str2hex(pw) +print(jsHex) + +# Notice how the JS uses an unconventional str2hex method +pyHex = pw.encode('utf-16').hex().lstrip('fe') # Stripped off the BOM +print(pyHex) + +# // split into 5 shares, with a threshold of 3 +# var shares = secrets.share(pwHex, 5, 3) +# +# // combine 2 shares: +# var comb = secrets.combine(shares.slice(1, 3)) +# +# //convert back to UTF string: +# comb = secrets.hex2str(comb) +# console.log(comb === pw) // => false +# +# // combine 3 shares: +# comb = secrets.combine([shares[1], shares[3], shares[4]]) +# +# //convert back to UTF string: +# comb = secrets.hex2str(comb) +# console.log(comb === pw) // => true +``` ## Install it from PyPI diff --git a/gems/example1.py b/gems/example1.py new file mode 100755 index 0000000..c6c0050 --- /dev/null +++ b/gems/example1.py @@ -0,0 +1,38 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- +# vim: set et sw=4 fenc=utf-8: +# +# example1.py + +import json +import js2pysecrets + +# // generate a 512-bit key +# key = js2pysecrets.random(512) // => key is a hex string +key = js2pysecrets.random(512) +print(key) + +# // split into 10 shares with a threshold of 5 +# shares = js2pysecrets.share(key, 10, 5) +# // => shares = ['801xxx...xxx','802xxx...xxx','803xxx...xxx','804xxx...xxx','805xxx...xxx'] +shares = js2pysecrets.share(key, 10, 5) +print(shares) + +# // combine 4 shares +# var comb = secrets.combine(shares.slice(0, 4)) +# console.log(comb === key) // => false +# +# // combine 5 shares +# comb = secrets.combine(shares.slice(4, 9)) +# console.log(comb === key) // => true +# +# // combine ALL shares +# comb = secrets.combine(shares) +# console.log(comb === key) // => true +# +# // create another share with id 8 +# var newShare = secrets.newShare(8, shares) // => newShare = '808xxx...xxx' +# +# // reconstruct using 4 original shares and the new share: +# comb = secrets.combine(shares.slice(1, 5).concat(newShare)) +# console.log(comb === key) // => true \ No newline at end of file diff --git a/gems/example2.py b/gems/example2.py new file mode 100755 index 0000000..60fc018 --- /dev/null +++ b/gems/example2.py @@ -0,0 +1,36 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- +# vim: set et sw=4 fenc=utf-8: +# +# example1.py + +import json +import js2pysecrets + +# var pw = "<>" +pw = "<>" + +# // convert the text into a hex string +# var pwHex = secrets.str2hex(pw) // => hex string +jsHex = js2pysecrets.str2hex(pw) +print(jsHex) + +pyHex = pw.encode('utf-16-be').hex().lstrip('fe') +print(pyHex) + +# // split into 5 shares, with a threshold of 3 +# var shares = secrets.share(pwHex, 5, 3) +# +# // combine 2 shares: +# var comb = secrets.combine(shares.slice(1, 3)) +# +# //convert back to UTF string: +# comb = secrets.hex2str(comb) +# console.log(comb === pw) // => false +# +# // combine 3 shares: +# comb = secrets.combine([shares[1], shares[3], shares[4]]) +# +# //convert back to UTF string: +# comb = secrets.hex2str(comb) +# console.log(comb === pw) // => true \ No newline at end of file diff --git a/js2pysecrets/__init__.py b/js2pysecrets/__init__.py index b6c60ec..ccb1c74 100644 --- a/js2pysecrets/__init__.py +++ b/js2pysecrets/__init__.py @@ -8,6 +8,7 @@ from .base import random from .base import str2hex from .base import jsFunction +from .base import combine #with open("VERSION", "r") as version_file: # __version__ = version_file.read().strip() diff --git a/js2pysecrets/base.py b/js2pysecrets/base.py index e1cb42c..c7ec404 100644 --- a/js2pysecrets/base.py +++ b/js2pysecrets/base.py @@ -31,3 +31,4 @@ def __call__(self, *args, **kwargs): setRNG = jsFunction('setRNG') str2hex = jsFunction('str2hex') hex2str = jsFunction('hex2str') +combine = jsFunction('combine')