Metadata-Version: 2.1
Name: mkm
Version: 2.3.2
Summary: A common identity module
Home-page: https://github.com/dimchat/mkm-py
Author: Albert Moky
Author-email: albert.moky@gmail.com
License: MIT
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Description-Content-Type: text/markdown
License-File: LICENSE

# Ming Ke Ming (名可名) -- Account Module (Python)

[![License](https://img.shields.io/github/license/dimchat/mkm-py)](https://github.com/dimchat/mkm-py/blob/master/LICENSE)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/dimchat/mkm-py/pulls)
[![Platform](https://img.shields.io/badge/Platform-Python%203-brightgreen.svg)](https://github.com/dimchat/mkm-py/wiki)
[![Issues](https://img.shields.io/github/issues/dimchat/mkm-py)](https://github.com/dimchat/mkm-py/issues)
[![Repo Size](https://img.shields.io/github/repo-size/dimchat/mkm-py)](https://github.com/dimchat/mkm-py/archive/refs/heads/main.zip)
[![Tags](https://img.shields.io/github/tag/dimchat/mkm-py)](https://github.com/dimchat/mkm-py/tags)
[![Version](https://img.shields.io/pypi/v/mkm)](https://pypi.org/project/mkm)

[![Watchers](https://img.shields.io/github/watchers/dimchat/mkm-py)](https://github.com/dimchat/mkm-py/watchers)
[![Forks](https://img.shields.io/github/forks/dimchat/mkm-py)](https://github.com/dimchat/mkm-py/forks)
[![Stars](https://img.shields.io/github/stars/dimchat/mkm-py)](https://github.com/dimchat/mkm-py/stargazers)
[![Followers](https://img.shields.io/github/followers/dimchat)](https://github.com/orgs/dimchat/followers)

This [document](https://github.com/moky/DIMP/blob/master/MingKeMing-Identity.md) introduces a common **Account Module** for decentralized user identity authentication.

## Meta

The **Meta** was generated by your **private key**, it can be used to build a new ID for entity, or verify the ID/PK pair.

It consists of 4 fields:

| Field       | Description                              |
| ----------- | ---------------------------------------- |
| type        | Algorithm Version                        |
| key         | Public Key                               |
| seed        | Entity Name (Optional)                   |
| fingerprint | Signature to generate address (Optional) |

1. if ```seed``` exists, ```fingerprint = private_key.sign(seed)```;
2. if ```seed``` not exists, ```fingerprint = public_key.data```.

### Meta Type

1. ```MKM``` _(Default)_
2. ```BTC```
3. ~~Extended BTC~~
4. ```ETH```
5. ~~Extended ETH~~
6. ...

### Meta Example
```javascript
/* Meta(JsON) for hulk@4YeVEN3aUnvC1DNUufCq1bs9zoBSJTzVEj */
{
    "type"        : 0x01,
    "key"         : {
        "algorithm" : "RSA",
        "data"      : "-----BEGIN PUBLIC KEY-----\nMIGJAoGBALB+vbUK48UU9rjlgnohQowME+3JtTb2hLPqtatVOW364/EKFq0/PSdnZVE9V2Zq+pbX7dj3nCS4pWnYf40ELH8wuDm0Tc4jQ70v4LgAcdy3JGTnWUGiCsY+0Z8kNzRkm3FJid592FL7ryzfvIzB9bjg8U2JqlyCVAyUYEnKv4lDAgMBAAE=\n-----END PUBLIC KEY-----",
        "mode"      : "ECB",
        "padding"   : "PKCS1",
        "digest"    : "SHA256"
    },
    "seed"        : "hulk",
    "fingerprint" : "jIPGWpWSbR/DQH6ol3t9DSFkYroVHQDvtbJErmFztMUP2DgRrRSNWuoKY5Y26qL38wfXJQXjYiWqNWKQmQe/gK8M8NkU7lRwm+2nh9wSBYV6Q4WXsCboKbnM0+HVn9Vdfp21hMMGrxTX1pBPRbi0567ZjNQC8ffdW2WvQSoec2I="
}
```

## ID
The **ID** is used to identify an **entity**(user/group). It consists of 3 fields:

| Field       | Description                    |
| ----------- | ------------------------------ |
| type        | Entity type                    |
| name        | Same with meta.seed (Optional) |
| address     | Unique Identification          |
| terminal    | Login point (Optional)         |

The ID format is ```name@address[/terminal]```.

```
# ID examples
ID1 = "hulk@4YeVEN3aUnvC1DNUufCq1bs9zoBSJTzVEj";  // Immortal Hulk
ID2 = "moki@4WDfe3zZ4T7opFSi3iDAKiuTnUHjxmXekk";  // Monkey King
```

### ID Name
The **Name** field is a username, or just a random string for group:

1. The length of name must more than 1 byte, less than 32 bytes;
2. It should be composed by a-z, A-Z, 0-9, or charactors '_', '-', '.';
3. It cannot contain key charactors('@', '/').

```
# Name examples
user_name  = "Albert.Moky";
group_name = "Group-9527";
```

It's equivalent to ```meta.seed```

### ID Address

The **Address** field was created with the Meta and a **Network ID**:

```python
from typing import Optional

from mkm.types import ConstantString
from mkm.digest import sha256, ripemd160
from mkm.format import base58_encode, base58_decode
from mkm import Address


class BTCAddress(ConstantString, Address):
    """
        Address like BitCoin
        ~~~~~~~~~~~~~~~~~~~~

        data format: "network+digest+code"
            network    --  1 byte
            digest     -- 20 bytes
            check code --  4 bytes

        algorithm:
            fingerprint = PK.data
            digest      = ripemd160(sha256(fingerprint));
            code        = sha256(sha256(network + digest)).prefix(4);
            address     = base58_encode(network + digest + code);
    """

    def __init__(self, address: str, network: int):
        super().__init__(string=address)
        self.__network = network

    @property  # Override
    def network(self) -> int:
        return self.__network

    @classmethod
    def from_data(cls, fingerprint: bytes, network: int) -> Address:
        """
        Generate address with fingerprint and network ID

        :param fingerprint: meta.fingerprint or key.data
        :param network:     address type
        :return: Address object
        """
        head = chr(network).encode('latin1')
        body = ripemd160(sha256(fingerprint))
        tail = check_code(head + body)
        address = base58_encode(head + body + tail)
        return cls(address=address, network=network)

    @classmethod
    def from_str(cls, address: str) -> Optional[Address]:
        """
        Parse a string for BTC address

        :param address: address string
        :return: Address object
        """
        if len(address) < 26 or len(address) > 35:
            return None
        # decode
        data = base58_decode(address)
        if data is None or len(data) != 25:
            return None
        # check code
        prefix = data[:21]
        suffix = data[21:]
        if check_code(prefix) == suffix:
            network = ord(data[:1])
            return cls(address=address, network=network)


def check_code(data: bytes) -> bytes:
    # check code in BTC address
    return sha256(sha256(data))[:4]
```

When you get a meta for the entity ID from the network,
you must verify it with the consensus algorithm before accepting its **public key**.

(All data encode with **BASE64** algorithm as default, excepts the **address**)

----

Copyright &copy; 2018 Albert Moky
[![Followers](https://img.shields.io/github/followers/moky)](https://github.com/moky?tab=followers)
