-
Notifications
You must be signed in to change notification settings - Fork 10
3.业务消息
消息统一采用json格式,并至少包含以下四个字段:
- Action:消息类型
- Timestamp:消息生成时间
- PublicKey:消息生成账号的公钥
- Signature:消息的签名
本地系统先按顺序填充除Signature以外的字段,然后对消息签名,再填充Signature字段
json = {"Action":...,...,"Timestamp":...,"PublicKey":...}
sig = sign(json)
json = {"Action":...,...,"Timestamp":...,"PublicKey":...,"Signature":sig}
举例:
json =
{"Action":200,
"Timestamp":1563332477646,
"PublicKey":"039342D72C90A41A2FF6807095BA91C8A1F69DE9E090B77CD90B40072B67539116",
"Signature":"3045022100C0B0FF2EC6A705EA2F542EE67BEE6064F52DE12C9F629BB16584F1E645211B730220170155F10E6E1AE26BEE360E0AD19A0070E416B0677691960CCEA011168BFA91"}
Action=200表示这是一条账号声明消息
远程系统通过公钥039342D72C90A41A2FF6807095BA91C8A1F69DE9E090B77CD90B40072B67539116推导出账号地址为oeZELFBq5mbE4y5XdvG1f3DsW85FM7HHGM
从上例4个字段的json中分离出3个变量msg,pk,sig,用于验签
- sig = json["Signature"]
- pk = json["PublicKey"]
- msg = json delete ["Signature"]
如果verify(msg,pk,sig)==true,则远程系统标记消息的来源连接为账号oeZELFBq5mbE4y5XdvG1f3DsW85FM7HHGM
消息始终在发送方本地系统上生成(组装、签名、存储),通过远程中继系统发送给接收方本地系统,再由接收方本地系统校验(格式、来源账号、签名等)后存储。
需要中继转发的消息包含字段
- To:消息接收账号地址
远程中继系统的作用是识别连接的来源账号,将消息转发给接收账号对应的连接,不处理具体业务,处理具体业务的是本地系统。
一个远程系统如果不能无差别地为所有账号的个体做好转发服务,就会被所有个体所抛弃,个体可以带着自己的本地系统连接一个老实的远程系统,反正数据都在本地系统。
系统描述中介绍了所有消息的基本4字段
- Action:消息类型
- Timestamp:消息生成时间
- PublicKey:消息生成账号的公钥
- Signature:消息的签名
及定向消息的必备字段
- To:消息接收账号地址
对一条消息的终极3问:
- 本条消息是那个账号什么时间生成的?
生成账号是可被证明的,不可伪造不可抵赖的
生成时间只供参考,不可被证明 - 本条消息是要干什么的?
Action告知系统本条消息的类型 - 本条消息要发给那个账号?
To告知系统本条消息应被转发给那个账号,无To字段的为非定向消息
公告是个体愿意向所有其他个体公开发布的信息。
由于在生成时并不明确向那个账号发送,公告消息是不带To字段。
出去4个必备字段外,还应该有内容,区块链属性序号和前消息散列值,具体如下:
- Action:201
- Sequence:公告序号
- PreHash:前公告散列值
- Content:公告内容
- Timestamp
- PublicKey
- Signature
这样单个账号的一系列公告就区块链(单链)化了。
公告序号从1开始,每次加1,第n+1条公告引用第n条公告的散列值,每条公告都由签发账号签名。
签名保证公告消息的完整性和与签发账号的关联性。
出于间接传播和交互的考虑,再增加一个字段,用于引用其他公告
- Quote:是一个0到8个元素的数组
每个元素用于明确一条公告消息,包含3个字段:
- Address:公告的发布账号
- Sequence:公告的序号
- Hash:公告的散列值
现在有了公告消息,再构造两个消息,用于请求和响应。
公告请求消息
用于向其他账号请求,指定签发账号和序号的公告
- Action:202
- Address:公告的发布账号
- Sequence:公告序号
- To:请求接收账号地址
- Timestamp
- PublicKey
- Signature
公告响应消息
用于返回一个公告
- Action:203
- Bulletin:公告消息
- To:响应接收账号地址
- Timestamp
- PublicKey
- Signature
-
账号在本地系统生成公告消息(可以引用本地存储的公告消息),等待其他账号来请求公告消息。
-
账号向其他账号发送公告请求消息,其他账号查询本地系统是否存在Address和Sequence相符的公告,如果有,则返回公告响应消息。
公告请求的两种模式:
- 对于关注的账号,本地已存储了该账号从1到n的公告,请求序号n之后的公告
- 对于已获取公告B中引用的公告Q,从B公告签发账号获取被引用公告Q(使用公告Q的账号地址和序号)
-
请求账号收到并校验无误后存储到其本地系统。
虽然公告消息由其背后的账号签发,公告消息一旦被其他账号获取,即便原签发账号不在线或者被毁灭,其公告消息依然可以通过被引用的方式在网络上继续传播。
原签发账号既无法收回公告消息,也无法否认公告消息是自己签发的,也无法重发一条相同序号的公告,如果网络上出现同一账号签发的同序号不同内容的两条公告,说明该公告消息的签发账号已经不可信了,自己说过的话,自己得认。
一条公告是否被传播,完全靠一个个账号背后的个体决定,而非某种权威或特权决定。
聊天消息也是在账号的本地系统中生成,由远程系统中继转发给对方账号。
远程系统通常是不受控制的,为了防止它作恶,需要对消息内容进行加密。
这里用到了Diffie–Hellman密钥交换算法,原理简单描述如下:
本地系统A生成pkA和skA
本地系统B生成pkB和skB
A和B通过远程系统交换pkA和pkB
本地系统A可以通过skA和pkB算出加密密钥x
本地系统B可以通过skB和pkA算出加密密钥x
但是远程系统只知道pkA和pkB,算不出加密密钥x
AES256对称加密算法本身具有很高的加密强度,但是通常使用对称加密算法,需要定期更换加密密钥,因此两个账号协商加密密钥时也应该考虑时间段的问题。
密钥协商消息具体如下:
- Action:204
- Division:一天分成多少个时间块,比如将一天分成3块,则每一块是8小时
- Sequence:从某一纪元开始,按Division划分,时间块的序号
- DHPublicKey:本方的pk
- Pair:对方的pk,如果为空,说明本方无法计算出会话加密密钥,还不能开始聊天,如果不为空,说明本方已经计算出会话加密密钥,本方已就绪,当本方和对方都就绪时才可以开始聊天
- To:聊天对方的账号地址
- Timestamp
- PublicKey
- Signature
参照公告消息,出于同步和区块链(单链)化的考虑,聊天消息也应该包含聊天消息序号和前聊天消息散列值,
另外,为了通知对方哪些消息已经接收了,增加字段PairHash,用于声明已经接收到的对方消息
具体如下:
- Action:205
- Sequence:聊天消息序号
- PreHash:前聊天消息散列值
- PairHash:是一个0到8个元素的数组,每个元素为一条未确认接收的对方消息的散列值
- Content:聊天消息内容,为加密后的消息内容
- To:聊天对方的账号地址
- Timestamp
- PublicKey
- Signature
由于消息始终存储在本地系统,无法保证每个账号的本地系统都始终在线,存在发送方本地系统的消息已经到了第n+m条了,但接收方本地系统接收的消息只到第n条,需要聊天同步消息告诉对方已接收到的消息最大序号:
- Action:206
- CurrentSequence:已接收消息的最大序号
- To:聊天对方的账号地址
- Timestamp
- PublicKey
- Signature
检查本方和对方在此刻对于的时间块的会话加密密钥是否就绪,如果未就绪,则开始协商会话加密密钥。
如就绪,则可以开始收发加密后的聊天消息。
如收到消息的序号为无效消息(序号或散列值与最近一条消息不对应),则向对方发送同步请求,获取缺失的消息。
没有系统能够监控在物理空间上接触的两个非特定个体之间的信息交互,同理网络空间也应如此。