This document introduces a common Account Module for decentralized user identity authentication.
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 = privateKey.Sign(seed)
MKM(Default)BTCExtended BTCETHExtended ETH- ...
A public key (PK) was bound to an ID by the Meta Algorithm.
A string as same as ID.name for generate the fingerprint.
THe fingerprint field was generated by your private key and seed:
data = UTF8Encode(seed)
fingerprint = privateKey.Sign(data)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].
type EntityType uint8
const (
/**
* Main: 0, 1
*/
USER EntityType = 0x00 // 0000 0000
GROUP EntityType = 0x01 // 0000 0001 (User Group)
/**
* Network: 2, 3
*/
STATION EntityType = 0x02 // 0000 0010 (Server Node)
ISP EntityType = 0x03 // 0000 0011 (Service Provider)
//STATION_GROUP EntityType = 0x03 // 0000 0011
/**
* Bot: 4, 5
*/
BOT EntityType = 0x04 // 0000 0100 (Business Node)
ICP EntityType = 0x05 // 0000 0101 (Content Provider)
//BOT_GROUP EntityType = 0x05 // 0000 0101
/*
* Management: 6, 7, 8
*/
//SUPERVISOR EntityType = 0x06 // 0000 0110 (Company CEO)
//COMPANY EntityType = 0x07 // 0000 0111 (Super Group for ISP/ICP)
//CA EntityType = 0x08 // 0000 1000 (Certification Authority)
/*
* Customized: 64, 65
*/
//APP_USER EntityType = 0x40 // 0100 0000 (Application Customized User)
//APP_GROUP EntityType = 0x41 // 0100 0001 (Application Customized Group)
/**
* Broadcast: 128, 129
*/
ANY EntityType = 0x80 // 1000 0000 (anyone@anywhere)
EVERY EntityType = 0x81 // 1000 0001 (everyone@everywhere)
)
func EntityTypeIsUser(networkType EntityType) bool {
return (networkType & GROUP) == USER
}
func EntityTypeIsGroup(networkType EntityType) bool {
return (networkType & GROUP) == GROUP
}
func EntityTypeIsBroadcast(networkType EntityType) bool {
return (networkType & ANY) == ANY
}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:
userName = "Albert.Moky"
groupTitle = "Group-9527"It's equivalent to meta.seed
The Address field was created with the Meta and a Network ID:
// BTCAddress implements the Address interface using Bitcoin-style address format.
//
// Format Structure (base58 encoded): "network+digest+checksum"
// network : 1 byte
// digest : 20 bytes
// checksum : 4 bytes
//
// Generation Algorithm:
// 1. fingerprint = sign(seed, SK)
// 2. digest = RIPEMD160(SHA256(fingerprint))
// 3. checksum = SHA256(SHA256(network + digest))[:4]
// 4. address = Base58Encode(network + digest + checksum)
type BTCAddress struct {
//Address
ConstantString
// network identifies the blockchain/entity type for this address
network EntityType
}
// NewBTCAddress creates a new BTCAddress instance with the given address string and network type
//
// Parameters:
// - address - Base58-encoded BTC address string
// - network - EntityType (blockchain network identifier)
//
// Returns: Pointer to initialized BTCAddress instance
func NewBTCAddress(address string, network EntityType) *BTCAddress {
return &BTCAddress{
ConstantString: *NewConstantString(address),
network: network,
}
}
// Override
func (address BTCAddress) Network() EntityType {
return address.network
}
// GenerateBTCAddress creates a valid BTCAddress from a fingerprint and network type
//
// # Follows standard Bitcoin address generation algorithm with double hashing and checksum
//
// Parameters:
// - fingerprint - Meta.fingerprint or PublicKey.data
// - network - EntityType (blockchain network identifier)
//
// Returns: Valid Address interface implementation (BTCAddress)
func GenerateBTCAddress(fingerprint []byte, network EntityType) Address {
// 1. digest = ripemd160(sha256(fingerprint))
digest := RIPEMD160(SHA256(fingerprint))
// 2. head = network + digest
head := make([]byte, 21)
head[0] = uint8(network)
BytesCopy(digest, 0, head, 1, 20)
// 3. cc = sha256(sha256(head)).prefix(4)
cc := checkCode(head)
// 4. data = base58_encode(head + cc)
data := make([]byte, 25)
BytesCopy(head, 0, data, 0, 21)
BytesCopy(cc, 0, data, 21, 4)
base58 := Base58Encode(data)
return NewBTCAddress(base58, network)
}
// ParseBTCAddress validates and parses a Base58 string into a BTCAddress
//
// # Performs length validation and checksum verification before creating address
//
// Parameters:
// - base58 - Base58-encoded BTC address string to parse
//
// Returns: Valid Address (BTCAddress) if parsing succeeds, nil if invalid
func ParseBTCAddress(base58 string) Address {
// decode
data := Base58Decode(base58)
if len(data) != 25 {
//panic("address length error")
return nil
}
// CheckCode
prefix := make([]byte, 21)
suffix := make([]byte, 4)
BytesCopy(data, 0, prefix, 0, 21)
BytesCopy(data, 21, suffix, 0, 4)
cc := checkCode(prefix)
// verify
if BytesEqual(cc, suffix) {
network := EntityType(data[0])
return NewBTCAddress(base58, network)
}
//panic("address check code error")
return nil
}
// checkCode computes the 4-byte checksum for BTC address validation
//
// # Implements double SHA256 hashing (SHA256(SHA256(data))) and returns first 4 bytes
//
// Parameters:
// - data - Byte slice to compute checksum for (network+digest for BTC addresses)
//
// Returns: 4-byte checksum slice
func checkCode(data []byte) []byte {
sha256d := SHA256(SHA256(data))
cc := make([]byte, 4)
BytesCopy(sha256d, 0, cc, 0, 4)
return cc
}// ETHAddress implements the Address interface using Ethereum-style address format.
//
// Format Structure:
// "0x{40-character hex string}" (case-insensitive with EIP-55 checksum)
//
// Generation Algorithm:
// 1. fingerprint = Public key data (PK.data)
// 2. digest = KECCAK256(fingerprint)
// 3. address = "0x" + EIP-55 checksummed hex of digest last 20 bytes
type ETHAddress struct {
//Address
ConstantString
}
// NewETHAddress creates a new ETHAddress instance with the given address string
//
// Parameters:
// - address - EIP-55 compliant ETH address string (0x + 40 hex chars)
//
// Returns: Pointer to initialized ETHAddress instance
func NewETHAddress(address string) *ETHAddress {
return ÐAddress{
ConstantString: *NewConstantString(address),
}
}
// Override
func (address ETHAddress) Network() EntityType {
return USER
}
// GenerateETHAddress creates a valid ETHAddress from a public key fingerprint
//
// # Follows Ethereum address generation standard (KECCAK256 hash of public key)
//
// # Implements EIP-55 checksum for case sensitivity validation
//
// Parameters:
// - fingerprint - Public key data (PK.data, 65 bytes with 0x04 prefix or 64 bytes raw)
//
// Returns: Valid Address interface implementation (ETHAddress)
func GenerateETHAddress(fingerprint []byte) Address {
if len(fingerprint) == 65 {
fingerprint = fingerprint[1:]
}
// 1. digest = keccak256(fingerprint);
digest := KECCAK256(fingerprint)
// 2. address = hex_encode(digest.suffix(20));
address := "0x" + eip55(HexEncode(digest[32-20:]))
return NewETHAddress(address)
}
// ParseETHAddress validates and parses a string into an ETHAddress
//
// # Checks format compliance (0x prefix, 42 total characters, valid hex chars)
//
// Parameters:
// - address - ETH address string to parse (0x + 40 hex chars)
//
// Returns: Valid Address (ETHAddress) if parsing succeeds, nil if invalid
func ParseETHAddress(address string) Address {
if isETH(address) {
return NewETHAddress(address)
}
return nil
}
// eip55 implements EIP-55 checksum for Ethereum addresses
//
// # Converts lowercase hex string to mixed-case checksum format
//
// Reference: https://eips.ethereum.org/EIPS/eip-55
//
// Parameters:
// - hex - 40-character lowercase hex string (without 0x prefix)
//
// Returns: EIP-55 checksummed 40-character hex string
func eip55(hex string) string {
sb := make([]byte, 40)
utf8 := UTF8Encode(hex)
hash := KECCAK256(utf8)
var ch byte
var i uint8
for i = 0; i < 40; i++ {
ch = utf8[i]
if ch > '9' {
// check for each 4 bits in the hash table
// if the first bit is '1',
// change the character to uppercase
ch -= (hash[i>>1] << (i << 2 & 4) & 0x80) >> 2
}
sb[i] = ch
}
return UTF8Decode(sb)
}
// isETH validates basic Ethereum address format
//
// # Checks: 42 characters total, 0x prefix, valid hex characters (0-9, A-F, a-f)
//
// Parameters:
// - address - ETH address string to validate
//
// Returns: true if address has valid basic format, false otherwise
func isETH(address string) bool {
if len(address) != 42 {
return false
}
if address[0] != '0' || address[1] != 'x' {
return false
}
var ch byte
for i := 2; i < 42; i++ {
ch = address[i]
if ch >= '0' && ch <= '9' {
continue
}
if ch >= 'A' && ch <= 'Z' {
continue
}
if ch >= 'a' && ch <= 'z' {
continue
}
// unexpected character
return false
}
return true
}
// GetValidateETHAddressString returns EIP-55 checksummed address from a valid basic ETH address
//
// # Converts valid address to lowercase then applies EIP-55 checksum
//
// Parameters:
// - address - Valid basic ETH address string (0x + 40 hex chars)
//
// Returns: EIP-55 checksummed address string, empty string if input is invalid
func GetValidateETHAddressString(address string) string {
if isETH(address) {
lower := strings.ToLower(address[2:])
return "0x" + eip55(lower)
}
return ""
}
// IsValidateETHAddressString checks if an address string is EIP-55 checksum compliant
//
// # Verifies both basic format and correct case checksum
//
// Parameters:
// - address - ETH address string to validate
//
// Returns: true if address is EIP-55 compliant, false otherwise
func IsValidateETHAddressString(address string) bool {
validate := GetValidateETHAddressString(address)
return validate == address
}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.
A resource identifier as Login Point.
ID examples
did1 = "hulk@4YeVEN3aUnvC1DNUufCq1bs9zoBSJTzVEj" // Immortal Hulk
did2 = "moki@4WDfe3zZ4T7opFSi3iDAKiuTnUHjxmXekk" // Monkey KingMeta 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)