Skip to main content

A common identity module

Project description

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

Project details


Release history Release notifications | RSS feed

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.0.tar.gz (31.0 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.0-py3-none-any.whl (59.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: mkm-2.4.0.tar.gz
  • Upload date:
  • Size: 31.0 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.0.tar.gz
Algorithm Hash digest
SHA256 2e4f79508d3dd1e29a60647173ebd2bba7a7a3d77f5dd5f86978f0742cc62c8a
MD5 cef9526de43d64145832a0f02f3e2e7c
BLAKE2b-256 1b15075b6fac10b3f27ec17a6206ee4893ba3a5b2de053034f4e63903d73bb73

See more details on using hashes here.

File details

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

File metadata

  • Download URL: mkm-2.4.0-py3-none-any.whl
  • Upload date:
  • Size: 59.4 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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c5912b4987d72f96b3af77e9c88c920712da03446c68f3a32fa9429e3956d977
MD5 7acb001e77760f12e462d0f25a6481c2
BLAKE2b-256 1e0a6dcaad055388ebe4582b66eb008ec102248764bc46a1f0a3d0e528733527

See more details on using hashes here.

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