Skip to content

Commit

Permalink
Add function call to verify keys in env
Browse files Browse the repository at this point in the history
  • Loading branch information
ross-spencer committed Jun 19, 2024
1 parent 841618e commit 8382520
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 3 deletions.
1 change: 1 addition & 0 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ ignore-patterns=
^(.+).html,
^(.+).htm,
^(.+).svg,
^(.+).env,
^\.,

ignore-paths=
Expand Down
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,36 @@ Outputs:
}
```

### Verification against a known set

Simple Sign provides enough for most use cases to receive a CIP-8 message,
check that it was signed, and then compare the signer's address against their
own known list of notaries.

To standardise the process Simple Sign will offer a number of helper functions.

#### Notaries in an environment variable

For a small number of notaries an environment variable may be sufficient. Use
`CIP8_NOTARIES=` with comma separated list of Cardano addresses before
invoking `signature_in_dapp_environment(pkey: str)` in your script.

```env
CIP8_NOTARIES=addr1...,addr2...,addr3...
```

Use `--list-env` or `-l` to display the contents of this variable locally:

```sh
python sign.py verify -
```

#### Other methods of checking signers

A number of stubs have been left in the code that might provide methods such
as checking against a UTxO or set of NFT holders in the future. These are yet
to be implemented.

## Developer install

### pip
Expand Down
3 changes: 3 additions & 0 deletions notaries.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Simple Signer Config File.

CIP8_NOTARIES=
54 changes: 51 additions & 3 deletions src/simple_sign/sign.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import argparse
import logging
import os
import sys
import time
from typing import Final
Expand Down Expand Up @@ -33,6 +34,13 @@
logger = logging.getLogger(__name__)


KNOWN_SIGNERS_CONFIG: Final[str] = "CIP8_NOTARIES"


class UnknownSigningKey(Exception):
"""Exception to raise when the signing key is unknown."""


def signature_in_license_pool():
"""Validate whether signing key matches one of those in a pool of
licenses associated with the project and return True if so.
Expand All @@ -47,11 +55,36 @@ def signature_in_constitution_datum_utxo():
raise NotImplementedError("reading from datum is not yet implemented")


def signature_in_constitution_config():
def signature_in_constitution_config(pkey: str) -> bool:
"""Validate whether signing key matches one of those listed in a
configuration file.
"""
raise NotImplementedError("reading from config is not yet implemented")
raise NotImplementedError(
"reading from a constitution config is not yet implemented"
)


def retrieve_env_notaries() -> list:
"""Retrieve notaries from the environment."""
notaries_env = os.getenv(KNOWN_SIGNERS_CONFIG, "")
if not notaries_env:
return []
return [notary.strip() for notary in notaries_env.split(",")]


def signature_in_dapp_environment(pkey: str) -> bool:
"""Validate whether signing key matches one of those configured in
the environment of the dApp.
Largely a method for early prototyping. This isn't the most secure
approach to doing this and especially not for use in decentralized
systems. This check is only for projects with complete control over
their own project.
"""
notaries = retrieve_env_notaries()
if pkey.strip() not in notaries:
raise UnknownSigningKey(f"{pkey} is an unknown key")
return True


def sign_with_key(data: str, signing_key: str) -> str:
Expand Down Expand Up @@ -117,6 +150,12 @@ def main() -> None:
sign = subparsers.add_parser(arg_sign)
subparsers.add_parser(arg_version)
verify.add_argument("-d", "--data", type=str, help="data to verify")
verify.add_argument(
"-l",
"--list-env",
action="store_true",
help=f"list known notaries in the environment at {KNOWN_SIGNERS_CONFIG}",
)
sign.add_argument("-d", "--data", type=str, help="data to sign")
sign.add_argument("-s", "--signing_key", type=str, help="signing key")
args = parser.parse_args()
Expand All @@ -125,10 +164,19 @@ def main() -> None:
sys.exit()
if args.cmd == arg_sign:
print(signing_handler(args.data, args.signing_key))
if args.cmd == arg_verify:
if args.cmd == arg_verify and not args.list_env:
print(verify_handler(args.data))
if args.cmd == arg_version:
print(f"simple-sign version: {get_version()}")
if args.list_env:
notaries = retrieve_env_notaries()
if not notaries:
logger.info(
"no environment notaries, ensuere '%s' is configured",
KNOWN_SIGNERS_CONFIG,
)
sys.exit()
print(notaries)


if __name__ == "__main__":
Expand Down
45 changes: 45 additions & 0 deletions tests/test_verify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""Test simple verify functions."""

import os

import pytest

from src.simple_sign.sign import (
UnknownSigningKey,
signature_in_dapp_environment,
verify_signature,
)


def test_env_config():
"""Test the most rudimentary way of checkingg a an address from
the caller's environment.
"""

os.environ["CIP8_NOTARIES"] = "123, 234"
assert signature_in_dapp_environment("123")
assert signature_in_dapp_environment("234")
with pytest.raises(UnknownSigningKey):
assert signature_in_dapp_environment("badf00d")

longer_list = [
"addr1v9gpw0tqt6e0kcjl30fg8e3tn0s3f678syx30nj2lc2lv6qwz6fdp",
"addr1vy0djq7lzjqnr2379uvevc88whrxrssnhfk0tvjka8u920gu2xdpf",
"addr1v9ykeuezg4h2688lkrgk8zkzmjjfw2ca26wczu68cpnct4cpv5ch2",
"addr1v956lwqjnzl4tejar4xw9yw0a2tr8qmdudtlfw86mjtynkggktuht",
"addr1v8lc4gtrnk85gd2lzyj69rs3k2yzt3la2hsn0lu2e3dyphsmg3nn6",
"addr1v9zcmugmdm43fclwrz7up5lm3ckzch7n2hs5n5azr5aqvfcxmstta",
]

os.environ["CIP8_NOTARIES"] = ",".join(longer_list)
assert signature_in_dapp_environment(
"addr1v956lwqjnzl4tejar4xw9yw0a2tr8qmdudtlfw86mjtynkggktuht"
)
with pytest.raises(UnknownSigningKey):
signature_in_dapp_environment(
"addr1v956lwqjnzl4tejar4xw9yw0a2tr8qmdudtlfw86mjtynkbadf00d"
)

cbor_data = "84584da301276761646472657373581d61458df11b6eeb14e3ee18bdc0d3fb8e2c2c5fd355e149d3a21d3a0627045820f9d8454265d02f54c7f658972189f6278a81c87762e911665603834bee9d31b6a166686173686564f45902957b22626c6f636b5f686569676874223a203130393731313737302c2022736f75726365223a20224d696e53776170222c202261646472657373223a202261646472317a38736e7a376334393734767a647078753635727570686c337a6a64767478773873747266326332746d716e787a6636673838326e3673613267786e6b34326865617675377564646c356a646c30656b746635663230346d6d63377333796b756639222c20226665656473223a205b7b2266656564223a20224144412d46414354222c20227574786f223a2022303034633434616437663239306162376534643265333639333064343732623165636131623937613665353938303532653938313237383833643334336136382330222c2022616d6f756e7473223a207b226c6f76656c616365223a203333313430353933353035342c202261333933313639316635633465363564303163343239653437336430646432346335316166646236646166383865363332613663316535312e36663732363336363631373837343666366236353665223a20393134383533363634363231367d7d2c207b2266656564223a20224144412d574d54222c20227574786f223a2022353937343335313130643334313833646266656238626631336234323332343632393466633132666331393963623165393831353937643033666166663264342330222c2022616d6f756e7473223a207b226c6f76656c616365223a20333134343035393434323831302c202231643766333362643233643835653161323564383764383666616334663139396333313937613266376166656236363261306633346531652e3737366637323663363436643666363236393663363537343666366236353665223a20393630313935343835333734337d7d5d7d58407c04f01d0cabe082eba26b5755e3d5dc6f815676fea67b4747c3ee572d7235cc3f2813fde7358af7f8b7588e81454070372d31b6609e30666ef602fa96d41e09"
verification = verify_signature(cbor_data)
assert signature_in_dapp_environment(verification["signing_address"])

0 comments on commit 8382520

Please sign in to comment.