{"id":32307845,"url":"https://github.com/dimchat/mkm-dart","last_synced_at":"2026-02-21T16:03:08.336Z","repository":{"id":160994822,"uuid":"622171977","full_name":"dimchat/mkm-dart","owner":"dimchat","description":"Ming Ke Ming (名可名) -- Account Module (dart)","archived":false,"fork":false,"pushed_at":"2026-01-02T18:04:15.000Z","size":306,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-01-09T03:28:41.507Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dimchat.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-04-01T10:36:27.000Z","updated_at":"2026-01-02T18:01:38.000Z","dependencies_parsed_at":null,"dependency_job_id":"4c68809a-6b01-4890-aab4-c9eaeb73cba5","html_url":"https://github.com/dimchat/mkm-dart","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/dimchat/mkm-dart","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimchat%2Fmkm-dart","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimchat%2Fmkm-dart/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimchat%2Fmkm-dart/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimchat%2Fmkm-dart/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dimchat","download_url":"https://codeload.github.com/dimchat/mkm-dart/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimchat%2Fmkm-dart/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29685050,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-21T15:51:39.154Z","status":"ssl_error","status_checked_at":"2026-02-21T15:49:03.425Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-23T07:24:19.996Z","updated_at":"2026-02-21T16:03:08.324Z","avatar_url":"https://github.com/dimchat.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ming Ke Ming (名可名) -- Account Module (Dart)\n\n[![License](https://img.shields.io/github/license/dimchat/mkm-dart)](https://github.com/dimchat/mkm-dart/blob/main/LICENSE)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreeng)](https://github.com/dimchat/mkm-dart/pulls)\n[![Platform](https://img.shields.io/badge/Platform-Dart%203-brightgreen)](https://github.com/dimchat/mkm-dart/wiki)\n[![Issues](https://img.shields.io/github/issues/dimchat/mkm-dart)](https://github.com/dimchat/mkm-dart/issues)\n[![Repo Size](https://img.shields.io/github/repo-size/dimchat/mkm-dart)](https://github.com/dimchat/mkm-dart/archive/refs/heads/main.zip)\n[![Tags](https://img.shields.io/github/tag/dimchat/mkm-dart)](https://github.com/dimchat/mkm-dart/tags)\n[![Version](https://img.shields.io/pub/v/mkm)](https://pub.dev/packages/mkm)\n\n[![Watchers](https://img.shields.io/github/watchers/dimchat/mkm-dart)](https://github.com/dimchat/mkm-dart/watchers)\n[![Forks](https://img.shields.io/github/forks/dimchat/mkm-dart)](https://github.com/dimchat/mkm-dart/forks)\n[![Stars](https://img.shields.io/github/stars/dimchat/mkm-dart)](https://github.com/dimchat/mkm-dart/stargazers)\n[![Followers](https://img.shields.io/github/followers/dimchat)](https://github.com/orgs/dimchat/followers)\n\nThis [document](https://github.com/moky/DIMP/blob/master/MingKeMing-Identity.md) introduces a common **Account Module** for decentralized user identity authentication.\n\n## Features\n\n- [Meta](#meta)\n    - [Type](#meta-type)\n    - [Key](#public-key)\n    - [Seed](#seed)\n    - [Fingerprint](#fingerprint)\n- [ID](#id)\n    - [Type](#id-type)\n    - [Name](#id-name)\n    - [Address](#id-address)\n    - [Terminal](#terminal)\n- [Samples](#samples)\n\n## Meta\n\nThe **Meta** was generated by your **private key**, it can be used to build a new ID for entity, or verify the ID/PK pair.\n\nIt consists of 4 fields:\n\n| Field       | Description                              |\n| ----------- | ---------------------------------------- |\n| type        | Algorithm Version                        |\n| key         | Public Key                               |\n| seed        | Entity Name (Optional)                   |\n| fingerprint | Signature to generate address (Optional) |\n\nIf ```seed``` exists, ```fingerprint = privateKey.sign(seed)```\n\n### Meta Type\n\n1. ```MKM``` _(Default)_\n2. ```BTC```\n3. ~~Extended BTC~~\n4. ```ETH```\n5. ~~Extended ETH~~\n6. ...\n\n### Public Key\n\nA **public key** (PK) was bound to an ID by the **Meta Algorithm**.\n\n### Seed\n\nA string as same as **ID.name** for generate the fingerprint.\n\n### Fingerprint\n\nTHe **fingerprint** field was generated by your **private key** and **seed**:\n\n````dart\ndata = UTF8.encode(seed);\nfingerprint = privateKey.sign(data);\n````\n\n## ID\n\nThe **ID** is used to identify an **entity**(user/group). It consists of 3 fields:\n\n| Field       | Description                    |\n| ----------- | ------------------------------ |\n| type        | Entity type                    |\n| name        | Same with meta.seed (Optional) |\n| address     | Unique Identification          |\n| terminal    | Login point (Optional)         |\n\nThe ID format is ```name@address[/terminal]```.\n\n### ID Type\n\n```dart\nclass EntityType {\n\n  ///  Main: 0, 1\n  static const USER             = (0x00); // 0000 0000\n  static const GROUP            = (0x01); // 0000 0001 (User Group)\n\n  ///  Network: 2, 3\n  static const STATION          = (0x02); // 0000 0010 (Server Node)\n  static const ISP              = (0x03); // 0000 0011 (Service Provider)\n  // static const STATION_GROUP = (0x03); // 0000 0011\n\n  ///  Bot: 4, 5\n  static const BOT              = (0x04); // 0000 0100 (Business Node)\n  static const ICP              = (0x05); // 0000 0101 (Content Provider)\n  // static const BOT_GROUP     = (0x05); // 0000 0101\n\n  ///  Management: 6, 7, 8\n  // static const SUPERVISOR    = (0x06); // 0000 0110 (Company CEO)\n  // static const COMPANY       = (0x07); // 0000 0111 (Super Group for ISP/ICP)\n  // static const CA            = (0x08); // 0000 1000 (Certification Authority)\n\n  // ///  Customized: 64, 65\n  // static const APP_USER      = (0x40); // 0100 0000 (Application Customized User)\n  // static const APP_GROUP     = (0x41); // 0100 0001 (Application Customized Group)\n\n  ///  Broadcast: 128, 129\n  static const ANY              = (0x80); // 1000 0000 (anyone@anywhere)\n  static const EVERY            = (0x81); // 1000 0001 (everyone@everywhere)\n\n\n  static bool isUser(int network) {\n    return network \u0026 GROUP == USER;\n  }\n\n  static bool isGroup(int network) {\n    return network \u0026 GROUP == GROUP;\n  }\n\n  static bool isBroadcast(int network) {\n    return network \u0026 ANY == ANY;\n  }\n}\n```\n\n### ID Name\n\nThe **Name** field is a username, or just a random string for group:\n\n1. The length of name must more than 1 byte, less than 32 bytes;\n2. It should be composed by a-z, A-Z, 0-9, or charactors '_', '-', '.';\n3. It cannot contain key charactors('@', '/').\n\nName examples:\n\n```dart\nuser_name  = \"Albert.Moky\";\ngroup_name = \"Group-9527\";\n```\n\nIt's equivalent to ```meta.seed```\n\n### ID Address\n\nThe **Address** field was created with the Meta and a **Network ID**:\n\n#### BTC Address\n\n```dart\nimport 'dart:typed_data';\n\nimport 'package:mkm/type.dart';\nimport 'package:mkm/digest.dart';\nimport 'package:mkm/format.dart';\nimport 'package:mkm/protocol.dart';\n\n///  Address like BitCoin\n///  ~~~~~~~~~~~~~~~~~~~~\n///\n///      data format: \"network+digest+code\"\n///          network    --  1 byte\n///          digest     -- 20 bytes\n///          check code --  4 bytes\n///\n///      algorithm:\n///          fingerprint = PK.data\n///          digest      = ripemd160(sha256(fingerprint));\n///          code        = sha256(sha256(network + digest)).prefix(4);\n///          address     = base58_encode(network + digest + code);\n///\nclass BTCAddress extends ConstantString implements Address {\n  BTCAddress(super.string, int network) : _type = network;\n\n  final int _type;\n\n  @override\n  int get network =\u003e _type;\n\n\n  ///  Generate BTC address with fingerprint and network ID\n  ///\n  /// @param fingerprint - meta.fingerprint or key.data\n  /// @param network     - address type\n  /// @return Address object\n  static BTCAddress generate(Uint8List fingerprint, int network) {\n    // 1. digest = ripemd160(sha256(fingerprint))\n    Uint8List digest = RIPEMD160.digest(SHA256.digest(fingerprint));\n    // 2. head = network + digest\n    BytesBuilder bb = BytesBuilder(copy: false);\n    bb.addByte(network);\n    bb.add(digest);\n    Uint8List head = bb.toBytes();\n    // 3. cc = sha256(sha256(head)).prefix(4)\n    Uint8List cc = _checkCode(head);\n    // 4. data = base58_encode(head + cc)\n    bb = BytesBuilder(copy: false);\n    bb.add(head);\n    bb.add(cc);\n    return BTCAddress(Base58.encode(bb.toBytes()), network);\n  }\n\n  ///  Parse a string for BTC address\n  ///\n  /// @param address - address string\n  /// @return null on error\n  static BTCAddress? parse(String address) {\n    if (address.length \u003c 26 || address.length \u003e 35) {\n      return null;\n    }\n    // decode\n    Uint8List? data = Base58.decode(address);\n    if (data == null || data.length != 25) {\n      return null;\n    }\n    // check code\n    Uint8List prefix = data.sublist(0, 21);\n    Uint8List suffix = data.sublist(21, 25);\n    Uint8List cc = _checkCode(prefix);\n    if (Arrays.equals(cc, suffix)) {\n      return BTCAddress(address, data[0]);\n    } else {\n      return null;\n    }\n  }\n}\n\nUint8List _checkCode(Uint8List data) {\n  return SHA256.digest(SHA256.digest(data)).sublist(0, 4);\n}\n```\n\n#### ETH Address\n\n```dart\nimport 'dart:typed_data';\n\nimport 'package:mkm/type.dart';\nimport 'package:mkm/digest.dart';\nimport 'package:mkm/format.dart';\nimport 'package:mkm/protocol.dart';\n\n///  Address like Ethereum\n///  ~~~~~~~~~~~~~~~~~~~~~\n///\n///      data format: \"0x{address}\"\n///\n///      algorithm:\n///          fingerprint = PK.data;\n///          digest      = keccak256(fingerprint);\n///          address     = hex_encode(digest.suffix(20));\n///\nclass ETHAddress extends ConstantString implements Address {\n  ETHAddress(super.string);\n\n  @override\n  int get network =\u003e EntityType.USER;\n\n  static String? getValidateAddress(String address) {\n    if (!_ETH.isETH(address)) {\n      // not an ETH address\n      return null;\n    }\n    String lower = address.substring(2).toLowerCase();\n    String eip55 = _ETH.eip55(lower);\n    return '0x$eip55';\n  }\n\n  static bool isValidate(String address) {\n    String? validate = getValidateAddress(address);\n    return validate != null \u0026\u0026 validate == address;\n  }\n\n  ///  Generate ETH address with key.data\n  ///\n  /// @param fingerprint = key.data\n  /// @return Address object\n  static ETHAddress generate(Uint8List fingerprint) {\n    if (fingerprint.length == 65) {\n      // skip first char\n      fingerprint = fingerprint.sublist(1);\n    }\n    assert(fingerprint.length == 64, 'key data error: ${fingerprint.length}');\n    // 1. digest = keccak256(fingerprint);\n    Uint8List digest = KECCAK256.digest(fingerprint);\n    // 2. address = hex_encode(digest.suffix(20));\n    Uint8List tail = digest.sublist(digest.length - 20);\n    String address = _ETH.eip55(Hex.encode(tail));\n    return ETHAddress('0x$address');\n  }\n\n  ///  Parse a string for ETH address\n  ///\n  /// @param address - address string\n  /// @return null on error\n  static ETHAddress? parse(String address) {\n    if (!_ETH.isETH(address)) {\n      // not an ETH address\n      return null;\n    }\n    return ETHAddress(address);\n  }\n}\n\nclass _ETH {\n\n  // https://eips.ethereum.org/EIPS/eip-55\n  static String eip55(String hex) {\n    StringBuffer sb = StringBuffer();\n    Uint8List hash = KECCAK256.digest(UTF8.encode(hex));\n    int ch;\n    for (int i = 0; i \u003c 40; ++i) {\n      ch = hex.codeUnitAt(i);\n      if (ch \u003e _c9) {\n        // check for each 4 bits in the hash table\n        // if the first bit is '1',\n        //     change the character to uppercase\n        ch -= (hash[i \u003e\u003e 1] \u003c\u003c (i \u003c\u003c 2 \u0026 4) \u0026 0x80) \u003e\u003e 2;\n      }\n      sb.writeCharCode(ch);\n    }\n    return sb.toString();\n  }\n\n  static bool isETH(String address) {\n    if (address.length != 42) {\n      return false;\n    }\n    if (address.codeUnitAt(0) != _c0 || address.codeUnitAt(1) != _cx) {\n      return false;\n    }\n    int ch;\n    for (int i = 2; i \u003c 42; ++i) {\n      ch = address.codeUnitAt(i);\n      if (ch \u003e= _c0 \u0026\u0026 ch \u003c= _c9) {\n        continue;\n      }\n      if (ch \u003e= _cA \u0026\u0026 ch \u003c= _cZ) {\n        continue;\n      }\n      if (ch \u003e= _ca \u0026\u0026 ch \u003c= _cz) {\n        continue;\n      }\n      // unexpected character\n      return false;\n    }\n    return true;\n  }\n\n  static final int _c0 = '0'.codeUnitAt(0);\n  static final int _c9 = '9'.codeUnitAt(0);\n  static final int _cA = 'A'.codeUnitAt(0);\n  static final int _cZ = 'Z'.codeUnitAt(0);\n  static final int _ca = 'a'.codeUnitAt(0);\n  static final int _cx = 'x'.codeUnitAt(0);\n  static final int _cz = 'z'.codeUnitAt(0);\n}\n```\n\nWhen you get a meta for the entity ID from the network,\nyou must verify it with the consensus algorithm before accepting its **public key**.\n\n### Terminal\n\nA resource identifier as **Login Point**.\n\n## Samples\n\nID examples\n\n```dart\nID1 = \"hulk@4YeVEN3aUnvC1DNUufCq1bs9zoBSJTzVEj\";  // Immortal Hulk\nID2 = \"moki@4WDfe3zZ4T7opFSi3iDAKiuTnUHjxmXekk\";  // Monkey King\n```\n\nMeta Example (JsON) for ```hulk@4YeVEN3aUnvC1DNUufCq1bs9zoBSJTzVEj```\n\n```javascript\n{\n    \"type\"        : \"1\",\n    \"key\"         : {\n        \"algorithm\" : \"RSA\",\n        \"data\"      : \"-----BEGIN PUBLIC KEY-----\\nMIGJAoGBALB+vbUK48UU9rjlgnohQowME+3JtTb2hLPqtatVOW364/EKFq0/PSdnZVE9V2Zq+pbX7dj3nCS4pWnYf40ELH8wuDm0Tc4jQ70v4LgAcdy3JGTnWUGiCsY+0Z8kNzRkm3FJid592FL7ryzfvIzB9bjg8U2JqlyCVAyUYEnKv4lDAgMBAAE=\\n-----END PUBLIC KEY-----\",\n        \"mode\"      : \"ECB\",\n        \"padding\"   : \"PKCS1\",\n        \"digest\"    : \"SHA256\"\n    },\n    \"seed\"        : \"hulk\",\n    \"fingerprint\" : \"jIPGWpWSbR/DQH6ol3t9DSFkYroVHQDvtbJErmFztMUP2DgRrRSNWuoKY5Y26qL38wfXJQXjYiWqNWKQmQe/gK8M8NkU7lRwm+2nh9wSBYV6Q4WXsCboKbnM0+HVn9Vdfp21hMMGrxTX1pBPRbi0567ZjNQC8ffdW2WvQSoec2I=\"\n}\n```\n\n(All data encoded with **BASE64** algorithm as default, excepts the **address**)\n\n----\n\nCopyright \u0026copy; 2023-2026 Albert Moky\n[![Followers](https://img.shields.io/github/followers/moky)](https://github.com/moky?tab=followers)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdimchat%2Fmkm-dart","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdimchat%2Fmkm-dart","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdimchat%2Fmkm-dart/lists"}