Skip to content

Commit

Permalink
Merge pull request #34 from sicpa-dlab/main
Browse files Browse the repository at this point in the history
release 0.4.0
  • Loading branch information
andkononykhin authored Oct 15, 2021
2 parents 58c8c15 + c634618 commit b1ab788
Show file tree
Hide file tree
Showing 14 changed files with 108 additions and 78 deletions.
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ name: release

on:
push:
branches: [stable]
workflow_dispatch:
inputs:
devN:
Expand Down
36 changes: 24 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
# Peer DID python API
# Peerdid Python

This is an implementation of the [Peer DID method specification](https://identity.foundation/peer-did-method-spec/).
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![Unit Tests](https://github.com/sicpa-dlab/peer-did-python/workflows/verify/badge.svg)](https://github.com/sicpa-dlab/peer-did-python/actions/workflows/verify.yml)
[![Python Package](https://img.shields.io/pypi/v/peerdid)](https://pypi.org/project/peerdid/)

This version of API implements
only [static layers of support (1, 2a, 2b)](https://identity.foundation/peer-did-method-spec/#layers-of-support).
Implementation of the [Peer DID method specification](https://identity.foundation/peer-did-method-spec/) in Python.

Implements [static layers of support (1, 2a, 2b)](https://identity.foundation/peer-did-method-spec/#layers-of-support) only.

## Installation
```
pip install peerdid
```

## DIDComm + peerdid Demo
See https://github.com/sicpa-dlab/didcomm-demo.

## Example

Expand All @@ -12,14 +23,14 @@ Example code:
encryption_keys = [
VerificationMaterialAgreement(
type=VerificationMethodTypeAgreement.X25519_KEY_AGREEMENT_KEY_2019,
format=VerificationMaterialFormat.BASE58,
format=VerificationMaterialFormatPeerDID.BASE58,
value="DmgBSHMqaZiYqwNMEJJuxWzsGGC8jUYADrfSdBrC6L8s",
)
]
signing_keys = [
VerificationMaterialAuthentication(
type=VerificationMethodTypeAuthentication.ED25519_VERIFICATION_KEY_2018,
format=VerificationMaterialFormat.BASE58,
format=VerificationMaterialFormatPeerDID.BASE58,
value="ByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7",
)
]
Expand Down Expand Up @@ -54,7 +65,7 @@ Example of DID documents:
"id": "did:peer:0z6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V#6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V",
"type": "Ed25519VerificationKey2020",
"controller": "did:peer:0z6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V",
"publicKeyMultibase": "zByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7"
"publicKeyMultibase": "z6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V"
}
]
}
Expand All @@ -67,21 +78,21 @@ Example of DID documents:
"id": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0#6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V",
"type": "Ed25519VerificationKey2020",
"controller": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0",
"publicKeyMultibase": "zByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7"
"publicKeyMultibase": "z6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V"
},
{
"id": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0#6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg",
"type": "Ed25519VerificationKey2020",
"controller": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0",
"publicKeyMultibase": "z3M5RCDjPTWPkKSN3sxUmmMqHbmRPegYP1tjcKyrDbt9J"
"publicKeyMultibase": "z6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg"
}
],
"keyAgreement": [
{
"id": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0#6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc",
"type": "X25519KeyAgreementKey2020",
"controller": "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0",
"publicKeyMultibase": "zJhNWeSVLMYccCk7iopQW4guaSJTojqpMEELgSLhKwRr"
"publicKeyMultibase": "z6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc"
}
],
"service": [
Expand All @@ -100,8 +111,9 @@ Example of DID documents:
}

## Assumptions and limitations
- Only `X25519` keys are support for key agreement
- Only `Ed25519` keys are support for authentication
- Only static layers [1, 2a, 2b](https://identity.foundation/peer-did-method-spec/#layers-of-support) are supported
- Only `X25519` keys are supported for key agreement
- Only `Ed25519` keys are supported for authentication
- Supported verification materials (input and in the resolved DID DOC):
- [Default] 2020 verification materials (`Ed25519VerificationKey2020` and `X25519KeyAgreementKey2020`) with multibase base58 (`publicKeyMultibase`) public key encoding.
- JWK (`JsonWebKey2020`) using JWK (`publicKeyJwk`) public key encoding
Expand Down
2 changes: 1 addition & 1 deletion peerdid/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.3.0"
__version__ = "0.4.0"
26 changes: 14 additions & 12 deletions peerdid/core/did_doc_types.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from enum import Enum
from typing import List, Optional, Dict, Union, Tuple
from typing import List, Dict, Union, Tuple

from peerdid.core.jwk_okp import get_verification_method_type
from peerdid.errors import MalformedPeerDIDDocError
from peerdid.types import (
VerificationMethodType,
VerificationMethodTypePeerDID,
VerificationMethodTypeAuthentication,
VerificationMethodTypeAgreement,
VerificationMaterial,
VerificationMaterialPeerDID,
VerificationMaterialFormatPeerDID,
VerificationMaterialAuthentication,
VerificationMaterialAgreement,
Expand All @@ -29,7 +29,9 @@ class VerificationMethodField(Enum):


class VerificationMethodPeerDID:
def __init__(self, id: str, controller: str, ver_material: VerificationMaterial):
def __init__(
self, id: str, controller: str, ver_material: VerificationMaterialPeerDID
):
self.id = id
self.controller = controller
self.ver_material = ver_material
Expand Down Expand Up @@ -58,7 +60,7 @@ def public_key_field(self):

@staticmethod
def _get_public_key_format(
ver_method_type: VerificationMethodType,
ver_method_type: VerificationMethodTypePeerDID,
) -> Tuple[VerificationMaterialFormatPeerDID, VerificationMethodField]:
if (
ver_method_type
Expand Down Expand Up @@ -99,7 +101,7 @@ def _get_public_key_format(
raise ValueError("Unsupported verification method type " + str(ver_method_type))

@staticmethod
def _get_ver_method_type(value: dict) -> VerificationMethodType:
def _get_ver_method_type(value: dict) -> VerificationMethodTypePeerDID:
ver_method_type_str = value["type"]
if (
ver_method_type_str
Expand Down Expand Up @@ -170,9 +172,9 @@ class DIDCommServicePeerDID:
def __init__(
self,
id: str,
service_endpoint: Optional[str],
routing_keys: Optional[List[str]],
accept: Optional[List[str]],
service_endpoint: str,
routing_keys: List[str],
accept: List[str],
) -> None:
self.id = id
self.service_endpoint = service_endpoint
Expand Down Expand Up @@ -211,9 +213,9 @@ def from_dict(cls, value: dict):

return cls(
id=value[SERVICE_ID],
service_endpoint=value.get(SERVICE_ENDPOINT, None),
routing_keys=value.get(SERVICE_ROUTING_KEYS, None),
accept=value.get(SERVICE_ACCEPT, None),
service_endpoint=value.get(SERVICE_ENDPOINT, ""),
routing_keys=value.get(SERVICE_ROUTING_KEYS, []),
accept=value.get(SERVICE_ACCEPT, []),
)


Expand Down
12 changes: 7 additions & 5 deletions peerdid/core/jwk_okp.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@
from peerdid.core.utils import urlsafe_b64encode, urlsafe_b64decode
from peerdid.errors import MalformedPeerDIDDocError
from peerdid.types import (
VerificationMethodType,
VerificationMethodTypePeerDID,
VerificationMethodTypeAgreement,
VerificationMethodTypeAuthentication,
VerificationMaterial,
VerificationMaterialPeerDID,
VerificationMaterialAuthentication,
VerificationMaterialAgreement,
)


def public_key_to_jwk_dict(public_key: bytes, ver_method_type: VerificationMethodType):
def public_key_to_jwk_dict(
public_key: bytes, ver_method_type: VerificationMethodTypePeerDID
):
x = urlsafe_b64encode(public_key).decode("utf-8")

if ver_method_type == VerificationMethodTypeAgreement.JSON_WEB_KEY_2020:
Expand All @@ -29,7 +31,7 @@ def public_key_to_jwk_dict(public_key: bytes, ver_method_type: VerificationMetho
}


def jwk_key_to_bytes(ver_material: VerificationMaterial) -> bytes:
def jwk_key_to_bytes(ver_material: VerificationMaterialPeerDID) -> bytes:
jwk_dict = (
json.loads(ver_material.value)
if isinstance(ver_material.value, str)
Expand Down Expand Up @@ -58,7 +60,7 @@ def jwk_key_to_bytes(ver_material: VerificationMaterial) -> bytes:
return urlsafe_b64decode(value.encode())


def get_verification_method_type(jwk_dict: dict) -> VerificationMethodType:
def get_verification_method_type(jwk_dict: dict) -> VerificationMethodTypePeerDID:
if "crv" not in jwk_dict:
raise MalformedPeerDIDDocError("No 'crv' field in JWK {}".format(jwk_dict))
crv = jwk_dict["crv"]
Expand Down
8 changes: 5 additions & 3 deletions peerdid/core/multibase.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,20 @@ def to_base58(value: bytes) -> str:

def from_base58_multibase(multibase: str) -> Tuple[str, bytes]:
if not multibase:
raise ValueError("No transform part in multibase encoding")
raise ValueError("Invalid key: No transform part in multibase encoding")
transform = multibase[0]
if not transform == MultibasePrefix.BASE58.value:
raise ValueError("Unsupported transform part of peer_did: " + transform)
raise ValueError(
"Invalid key: Unsupported transform part of peer_did: " + transform
)
encnumbasis = multibase[1:]
decoded = from_base58(encnumbasis)
return encnumbasis, decoded


def from_base58(base58encoded: str) -> bytes:
if not is_base58(base58encoded):
raise ValueError("Invalid base58 encoding: " + base58encoded)
raise ValueError("Invalid key: Invalid base58 encoding: " + base58encoded)
decoded = base58.b58decode(base58encoded)
return decoded

Expand Down
16 changes: 10 additions & 6 deletions peerdid/core/multicodec.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@

import varint

from peerdid.types import VerificationMethodType, VerificationMethodTypeAgreement
from peerdid.types import VerificationMethodTypePeerDID, VerificationMethodTypeAgreement


class Codec(Enum):
X25519 = 0xEC
ED25519 = 0xED


def to_multicodec(value: bytes, key_type: VerificationMethodType) -> bytes:
def to_multicodec(value: bytes, key_type: VerificationMethodTypePeerDID) -> bytes:
codec = _get_codec(key_type)
prefix = varint.encode(codec.value)
return b"".join([prefix, value])
Expand All @@ -20,21 +20,25 @@ def to_multicodec(value: bytes, key_type: VerificationMethodType) -> bytes:
def from_multicodec(value: bytes) -> Tuple[bytes, Codec]:
try:
prefix_int = varint.decode_bytes(value)
except TypeError:
raise ValueError("Invalid multicodec prefix in {}".format(str(value)))
except Exception:
raise ValueError(
"Invalid key: Invalid multicodec prefix in {}".format(str(value))
)

try:
codec = Codec(prefix_int)
except ValueError:
raise ValueError(
"Unknown multicodec prefix {} in {}".format(str(prefix_int), str(value))
"Invalid key: Unknown multicodec prefix {} in {}".format(
str(prefix_int), str(value)
)
)

prefix = varint.encode(prefix_int)
return value[len(prefix) :], codec


def _get_codec(key_type: VerificationMethodType) -> Codec:
def _get_codec(key_type: VerificationMethodTypePeerDID) -> Codec:
if isinstance(key_type, VerificationMethodTypeAgreement):
return Codec.X25519
else:
Expand Down
15 changes: 10 additions & 5 deletions peerdid/core/peer_did_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from peerdid.types import (
JSON,
PEER_DID,
VerificationMaterial,
VerificationMaterialPeerDID,
VerificationMaterialFormatPeerDID,
VerificationMaterialAgreement,
VerificationMaterialAuthentication,
Expand Down Expand Up @@ -111,7 +111,7 @@ def decode_service(service: str, peer_did: PEER_DID) -> Optional[List[dict]]:
return list_of_service_dict


def create_multibase_encnumbasis(key: VerificationMaterial) -> str:
def create_multibase_encnumbasis(key: VerificationMaterialPeerDID) -> str:
"""
Creates multibased encnumbasis according to Peer DID spec
(https://identity.foundation/peer-did-method-spec/index.html#method-specific-identifier)
Expand All @@ -121,7 +121,7 @@ def create_multibase_encnumbasis(key: VerificationMaterial) -> str:
if key.format == VerificationMaterialFormatPeerDID.BASE58:
decoded_key = from_base58(key.value)
elif key.format == VerificationMaterialFormatPeerDID.MULTIBASE:
decoded_key = from_base58_multibase(key.value)[1]
decoded_key = from_multicodec(from_base58_multibase(key.value)[1])[0]
elif key.format == VerificationMaterialFormatPeerDID.JWK:
decoded_key = jwk_key_to_bytes(key)
else:
Expand All @@ -134,7 +134,7 @@ def create_multibase_encnumbasis(key: VerificationMaterial) -> str:
"DecodedEncnumbasis",
[
("encnumbasis", str),
("ver_material", VerificationMaterial),
("ver_material", VerificationMaterialPeerDID),
],
)

Expand Down Expand Up @@ -168,7 +168,12 @@ def decode_multibase_encnumbasis(
ver_material = ver_material_cls(
format=ver_material_format,
type=__get_2020_ver_material_type(codec),
value=to_base58_multibase(decoded_encnumbasis_without_prefix),
value=to_base58_multibase(
to_multicodec(
decoded_encnumbasis_without_prefix,
__get_2020_ver_material_type(codec),
),
),
)
elif ver_material_format == VerificationMaterialFormatPeerDID.JWK:
ver_material_type = __get_jwk_ver_material_type(codec)
Expand Down
4 changes: 2 additions & 2 deletions peerdid/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def values(cls):
return [e.value for e in cls]


VerificationMethodType = Union[
VerificationMethodTypePeerDID = Union[
VerificationMethodTypeAgreement, VerificationMethodTypeAuthentication
]

Expand All @@ -51,7 +51,7 @@ def values(cls):
)


VerificationMaterial = Union[
VerificationMaterialPeerDID = Union[
VerificationMaterialAuthentication, VerificationMaterialAgreement
]

Expand Down
10 changes: 6 additions & 4 deletions tests/test_create_peer_did_numalgo_0.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
),
pytest.param(
VerificationMaterialAuthentication(
value="zByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7",
value="z6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V",
type=VerificationMethodTypeAuthentication.ED25519_VERIFICATION_KEY_2020,
format=VerificationMaterialFormatPeerDID.MULTIBASE,
),
Expand Down Expand Up @@ -89,7 +89,7 @@ def test_create_numalgo_0_positive(ver_material):
],
)
def test_create_numalgo_0_malformed_inception_key_not_base58_encoded(ver_material):
with pytest.raises(ValueError, match=r"Invalid base58 encoding"):
with pytest.raises(ValueError, match=r"Invalid key: Invalid base58 encoding"):
create_peer_did_numalgo_0(inception_key=ver_material)


Expand Down Expand Up @@ -219,7 +219,9 @@ def test_create_numalgo_0_malformed_empty_inception_key_multibase():
type=VerificationMethodTypeAuthentication.ED25519_VERIFICATION_KEY_2020,
format=VerificationMaterialFormatPeerDID.MULTIBASE,
)
with pytest.raises(ValueError, match=r"No transform part in multibase encoding"):
with pytest.raises(
ValueError, match=r"Invalid key: No transform part in multibase encoding"
):
create_peer_did_numalgo_0(inception_key=ver_material)


Expand All @@ -236,7 +238,7 @@ def test_create_numalgo_0_malformed_empty_inception_key_multibase():
),
pytest.param(
VerificationMaterialAgreement(
value="zByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7",
value="z6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V",
type=VerificationMethodTypeAgreement.X25519_KEY_AGREEMENT_KEY_2020,
format=VerificationMaterialFormatPeerDID.MULTIBASE,
),
Expand Down
Loading

0 comments on commit b1ab788

Please sign in to comment.