Skip to content

dimchat/mkm-dart

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

43 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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

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 = 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 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(seed);
fingerprint = privateKey.sign(data);

Meta Example

/* Meta(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="
}

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 examples
ID1 = "hulk@4YeVEN3aUnvC1DNUufCq1bs9zoBSJTzVEj";  // Immortal Hulk
ID2 = "moki@4WDfe3zZ4T7opFSi3iDAKiuTnUHjxmXekk";  // Monkey King

ID Type

class EntityType {

  ///  Main: 0, 1
  static const USER             = (0x00); // 0000 0000
  static const GROUP            = (0x01); // 0000 0001 (User Group)

  ///  Network: 2, 3
  static const STATION          = (0x02); // 0000 0010 (Server Node)
  static const ISP              = (0x03); // 0000 0011 (Service Provider)
  // static const STATION_GROUP = (0x03); // 0000 0011

  ///  Bot: 4, 5
  static const BOT              = (0x04); // 0000 0100 (Business Node)
  static const ICP              = (0x05); // 0000 0101 (Content Provider)
  // static const BOT_GROUP     = (0x05); // 0000 0101

  ///  Management: 6, 7, 8
  // static const SUPERVISOR    = (0x06); // 0000 0110 (Company CEO)
  // static const COMPANY       = (0x07); // 0000 0111 (Super Group for ISP/ICP)
  // static const CA            = (0x08); // 0000 1000 (Certification Authority)

  // ///  Customized: 64, 65
  // static const APP_USER      = (0x40); // 0100 0000 (Application Customized User)
  // static const APP_GROUP     = (0x41); // 0100 0001 (Application Customized Group)

  ///  Broadcast: 128, 129
  static const ANY              = (0x80); // 1000 0000 (anyone@anywhere)
  static const EVERY            = (0x81); // 1000 0001 (everyone@everywhere)


  static bool isUser(int network) {
    return network & GROUP == USER;
  }

  static bool isGroup(int network) {
    return network & GROUP == GROUP;
  }

  static bool isBroadcast(int network) {
    return network & 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
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

import 'dart:typed_data';

import 'package:mkm/type.dart';
import 'package:mkm/digest.dart';
import 'package:mkm/format.dart';
import 'package:mkm/protocol.dart';

///  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);
///
class BTCAddress extends ConstantString implements Address {
  BTCAddress(super.string, int network) : _type = network;

  final int _type;

  @override
  int get network => _type;


  ///  Generate BTC address with fingerprint and network ID
  ///
  /// @param fingerprint - meta.fingerprint or key.data
  /// @param network     - address type
  /// @return Address object
  static BTCAddress generate(Uint8List fingerprint, int network) {
    // 1. digest = ripemd160(sha256(fingerprint))
    Uint8List digest = RIPEMD160.digest(SHA256.digest(fingerprint));
    // 2. head = network + digest
    BytesBuilder bb = BytesBuilder(copy: false);
    bb.addByte(network);
    bb.add(digest);
    Uint8List head = bb.toBytes();
    // 3. cc = sha256(sha256(head)).prefix(4)
    Uint8List cc = _checkCode(head);
    // 4. data = base58_encode(head + cc)
    bb = BytesBuilder(copy: false);
    bb.add(head);
    bb.add(cc);
    return BTCAddress(Base58.encode(bb.toBytes()), network);
  }

  ///  Parse a string for BTC address
  ///
  /// @param address - address string
  /// @return null on error
  static BTCAddress? parse(String address) {
    if (address.length < 26 || address.length > 35) {
      return null;
    }
    // decode
    Uint8List? data = Base58.decode(address);
    if (data == null || data.length != 25) {
      return null;
    }
    // check code
    Uint8List prefix = data.sublist(0, 21);
    Uint8List suffix = data.sublist(21, 25);
    Uint8List cc = _checkCode(prefix);
    if (Arrays.equals(cc, suffix)) {
      return BTCAddress(address, data[0]);
    } else {
      return null;
    }
  }
}

Uint8List _checkCode(Uint8List data) {
  return SHA256.digest(SHA256.digest(data)).sublist(0, 4);
}

ETH Address

import 'dart:typed_data';

import 'package:mkm/type.dart';
import 'package:mkm/digest.dart';
import 'package:mkm/format.dart';
import 'package:mkm/protocol.dart';

///  Address like Ethereum
///  ~~~~~~~~~~~~~~~~~~~~~
///
///      data format: "0x{address}"
///
///      algorithm:
///          fingerprint = PK.data;
///          digest      = keccak256(fingerprint);
///          address     = hex_encode(digest.suffix(20));
///
class ETHAddress extends ConstantString implements Address {
  ETHAddress(super.string);

  @override
  int get network => EntityType.USER;

  static String? getValidateAddress(String address) {
    if (!_ETH.isETH(address)) {
      // not an ETH address
      return null;
    }
    String lower = address.substring(2).toLowerCase();
    String eip55 = _ETH.eip55(lower);
    return '0x$eip55';
  }

  static bool isValidate(String address) {
    String? validate = getValidateAddress(address);
    return validate != null && validate == address;
  }

  ///  Generate ETH address with key.data
  ///
  /// @param fingerprint = key.data
  /// @return Address object
  static ETHAddress generate(Uint8List fingerprint) {
    if (fingerprint.length == 65) {
      // skip first char
      fingerprint = fingerprint.sublist(1);
    }
    assert(fingerprint.length == 64, 'key data error: ${fingerprint.length}');
    // 1. digest = keccak256(fingerprint);
    Uint8List digest = KECCAK256.digest(fingerprint);
    // 2. address = hex_encode(digest.suffix(20));
    Uint8List tail = digest.sublist(digest.length - 20);
    String address = _ETH.eip55(Hex.encode(tail));
    return ETHAddress('0x$address');
  }

  ///  Parse a string for ETH address
  ///
  /// @param address - address string
  /// @return null on error
  static ETHAddress? parse(String address) {
    if (!_ETH.isETH(address)) {
      // not an ETH address
      return null;
    }
    return ETHAddress(address);
  }
}

class _ETH {

  // https://eips.ethereum.org/EIPS/eip-55
  static String eip55(String hex) {
    StringBuffer sb = StringBuffer();
    Uint8List hash = KECCAK256.digest(UTF8.encode(hex));
    int ch;
    for (int i = 0; i < 40; ++i) {
      ch = hex.codeUnitAt(i);
      if (ch > _c9) {
        // 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.writeCharCode(ch);
    }
    return sb.toString();
  }

  static bool isETH(String address) {
    if (address.length != 42) {
      return false;
    }
    if (address.codeUnitAt(0) != _c0 || address.codeUnitAt(1) != _cx) {
      return false;
    }
    int ch;
    for (int i = 2; i < 42; ++i) {
      ch = address.codeUnitAt(i);
      if (ch >= _c0 && ch <= _c9) {
        continue;
      }
      if (ch >= _cA && ch <= _cZ) {
        continue;
      }
      if (ch >= _ca && ch <= _cz) {
        continue;
      }
      // unexpected character
      return false;
    }
    return true;
  }

  static final int _c0 = '0'.codeUnitAt(0);
  static final int _c9 = '9'.codeUnitAt(0);
  static final int _cA = 'A'.codeUnitAt(0);
  static final int _cZ = 'Z'.codeUnitAt(0);
  static final int _ca = 'a'.codeUnitAt(0);
  static final int _cx = 'x'.codeUnitAt(0);
  static final int _cz = 'z'.codeUnitAt(0);
}

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.

(All data encode with BASE64 algorithm as default, excepts the address)


Copyright © 2023-2025 Albert Moky Followers

About

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

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages