Skip to content

dimchat/mkm-go

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

85 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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

License PRs Welcome Platform Issues Repo Size Tags

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 = privateKey.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 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 = UTF8Encode(seed)
fingerprint = privateKey.Sign(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

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
}

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:

userName = "Albert.Moky"
groupTitle = "Group-9527"

It's equivalent to meta.seed

ID Address

The Address field was created with the Meta and a Network ID:

BTC Address

// 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
}

ETH Address

// 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 &ETHAddress{
		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.

Terminal

A resource identifier as Login Point.

Samples

ID examples

did1 = "hulk@4YeVEN3aUnvC1DNUufCq1bs9zoBSJTzVEj"  // Immortal Hulk
did2 = "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)


Copyright © 2020-2026 Albert Moky Followers

About

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

Resources

License

Stars

0 stars

Watchers

2 watching

Forks

Packages

 
 
 

Contributors

Languages