IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 区块链 -> libsecp256k1比特币密码算法开源库(十) -> 正文阅读

[区块链]libsecp256k1比特币密码算法开源库(十)

2021SC@SDUSC

secp256k1定义的结构有很多,主要会说明以下内容:
Message:哈希摘要;
PublicKey:公钥;
SecretKey:256比特私钥;
RecoveryId:用于从签名中恢复公钥的标记;
SharedSecret:ECDH共享密钥;
Signature:数字签名

这些结构的定义和实施都比较类似,本篇主要讲述Message。此外在这些结构中都使用了libsecp256k1中对大数长度的一些定义,在本篇中加以说明,方便后续的分析。

Message

哈希摘要Message总述

hash摘要在数字签名中非常常见,在区块链比特币场景中多处要用到数字签名,比如转账方要对转账记录进行数字签名,以确保转账由他本人发起;此外在区块链中还有些地方单独使用了hash函数,比如挖矿、对交易hash得到merkle树的树根、以及转账时转账方要给出接收方的公钥hash以便接收方在给别人转账时可以通过脚本引擎的验证……

在之前的博客中多次出现变量message,之前解释过使用的message是Scalar类型的256位标量值,这里专门为hash摘要创建一个类型Message:

pub struct Message(pub Scalar);

待加密信息要经过hash处理成为摘要才能参与签名生成、签名验证、公钥恢复等运算,那么这个hash摘要结过就是一个256比特的01串,Message类型实例化的对象就是这样的256比特01串,将这个对象转化为数组序列的过程称之为序列化(在代码中使用serialize表示);将数组序列恢复成为Message类型的一个对象的过程称为反序列化(在代码中用parse表示)。

首先在Message中定义了三个函数,分别是反序列化parse、切片parse_slice和序列化serialize:

impl Message {
//反序列化:将数组序列恢复成为Message类型的一个对象
    pub fn parse(p: &[u8; util::MESSAGE_SIZE]) -> Message {
        let mut m = Scalar::default();
        let _ = m.set_b32(p);

        Message(m)
    }
//切片
    pub fn parse_slice(p: &[u8]) -> Result<Message, Error> {
        if p.len() != util::MESSAGE_SIZE {
            return Err(Error::InvalidInputLength);
        }//如果p的长度不为常量MESSAGE_SIZE定义值32则报错

        let mut a = [0; util::MESSAGE_SIZE];
        a.copy_from_slice(p);
        Ok(Self::parse(&a))
    }
//序列化:将这个Message对象转化为数组序列
    pub fn serialize(&self) -> [u8; util::MESSAGE_SIZE] {
        self.0.b32()
    }
}

反序列化parse

首先是parse反序列化过程:
libsecp256k1库为message长度定义一个常量MESSAGE_SIZE,由于usize = 32,这表示message共32单位长度:

pub const MESSAGE_SIZE: usize = 32;

message共32单位长度,那么一个单位是多大呢?
下面代码中有p: &[u8; util::MESSAGE_SIZE],这表示的意思就是变量p用数组元素存储(p是一个序列化的变量),u8表示数组元素是无符号char类型,存储8位比特;util::MESSAGE_SIZE变量p共包含多少个数组元素,前面说过,这里的MESSAGE_SIZE是个常量,为32。那么这其实就表示变量p包含32个8比特的数组元素,即共包含256比特,刚好是一个hash摘要的长度。
那么接下来只要创建一个Scalar类型的变量m来接收p即可,就把p转化为了一个Message类型的变量m,实现了反序列化的过程。

 pub fn parse(p: &[u8; util::MESSAGE_SIZE]) -> Message {
        let mut m = Scalar::default();
        let _ = m.set_b32(p);

        Message(m)
    }

实际上这里是调用了一个set_b32的函数实现了反序列化,就是基本的移位、与或运算等,没有太多解释的必要,相关运算法则我在libsecp256k1比特币密码算法开源库(八)中曾经介绍过,这里不再细说。具体set_b32实现过程如下代码所示:

pub fn set_b32(&mut self, a: &[u8; 32]) -> bool {
        self.n[0] = (a[31] as u32)
            | ((a[30] as u32) << 8)
            | ((a[29] as u32) << 16)
            | (((a[28] & 0x3) as u32) << 24);
        self.n[1] = (((a[28] >> 2) & 0x3f) as u32)
            | ((a[27] as u32) << 6)
            | ((a[26] as u32) << 14)
            | (((a[25] & 0xf) as u32) << 22);
        self.n[2] = (((a[25] >> 4) & 0xf) as u32)
            | ((a[24] as u32) << 4)
            | ((a[23] as u32) << 12)
            | (((a[22] as u32) & 0x3f) << 20);
        self.n[3] = (((a[22] >> 6) & 0x3) as u32)
            | ((a[21] as u32) << 2)
            | ((a[20] as u32) << 10)
            | ((a[19] as u32) << 18);
        self.n[4] = (a[18] as u32)
            | ((a[17] as u32) << 8)
            | ((a[16] as u32) << 16)
            | (((a[15] & 0x3) as u32) << 24);
        self.n[5] = (((a[15] >> 2) & 0x3f) as u32)
            | ((a[14] as u32) << 6)
            | ((a[13] as u32) << 14)
            | (((a[12] as u32) & 0xf) << 22);
        self.n[6] = (((a[12] >> 4) & 0xf) as u32)
            | ((a[11] as u32) << 4)
            | ((a[10] as u32) << 12)
            | (((a[9] & 0x3f) as u32) << 20);
        self.n[7] = (((a[9] >> 6) & 0x3) as u32)
            | ((a[8] as u32) << 2)
            | ((a[7] as u32) << 10)
            | ((a[6] as u32) << 18);
        self.n[8] = (a[5] as u32)
            | ((a[4] as u32) << 8)
            | ((a[3] as u32) << 16)
            | (((a[2] & 0x3) as u32) << 24);
        self.n[9] = (((a[2] >> 2) & 0x3f) as u32) | ((a[1] as u32) << 6) | ((a[0] as u32) << 14);

        if self.n[9] == 0x03fffff
            && (self.n[8] & self.n[7] & self.n[6] & self.n[5] & self.n[4] & self.n[3] & self.n[2])
                == 0x3ffffff
            && (self.n[1] + 0x40 + ((self.n[0] + 0x3d1) >> 26)) > 0x3ffffff
        {
            return false;
        }

        self.magnitude = 1;
        self.normalized = true;
        debug_assert!(self.verify());

        true
    }

这个代码的就是实现将一个[u8; 32](一个数组元素占8位,共32个数组元素的变量)字段元素转化为一个32字节的大端序(就是Scalar类型变量),并且转化结束后生成的大端值将被规范化。

序列化serialize

序列化就是把反序列化反过来,将一个Scalar类型(也可以认为是Message类型,因为专门创建一个Message结构体)转化为一个[u8; util::MESSAGE_SIZE]的数组变量,这个转化过程通过函数b32()来实现

pub fn serialize(&self) -> [u8; util::MESSAGE_SIZE] {
        self.0.b32()
    }

b32实现代码如下,显然它又调用了一个fill_b32函数(套娃警告),在b32中创建了一个数组变量bin,与Message类型变量序列化后的变量结构一致:

pub fn b32(&self) -> [u8; 32] {
        let mut bin = [0u8; 32];
        self.fill_b32(&mut bin);
        bin
    }

在介绍fill_b32之前合理想象,每个数组元素是u8类型占8位,数组共32个元素,这个代码也短不了,实际上就是用32个u8类型的数组元素接收Message类型的对应位。

pub fn fill_b32(&self, bin: &mut [u8; 32]) {
        bin[0] = (self.0[7] >> 24) as u8;
        bin[1] = (self.0[7] >> 16) as u8;
        bin[2] = (self.0[7] >> 8) as u8;
        bin[3] = (self.0[7]) as u8;
        bin[4] = (self.0[6] >> 24) as u8;
        bin[5] = (self.0[6] >> 16) as u8;
        bin[6] = (self.0[6] >> 8) as u8;
        bin[7] = (self.0[6]) as u8;
        bin[8] = (self.0[5] >> 24) as u8;
        bin[9] = (self.0[5] >> 16) as u8;
        bin[10] = (self.0[5] >> 8) as u8;
        bin[11] = (self.0[5]) as u8;
        bin[12] = (self.0[4] >> 24) as u8;
        bin[13] = (self.0[4] >> 16) as u8;
        bin[14] = (self.0[4] >> 8) as u8;
        bin[15] = (self.0[4]) as u8;
        bin[16] = (self.0[3] >> 24) as u8;
        bin[17] = (self.0[3] >> 16) as u8;
        bin[18] = (self.0[3] >> 8) as u8;
        bin[19] = (self.0[3]) as u8;
        bin[20] = (self.0[2] >> 24) as u8;
        bin[21] = (self.0[2] >> 16) as u8;
        bin[22] = (self.0[2] >> 8) as u8;
        bin[23] = (self.0[2]) as u8;
        bin[24] = (self.0[1] >> 24) as u8;
        bin[25] = (self.0[1] >> 16) as u8;
        bin[26] = (self.0[1] >> 8) as u8;
        bin[27] = (self.0[1]) as u8;
        bin[28] = (self.0[0] >> 24) as u8;
        bin[29] = (self.0[0] >> 16) as u8;
        bin[30] = (self.0[0] >> 8) as u8;
        bin[31] = (self.0[0]) as u8;
    }

切片parse_slice

虽然这个函数取了一个slice切片的名字,但我认为这单纯只是一个复制,并使用了一个可修复错误的错误处理Result<T, E>来接收结果(其中T对应正常访问值的数据类型, E对应错误返回值的数据类型)。这个函数的作用就是将序列化元素(下面代码中的变量p)复制存放在序列化元素数组(下面代码中的变量a)中,然后再调用反序列化函数parse将序列化数组a给反序列化。

 pub fn parse_slice(p: &[u8]) -> Result<Message, Error> {
        if p.len() != util::MESSAGE_SIZE {
            return Err(Error::InvalidInputLength);
        }//错误,如果p的长度不为32则报错

        let mut a = [0; util::MESSAGE_SIZE];
        a.copy_from_slice(p);
        Ok(Self::parse(&a))//没有错误,结果为反序列化
    }

具体的复制操作由函数copy_from_slice来实现,就是把序列化数组p中的元素给复制到一个新数组a中。

utils相关参数定义

在前文中有一个util::MESSAGE_SIZE,常量MESSAGE_SIZE定义了一个message的长度,除此之外libsecp256k1中还定义了很多常量包括私钥长度、签名长度等,为了后面继续介绍相应的结构,在这里贴个全的,后面有用到对应的还会单独拿出来加以说明。

pub mod util {
    pub const TAG_PUBKEY_EVEN: u8 = 0x02;//公钥y坐标为偶数时添加前缀0x02
    pub const TAG_PUBKEY_ODD: u8 = 0x03;//公钥y坐标为奇数时添加前缀0x03
    pub const TAG_PUBKEY_FULL: u8 = 0x04;//公钥未压缩时添加前缀0x04
    pub const TAG_PUBKEY_HYBRID_EVEN: u8 = 0x06;
    pub const TAG_PUBKEY_HYBRID_ODD: u8 = 0x07;

    pub const MESSAGE_SIZE: usize = 32;//哈希摘要共32字节
    pub const SECRET_KEY_SIZE: usize = 32;//私钥共32字节
    pub const RAW_PUBLIC_KEY_SIZE: usize = 64;//原始公钥共64字节
    pub const FULL_PUBLIC_KEY_SIZE: usize = 65;//未压缩公钥65字节
    pub const COMPRESSED_PUBLIC_KEY_SIZE: usize = 33;//压缩公钥33字节
    pub const SIGNATURE_SIZE: usize = 64;//数字签名64字节
    pub const DER_MAX_SIGNATURE_SIZE: usize = 72;//数字签名DER序列化72字节

    pub use crate::{
        ecmult::{
            odd_multiples_table, ECMULT_TABLE_SIZE_A, ECMULT_TABLE_SIZE_G, WINDOW_A, WINDOW_G,
        },
        group::{globalz_set_table_gej, set_table_gej_var, AFFINE_INFINITY, JACOBIAN_INFINITY},
    };

    pub use crate::der::{Decoder, SignatureArray};
}
  区块链 最新文章
盘点具备盈利潜力的几大加密板块,以及潜在
阅读笔记|让区块空间成为商品,打造Web3云
区块链1.0-比特币的数据结构
Team Finance被黑分析|黑客自建Token“瞒天
区块链≠绿色?波卡或成 Web3“生态环保”标
期货从入门到高深之手动交易系列D1课
以太坊基础---区块验证
进入以太坊合并的五个数字
经典同态加密算法Paillier解读 - 原理、实现
IPFS/Filecoin学习知识科普(四)
上一篇文章      下一篇文章      查看所有文章
加:2021-11-23 12:24:43  更:2021-11-23 12:25:31 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 22:49:09-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码