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

https://github.com/dimchat/mkm-objc

Ming Ke Ming (名可名) -- Account Module
https://github.com/dimchat/mkm-objc

Last synced: 4 months ago
JSON representation

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

Awesome Lists containing this project

README

          

# Ming Ke Ming (名可名) -- Account Module (Objective-C)

[![License](https://img.shields.io/github/license/dimchat/mkm-objc)](https://github.com/dimchat/mkm-objc/blob/master/LICENSE)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/dimchat/mkm-objc/pulls)
[![Platform](https://img.shields.io/badge/Platform-iOS%20%7C%20OSX%20%7C%20watchOS%20%7C%20tvOS-brightgreen.svg)](https://github.com/dimchat/mkm-objc/wiki)
[![Issues](https://img.shields.io/github/issues/dimchat/mkm-objc)](https://github.com/dimchat/mkm-objc/issues)
[![Repo Size](https://img.shields.io/github/repo-size/dimchat/mkm-objc)](https://github.com/dimchat/mkm-objc/archive/refs/heads/master.zip)
[![Tags](https://img.shields.io/github/tag/dimchat/mkm-objc)](https://github.com/dimchat/mkm-objc/tags)
[![Version](https://img.shields.io/cocoapods/v/MingKeMing
)](https://cocoapods.org/pods/MingKeMing)

[![Watchers](https://img.shields.io/github/watchers/dimchat/mkm-objc)](https://github.com/dimchat/mkm-objc/watchers)
[![Forks](https://img.shields.io/github/forks/dimchat/mkm-objc)](https://github.com/dimchat/mkm-objc/forks)
[![Stars](https://img.shields.io/github/stars/dimchat/mkm-objc)](https://github.com/dimchat/mkm-objc/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**:

````objective-c
data = [seed dataUsingEncoding:NSUTF8StringEncoding];
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

```objective-c
typedef NS_ENUM(UInt8, MKMNetworkID) {

/**
* Main: 0, 1
*/
MKMEntityType_User = 0x00, // 0000 0000
MKMEntityType_Group = 0x01, // 0000 0001 (User Group)

/**
* Network: 2, 3
*/
MKMEntityType_Station = 0x02, // 0000 0010 (Server Node)
MKMEntityType_ISP = 0x03, // 0000 0011 (Service Provider)
//MKMEntityType_StationGroup = 0x03, // 0000 0011

/**
* Bot: 4, 5
*/
MKMEntityType_Bot = 0x04, // 0000 0100 (Business Node)
MKMEntityType_ICP = 0x05, // 0000 0101 (Content Provider)
//MKMEntityType_BotGroup = 0x05, // 0000 0101

/**
* Management: 6, 7, 8
*/
//MKMEntityType_Supervisor = 0x06, // 0000 0110 (Company President)
//MKMEntityType_Company = 0x07, // 0000 0111 (Super Group for ISP/ICP)
//MKMEntityType_CA = 0x08, // 0000 1000 (Certification Authority)

/*
* Customized: 64, 65
*/
//MKMEntityType_AppUser = 0x40, // 0100 0000 (Application Customized User)
//MKMEntityType_AppGroup = 0x41, // 0100 0001 (Application Customized Group)

/**
* Broadcast: 128, 129
*/
MKMEntityType_Any = 0x80, // 1000 0000 (anyone@anywhere)
MKMEntityType_Every = 0x81, // 1000 0001 (everyone@everywhere)
};
typedef UInt8 MKMEntityType;

#define MKMEntityTypeIsUser(network) (((network) & MKMEntityType_Group) == MKMEntityType_User)
#define MKMEntityTypeIsGroup(network) (((network) & MKMEntityType_Group) == MKMEntityType_Group)
#define MKMEntityTypeIsBroadcast(network) (((network) & MKMEntityType_Any) == MKMEntityType_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:

```objective-c
user_name = @"Albert.Moky";
group_name = @"Group-9527";
```

### ID Address

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

#### BTC Address

```objective-c
#import
#import

NS_ASSUME_NONNULL_BEGIN

/*
* Address like BitCoin
*
* data format: "network+digest+code"
* network -- 1 byte
* digest -- 20 bytes
* code -- 4 bytes
*
* algorithm:
* fingerprint = sign(seed, SK); // public key data
* digest = ripemd160(sha256(fingerprint));
* code = sha256(sha256(network + digest)).prefix(4);
* address = base58_encode(network + digest + code);
*/
@interface DIMBTCAddress : MKString

- (instancetype)initWithString:(NSString *)address type:(MKMEntityType)network
NS_DESIGNATED_INITIALIZER;

/**
* Generate address with fingerprint and network ID
*
* @param fingerprint = meta.fingerprint or key.data
* @param network - address type
* @return Address object
*/
+ (instancetype)generate:(NSData *)fingerprint type:(MKMEntityType)network;

/**
* Parse a string for BTC address
*
* @param string - address string
* @return null on error
*/
+ (instancetype)parse:(NSString *)string;

@end

NS_ASSUME_NONNULL_END
```

```objective-c
#import "DIMBTCAddress.h"

@interface DIMBTCAddress ()

@property (nonatomic) MKMEntityType network; // Network ID

@end

/**
* BTC address algorithm:
* digest = ripemd160(sha256(fingerprint));
* check_code = sha256(sha256(network + digest)).prefix(4);
* addr = base58_encode(network + digest + check_code);
*/
@implementation DIMBTCAddress

- (instancetype)init {
NSAssert(false, @"DON'T call me!");
NSString *string = nil;
return [self initWithString:string];
}

- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
NSAssert(false, @"DON'T call me!");
NSString *string = nil;
return [self initWithString:string type:0];
}

- (instancetype)initWithString:(NSString *)address {
//NSAssert(false, @"DON'T call me!");
return [self initWithString:address type:0];
}

/* designated initializer */
- (instancetype)initWithString:(NSString *)address type:(MKMEntityType)network {
if (self = [super initWithString:address]) {
_network = network;
}
return self;
}

- (id)copyWithZone:(nullable NSZone *)zone {
DIMBTCAddress *address = [super copyWithZone:zone];
if (address) {
address.network = _network;
}
return address;
}

#pragma mark Coding

static inline NSData *check_code(NSData *data) {
assert([data length] == 21);
NSData *sha256d = MKSHA256Digest(MKSHA256Digest(data));
return [sha256d subdataWithRange:NSMakeRange(0, 4)];
}

+ (instancetype)generate:(NSData *)fingerprint type:(MKMEntityType)network {
// 1. digest = ripemd160(sha256(fingerprint))
NSData *digest = MKRipeMD160Digest(MKSHA256Digest(fingerprint));
// 2. head = network + digest
NSMutableData *data = [[NSMutableData alloc] initWithBytes:&network length:1];
[data appendData:digest];
// 3. cc = sha256(sha256(head)).prefix(4)
NSData *cc = check_code(data);
// 4. addr = base58_encode(_h + cc)
[data appendData:cc];
NSString *string = MKBase58Encode(data);
return [[self alloc] initWithString:string type:network];
}

+ (instancetype)parse:(NSString *)string {
if (string.length < 26 || string.length > 35) {
return nil;
}
// decode
NSData *data = MKBase58Decode(string);
if (data.length != 25) {
return nil;
}
// Check Code
NSData *prefix = [data subdataWithRange:NSMakeRange(0, 21)];
NSData *suffix = [data subdataWithRange:NSMakeRange(21, 4)];
NSData *cc = check_code(prefix);
if ([cc isEqualToData:suffix]) {
UInt8 *bytes = (UInt8 *)data.bytes;
return [[self alloc] initWithString:string type:bytes[0]];
} else {
return nil;
}
}

@end
```

#### ETH Address

```objective-c
#import
#import

NS_ASSUME_NONNULL_BEGIN

/**
* Address like Ethereum
*
* data format: "0x{address}"
*
* algorithm:
* fingerprint = PK.data;
* digest = keccak256(fingerprint);
* address = hex_encode(digest.suffix(20));
*/
@interface DIMETHAddress : MKString

+ (NSString *)validateAddress:(NSString *)address;
+ (BOOL)isValidate:(NSString *)address;

/**
* Generate ETH address with key.data
*
* @param fingerprint = key.data
* @return Address object
*/
+ (instancetype)generate:(NSData *)fingerprint;

/**
* Parse a string for ETH address
*
* @param string - address string
* @return null on error
*/
+ (instancetype)parse:(NSString *)string;

@end
```

```objective-c
#import "DIMETHAddress.h"

// https://eips.ethereum.org/EIPS/eip-55
static inline NSString *eip55(NSString *hex) {
NSData *utf8 = MKUTF8Encode(hex);
NSData *digest = MKKeccak256Digest(utf8);
UInt8 *origin = (UInt8 *)utf8.bytes;
UInt8 *hash = (UInt8 *)digest.bytes;
UInt8 buffer[40];
UInt8 ch;
for (int i = 0; i < 40; ++i) {
ch = origin[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;
}
buffer[i] = ch;
}
return [[NSString alloc] initWithBytes:buffer length:40 encoding:NSUTF8StringEncoding];
}

static inline BOOL is_eth(NSString *address) {
if (address.length != 42) {
return NO;
}
NSData *data = MKUTF8Encode(address);
UInt8 *buffer = (UInt8 *)data.bytes;
if (buffer[0] != '0' || buffer[1]!= 'x') {
return NO;
}
char ch;
for (int i = 2; i < 42; ++i) {
ch = buffer[i];
if (ch >= '0' && ch <= '9') {
continue;
}
if (ch >= 'A' && ch <= 'Z') {
continue;
}
if (ch >= 'a' && ch <= 'z') {
continue;
}
// unexpected character
return NO;
}
return YES;
}

@implementation DIMETHAddress

// Override
- (MKMEntityType)network {
return MKMEntityType_User;
}

#pragma mark Coding

+ (NSString *)validateAddress:(NSString *)address {
if (is_eth(address)) {
address = [address substringFromIndex:2];
address = [address lowercaseString];
return [NSString stringWithFormat:@"0x%@", eip55(address)];
}
return nil;
}

+ (BOOL)isValidate:(NSString *)address {
NSString *validate = [self validateAddress:address];
return [validate isEqualToString:address];
}

+ (instancetype)generate:(NSData *)fingerprint {
if (fingerprint.length == 65) {
fingerprint = [fingerprint subdataWithRange:NSMakeRange(1, 64)];
}
NSAssert(fingerprint.length == 64, @"key data length error: %lu", fingerprint.length);
// 1. digest = keccak256(fingerprint);
NSData *digest = MKKeccak256Digest(fingerprint);
// 2. address = hex_encode(digest.suffix(20));
NSData *tail = [digest subdataWithRange:NSMakeRange(digest.length - 20, 20)];
NSString *hex = MKHexEncode(tail);
NSString *address = [NSString stringWithFormat:@"0x%@", eip55(hex)];
return [[self alloc] initWithString:address];
}

+ (instancetype)parse:(NSString *)string {
if (is_eth(string)) {
return [[self alloc] initWithString:string];
}
return nil;
}

@end
```

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

```objective-c
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 © 2018-2026 Albert Moky
[![Followers](https://img.shields.io/github/followers/moky)](https://github.com/moky?tab=followers)