gmsm
gmsm copied to clipboard
关于 GenerateKey 方法生成 PrivateKey 结构体的问题
func GenerateKey(random io.Reader) (*PrivateKey, error) {
c := P256Sm2()
if random == nil {
random = rand.Reader //If there is no external trusted random source,please use rand.Reader to instead of it.
}
params := c.Params()
b := make([]byte, params.BitSize/8+8)
_, err := io.ReadFull(random, b)
if err != nil {
return nil, err
}
k := new(big.Int).SetBytes(b)
n := new(big.Int).Sub(params.N, two)
k.Mod(k, n)
k.Add(k, one)
priv := new(PrivateKey)
priv.PublicKey.Curve = c
priv.D = k
priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(k.Bytes())
return priv, nil
}
目前我理解这个方法实际上充当了外部导入密钥的功能,例如我持有一个裸私钥的字节数组,要还原为 PrivateKey 结构体。
发现了两个疑惑的点,第一个是初始化私钥字节数组的长度:
b := make([]byte, params.BitSize/8+8)
这里的 param.BitSize 实际值是固定的 256,除以 8 算出的数字正好是私钥长度 32,不明白为什么做了一个 +8 操作将字节数组长度初始化为了 40?
第二个是操作私钥值时有一个 +1 操作:
k.Add(k, one)
不知道是出于什么样的目的,从目前对比 java 来看,这个 +1 操作会造成传入的私钥值 +1,进而导致算出的公钥值不一致。
说说我的看法,但是仅是个人理解: 首先,
b := make([]byte, params.BitSize/8+8)
我认为这个应该是改成:
b := make([]byte, (params.BitSize+7)/8)
这样做是为了当BitSize不为8的倍数时可以调整,如果已经符合要求,+7也不会影响;但是,当下大家基本都会设置成符合要求的值,所以是否+7也无关紧要。
关于第二点,
k.Add(k, one)
我认为,+1是为了避免0,因为私钥的取值要求>0,当然可以做个0值判断,非0不做+1。
最后,关于sm实现有挺多版本和实现,各种实现可能都存在差异,所以坑还是有的,希望早点出个标准来规范下。
代码中的+1以前,有一个n-2, 这个是保证私钥不会是0,想法不错;原代码中没有实现相关公钥,私钥的构造函数,可自行添加如下明文私钥生成私钥结构体
func FormatPri(priByte []byte) (*PrivateKey, error) {
c := P256Sm2()
k := new(big.Int).SetBytes(priByte)
priv := new(PrivateKey)
priv.PublicKey.Curve = c
priv.D = k
priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(k.Bytes())
return priv, nil
}