gmsm icon indicating copy to clipboard operation
gmsm copied to clipboard

关于 GenerateKey 方法生成 PrivateKey 结构体的问题

Open changhr2013 opened this issue 3 years ago • 2 comments

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,进而导致算出的公钥值不一致。

changhr2013 avatar Jan 19 '22 07:01 changhr2013

说说我的看法,但是仅是个人理解: 首先,

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实现有挺多版本和实现,各种实现可能都存在差异,所以坑还是有的,希望早点出个标准来规范下。

tkblack avatar Jan 21 '22 03:01 tkblack

代码中的+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
}

lpilp avatar Mar 07 '22 03:03 lpilp