An open API service indexing awesome lists of open source software.

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)

Awesome Lists containing this project

README

          

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

[![License](https://img.shields.io/github/license/dimchat/mkm-dart)](https://github.com/dimchat/mkm-dart/blob/main/LICENSE)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreeng)](https://github.com/dimchat/mkm-dart/pulls)
[![Platform](https://img.shields.io/badge/Platform-Dart%203-brightgreen)](https://github.com/dimchat/mkm-dart/wiki)
[![Issues](https://img.shields.io/github/issues/dimchat/mkm-dart)](https://github.com/dimchat/mkm-dart/issues)
[![Repo Size](https://img.shields.io/github/repo-size/dimchat/mkm-dart)](https://github.com/dimchat/mkm-dart/archive/refs/heads/main.zip)
[![Tags](https://img.shields.io/github/tag/dimchat/mkm-dart)](https://github.com/dimchat/mkm-dart/tags)
[![Version](https://img.shields.io/pub/v/mkm)](https://pub.dev/packages/mkm)

[![Watchers](https://img.shields.io/github/watchers/dimchat/mkm-dart)](https://github.com/dimchat/mkm-dart/watchers)
[![Forks](https://img.shields.io/github/forks/dimchat/mkm-dart)](https://github.com/dimchat/mkm-dart/forks)
[![Stars](https://img.shields.io/github/stars/dimchat/mkm-dart)](https://github.com/dimchat/mkm-dart/stargazers)
[![Followers](https://img.shields.io/github/followers/dimchat)](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
[![Followers](https://img.shields.io/github/followers/moky)](https://github.com/moky?tab=followers)