https://github.com/dimchat/mkm-dart
Ming Ke Ming (名可名) -- Account Module (dart)
https://github.com/dimchat/mkm-dart
Last synced: 4 months ago
JSON representation
Ming Ke Ming (名可名) -- Account Module (dart)
- Host: GitHub
- URL: https://github.com/dimchat/mkm-dart
- Owner: dimchat
- License: mit
- Created: 2023-04-01T10:36:27.000Z (about 3 years ago)
- Default Branch: main
- Last Pushed: 2026-01-02T18:04:15.000Z (5 months ago)
- Last Synced: 2026-01-09T03:28:41.507Z (5 months ago)
- Language: Dart
- Size: 299 KB
- Stars: 1
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# Ming Ke Ming (名可名) -- Account Module (Dart)
[](https://github.com/dimchat/mkm-dart/blob/main/LICENSE)
[](https://github.com/dimchat/mkm-dart/pulls)
[](https://github.com/dimchat/mkm-dart/wiki)
[](https://github.com/dimchat/mkm-dart/issues)
[](https://github.com/dimchat/mkm-dart/archive/refs/heads/main.zip)
[](https://github.com/dimchat/mkm-dart/tags)
[](https://pub.dev/packages/mkm)
[](https://github.com/dimchat/mkm-dart/watchers)
[](https://github.com/dimchat/mkm-dart/forks)
[](https://github.com/dimchat/mkm-dart/stargazers)
[](https://github.com/orgs/dimchat/followers)
This [document](https://github.com/moky/DIMP/blob/master/MingKeMing-Identity.md) introduces a common **Account Module** for decentralized user identity authentication.
## Features
- [Meta](#meta)
- [Type](#meta-type)
- [Key](#public-key)
- [Seed](#seed)
- [Fingerprint](#fingerprint)
- [ID](#id)
- [Type](#id-type)
- [Name](#id-name)
- [Address](#id-address)
- [Terminal](#terminal)
- [Samples](#samples)
## 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**:
````dart
data = UTF8.encode(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
```dart
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:
```dart
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
```dart
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
```dart
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**.
## Samples
ID examples
```dart
ID1 = "hulk@4YeVEN3aUnvC1DNUufCq1bs9zoBSJTzVEj"; // Immortal Hulk
ID2 = "moki@4WDfe3zZ4T7opFSi3iDAKiuTnUHjxmXekk"; // Monkey King
```
Meta Example (JsON) for ```hulk@4YeVEN3aUnvC1DNUufCq1bs9zoBSJTzVEj```
```javascript
{
"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 © 2023-2026 Albert Moky
[](https://github.com/moky?tab=followers)