Ming Ke Ming (名可名) -- Account Module (Python)
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
MKM(Default)BTCExtended BTCETHExtended ETH- ...
Public Key
A public key (PK) was bound 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:
- The length of name must more than 1 byte, less than 32 bytes;
- It should be composed by a-z, A-Z, 0-9, or charactors '_', '-', '.';
- 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.digest(SHA256.digest(fingerprint))
# 2. head = network + digest
head = bytes([network]) + 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 = data[0]
return cls(address=address, network=network)
def check_code(data: bytes) -> bytes:
# check code in BTC address
return SHA256.digest(SHA256.digest(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 error: %d' % len(fingerprint)
# 1. digest = keccak256(fingerprint)
digest = KECCAK256.digest(fingerprint)
# 2. address = hex_encode(digest.suffix(20))
tail = digest[-20:]
address = '0x' + eip55(address=Hex.encode(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.digest(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 encoded with BASE64 algorithm as default, excepts the address)
Release files for mkm 2.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mkm-2.5.0.tar.gz | 39.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mkm-2.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 109.6 kB
Release files / mkm-2.5.0.tar.gz
| Download URL | mkm-2.5.0.tar.gz |
|---|---|
| Size | 39.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1e8d8c44b1897bb351a9a3b9e7303681f24402cd35f550a1171c271ad205e111
|
|
BLAKE2b-256 checksum How to use checksums |
9965c3e0883598525e85d0bc28fc06acfea0077368a33d8e360ad221f49c2c82
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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
|
Release files / mkm-2.5.0-py3-none-any.whl
| Download URL | mkm-2.5.0-py3-none-any.whl |
|---|---|
| Size | 70.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b1693521853cf2cd56f756782b0e26c19a3bdae4a2cb0d327f40081b429e4b38
|
|
BLAKE2b-256 checksum How to use checksums |
46b639bb3bee1aa9caf13cc971bb30cd31061063f7e8c767f9a9685b9f74b7dc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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
|