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 {
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);
}
let mut a = [0; util::MESSAGE_SIZE];
a.copy_from_slice(p);
Ok(Self::parse(&a))
}
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);
}
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;
pub const TAG_PUBKEY_ODD: u8 = 0x03;
pub const TAG_PUBKEY_FULL: u8 = 0x04;
pub const TAG_PUBKEY_HYBRID_EVEN: u8 = 0x06;
pub const TAG_PUBKEY_HYBRID_ODD: u8 = 0x07;
pub const MESSAGE_SIZE: usize = 32;
pub const SECRET_KEY_SIZE: usize = 32;
pub const RAW_PUBLIC_KEY_SIZE: usize = 64;
pub const FULL_PUBLIC_KEY_SIZE: usize = 65;
pub const COMPRESSED_PUBLIC_KEY_SIZE: usize = 33;
pub const SIGNATURE_SIZE: usize = 64;
pub const DER_MAX_SIGNATURE_SIZE: usize = 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};
}
|