A common identity module
Project description
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 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:
- 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(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)
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2e4f79508d3dd1e29a60647173ebd2bba7a7a3d77f5dd5f86978f0742cc62c8a
|
|
| MD5 |
cef9526de43d64145832a0f02f3e2e7c
|
|
| BLAKE2b-256 |
1b15075b6fac10b3f27ec17a6206ee4893ba3a5b2de053034f4e63903d73bb73
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c5912b4987d72f96b3af77e9c88c920712da03446c68f3a32fa9429e3956d977
|
|
| MD5 |
7acb001e77760f12e462d0f25a6481c2
|
|
| BLAKE2b-256 |
1e0a6dcaad055388ebe4582b66eb008ec102248764bc46a1f0a3d0e528733527
|