Skip to main content

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

License PRs Welcome Platform Issues Repo Size Tags Version

Watchers Forks Stars Followers

This document introduces a common Account Module for decentralized user identity authentication.

Features

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)

If seed exists, fingerprint = private_key.sign(seed)

Meta Type

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

Public Key

A public key (PK) was binded to an ID by the Meta Algorithm.

Seed

A string as same as ID.name for generate the fingerprint.

Fingerprint

THe fingerprint field was generated by your private key and seed:

data = utf8_encode(string=seed);
fingerprint = private_key.sign(data=data);

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 Type

class EntityType(IntEnum):
    
    ################################
    #  Main: 0, 1
    ################################
    USER = 0x00             # 0000 0000
    GROUP = 0x01            # 0000 0001 (User Group)

    ################################
    #  Network: 2, 3
    ################################
    STATION = 0x02          # 0000 0010 (Server Node)
    ISP = 0x03              # 0000 0011 (Service Provider)
    # STATION_GROUP = 0x03  # 0000 0011

    ################################
    #  Bot: 4, 5
    ################################
    BOT = 0x04              # 0000 0100 (Business Node)
    ICP = 0x05              # 0000 0101 (Content Provider)
    # BOT_GROUP = 0x05      # 0000 0101

    ################################
    #  Management: 6, 7, 8
    ################################
    # SUPERVISOR = 0x06     # 0000 0110 (Company CEO)
    # COMPANY = 0x07        # 0000 0111 (Super Group for ISP/ICP)
    # CA = 0x08             # 0000 1000 (Certification Authority)

    ################################
    #  Customized: 64, 65
    ################################
    # APP_USER = 0x40       # 0100 0000 (Application Customized User)
    # APP_GROUP = 0x41      # 0100 0001 (Application Customized Group)

    ################################
    #  Broadcast: 128, 129
    ################################
    ANY = 0x80              # 1000 0000 (anyone@anywhere)
    EVERY = 0x81            # 1000 0001 (everyone@everywhere)

    @classmethod
    def is_user(cls, network: int) -> bool:
        return (network & cls.GROUP) == cls.USER

    @classmethod
    def is_group(cls, network: int) -> bool:
        return (network & cls.GROUP) == cls.GROUP

    @classmethod
    def is_broadcast(cls, network: int) -> bool:
        return (network & cls.ANY) == cls.ANY

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:

BTC Address

from typing import Optional

from mkm import *


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.__type = network

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

    #
    #   Factory methods
    #
    @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
        """
        # 1. digest = ripemd160(sha256(fingerprint))
        digest = ripemd160(sha256(fingerprint))
        # 2. head = network + digest
        head = chr(network).encode('latin1') + digest
        # 3. cc = sha256(sha256(head)).prefix(4)
        code = check_code(head)
        # 4. data = base58_encode(head + cc)
        address = base58_encode(head + code)
        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]

ETH Address

from typing import Optional

from mkm import *


class ETHAddress(ConstantString, Address):
    """
        Address like Ethereum
        ~~~~~~~~~~~~~~~~~~~~~

        data format: "0x{address}"

        algorithm:
            fingerprint = PK.data
            digest      = keccak256(fingerprint)
            address     = hex_encode(digest.suffix(20))
    """

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

    @property  # Override
    def network(self) -> int:
        return EntityType.USER.value

    @classmethod
    def validate_address(cls, address: str) -> Optional[str]:
        if is_eth(address=address):
            lower = address[2:].lower()
            return '0x%s' % eip55(address=lower)
        # not an ETH address

    @classmethod
    def is_validate(cls, address: str) -> bool:
        validate = cls.validate_address(address=address)
        return validate is not None and validate == address

    #
    #   Factory methods
    #
    @classmethod
    def from_data(cls, fingerprint: bytes) -> Address:
        """
        Generate ETH address with key.data

        :param fingerprint: key.data
        :return: Address object
        """
        if len(fingerprint) == 65:
            # skip first char
            fingerprint = fingerprint[1:]
        assert len(fingerprint) == 64, 'key data length error: %d' % len(fingerprint)
        # 1. digest = keccak256(fingerprint)
        digest = keccak256(data=fingerprint)
        # 2. address = hex_encode(digest.suffix(20))
        tail = digest[-20:]
        address = '0x' + eip55(address=hex_encode(data=tail))
        return cls(address=address)

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

        :param address: address string
        :return: Address object
        """
        if is_eth(address=address):
            return cls(address=address)


# https://eips.ethereum.org/EIPS/eip-55
def eip55(address: str) -> str:
    res = ''
    table = keccak256(address.encode('utf-8'))
    for i in range(40):
        ch = address[i]
        x = ord(ch)
        if x > 0x39:
            # check for each 4 bits in the hash table
            # if the first bit is '1',
            #     change the character to uppercase
            x -= (table[i >> 1] << (i << 2 & 4) & 0x80) >> 2
            ch = chr(x)
        res += ch
    return res


def is_eth(address: str) -> bool:
    if len(address) != 42:
        return False
    if address[0] != '0' or address[1] != 'x':
        return False
    for i in range(2, 42):
        ch = address[i]
        if '0' <= ch <= '9':
            continue
        if 'A' <= ch <= 'Z':
            continue
        if 'a' <= ch <= 'z':
            continue
        # unexpected character
        return False
    return True

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.

Terminal

A resource identifier as Login Point.

Samples

ID examples

ID1 = "hulk@4YeVEN3aUnvC1DNUufCq1bs9zoBSJTzVEj"  # Immortal Hulk
ID2 = "moki@4WDfe3zZ4T7opFSi3iDAKiuTnUHjxmXekk"  # Monkey King

Meta Example (JsON) for hulk@4YeVEN3aUnvC1DNUufCq1bs9zoBSJTzVEj

{
    "type"        : "1",
    "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="
}

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


Copyright © 2018-2026 Albert Moky Followers

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mkm-2.4.3.tar.gz (33.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mkm-2.4.3-py3-none-any.whl (65.0 kB view details)

Uploaded Python 3

File details

Details for the file mkm-2.4.3.tar.gz.

File metadata

  • Download URL: mkm-2.4.3.tar.gz
  • Upload date:
  • Size: 33.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/68.0.0 requests-toolbelt/0.9.1 tqdm/4.32.2 CPython/3.7.0b3

File hashes

Hashes for mkm-2.4.3.tar.gz
Algorithm Hash digest
SHA256 ae2d1b5b6aa8bc1d7051a7a01a9dc1d61c85161b38daeb2167d6bd515d56a6f0
MD5 e13887a08bcfad93569ee010ebcf2343
BLAKE2b-256 b844ace569296489e45c78f19e901dd655c91f282e362422c9060fc8ab6c0358

See more details on using hashes here.

File details

Details for the file mkm-2.4.3-py3-none-any.whl.

File metadata

  • Download URL: mkm-2.4.3-py3-none-any.whl
  • Upload date:
  • Size: 65.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/68.0.0 requests-toolbelt/0.9.1 tqdm/4.32.2 CPython/3.7.0b3

File hashes

Hashes for mkm-2.4.3-py3-none-any.whl
Algorithm Hash digest
SHA256 7ba3826bc4d6f47498e2892507f012749aabd063cd728d1a39101f5a7052d9c5
MD5 422388f88ce39007675cac65cf06bf78
BLAKE2b-256 395efaee75fc828759fa7857305fac606d9a0e3723e39af9c04ecabfcab32eb7

See more details on using hashes here.

Release history Release notifications | RSS feed

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page