init: v1.0.0
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
package fpe
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"xdx.jelly/xgcl/sm/sm4"
|
||||
)
|
||||
|
||||
// return the check code of id.
|
||||
func checkCodeOfID(id0 string) byte {
|
||||
panic("todo")
|
||||
}
|
||||
|
||||
// EncryptID 加密身份证号。最后一位为校验位。
|
||||
// key为SM4密钥,16字节
|
||||
// tweak随机数,任意长度,可明文保存。
|
||||
func EncryptID(id string, key []byte, tweak []byte) (string, error) {
|
||||
if len(id) != 18 {
|
||||
return "", errors.New("wrong id")
|
||||
}
|
||||
|
||||
if len(key) != 16 {
|
||||
return "", errors.New("wrong key")
|
||||
}
|
||||
b, err := sm4.NewCipher(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
f := NewFF1(b, Numeric)
|
||||
numerals, err := f.Encode(id[:len(id)-1])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
cipher, err := f.Encrypt(tweak, numerals)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
encryptedID, err := f.Decode(cipher)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// TODO: add the check code
|
||||
|
||||
return encryptedID, nil
|
||||
|
||||
}
|
||||
|
||||
// 加密纯数字的手机号
|
||||
func EncryptPhoneNumber(phoneNumber string, key []byte, tweak []byte) (string, error) {
|
||||
|
||||
panic("todo")
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
package fpe
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"errors"
|
||||
"math"
|
||||
"math/big"
|
||||
|
||||
"xdx.jelly/xgcl/internal/xor"
|
||||
)
|
||||
|
||||
func rev[T any](X []T) {
|
||||
l := len(X) / 2
|
||||
for i := 0; i < l; i++ {
|
||||
t := X[i]
|
||||
X[i] = X[l-i-1]
|
||||
X[l-i-1] = t
|
||||
}
|
||||
}
|
||||
|
||||
// in = in0||in1||in2...
|
||||
// out0 = Enc(iv ^ in0)
|
||||
// out1 = Enc(out0 ^ in1)
|
||||
// ...
|
||||
// if iv == nil <==> iv = 0^16.
|
||||
func prf(res []byte, b cipher.Block, iv []byte, in []byte) []byte {
|
||||
xor.XorBytes(res, iv, in[:16])
|
||||
b.Encrypt(res, res)
|
||||
in = in[16:]
|
||||
|
||||
for len(in) > 0 {
|
||||
xor.XorBytes(res, res, in[:16])
|
||||
b.Encrypt(res, res)
|
||||
in = in[16:]
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
type FF1 struct {
|
||||
cipher.Block
|
||||
Alphabet
|
||||
}
|
||||
|
||||
func NewFF1(b cipher.Block, a Alphabet) FPE {
|
||||
return &FF1{
|
||||
Block: b,
|
||||
Alphabet: a,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FF1) round(P []byte, Q []byte, S []byte, A []Numeral, B []Numeral, i int, b int, d int, module *big.Int, isenc bool) {
|
||||
Q[len(Q)-b-1] = byte(i)
|
||||
f.Num(B).FillBytes(Q[len(Q)-b:])
|
||||
prf(S, f.Block, P, Q)
|
||||
for j := 1; j < (d+15)/16; j++ {
|
||||
Sj := S[16*j : 16*j+16]
|
||||
S[16*j+12] = byte(j >> 24)
|
||||
S[16*j+13] = byte(j >> 16)
|
||||
S[16*j+14] = byte(j >> 8)
|
||||
S[16*j+15] = byte(j)
|
||||
xor.XorBytes(Sj, S[:16], Sj)
|
||||
f.Block.Encrypt(Sj, Sj)
|
||||
}
|
||||
y := new(big.Int).SetBytes(S[:d])
|
||||
c := f.Num(A)
|
||||
if isenc {
|
||||
c.Add(c, y).Mod(c, module)
|
||||
} else {
|
||||
c.Sub(c, y).Mod(c, module)
|
||||
}
|
||||
f.Str(A, c)
|
||||
}
|
||||
|
||||
func (f *FF1) endecrypt(T []byte, X []Numeral, isEnc bool) ([]Numeral, error) {
|
||||
n := len(X)
|
||||
if n <= 2 {
|
||||
return nil, errors.New("input numeral string X must at least 2 characters")
|
||||
}
|
||||
|
||||
radix := f.Alphabet.Radix()
|
||||
t := len(T)
|
||||
u := n / 2
|
||||
v := n - u
|
||||
b := (int(math.Ceil(float64(v)*math.Log2(float64(radix)))) + 7) >> 3
|
||||
d := (b + 7) & (^3)
|
||||
P := []byte{
|
||||
1, 2, 1,
|
||||
0, byte(radix >> 8), byte(radix),
|
||||
10, byte(u),
|
||||
byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n),
|
||||
byte(t >> 24), byte(t >> 16), byte(t >> 8), byte(t),
|
||||
}
|
||||
f.Block.Encrypt(P, P)
|
||||
// make a copy of X
|
||||
res := make([]Numeral, len(X))
|
||||
copy(res, X)
|
||||
A := res[:u]
|
||||
B := res[u:]
|
||||
|
||||
// Q := append(make([]byte, 0, (b+t+1+15)&(^15)), T...)
|
||||
Q := make([]byte, (b+t+1+15)&(^15))
|
||||
copy(Q, T)
|
||||
S := make([]byte, (d+15)&(^15))
|
||||
|
||||
var moduleA, moduleB *big.Int
|
||||
radixBig := big.NewInt(int64(radix))
|
||||
// 如果输入X很大,这里的module会很大。
|
||||
moduleA = new(big.Int).Exp(radixBig, big.NewInt(int64(u)), nil)
|
||||
moduleB = moduleA
|
||||
if v > u {
|
||||
moduleB = radixBig.Mul(moduleB, radixBig)
|
||||
}
|
||||
|
||||
if isEnc {
|
||||
for i := 0; i < 10; i += 2 {
|
||||
f.round(P, Q, S, A, B, i, b, d, moduleA, true)
|
||||
f.round(P, Q, S, B, A, i+1, b, d, moduleB, true)
|
||||
}
|
||||
} else {
|
||||
for i := 9; i >= 0; i -= 2 {
|
||||
f.round(P, Q, S, B, A, i, b, d, moduleB, false)
|
||||
f.round(P, Q, S, A, B, i-1, b, d, moduleA, false)
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (f *FF1) Encrypt(T []byte, X []Numeral) ([]Numeral, error) {
|
||||
return f.endecrypt(T, X, true)
|
||||
}
|
||||
|
||||
func (f FF1) Decrypt(T []byte, X []Numeral) ([]Numeral, error) {
|
||||
return f.endecrypt(T, X, false)
|
||||
}
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
// fpe is the Format-Preserving Encryption.
|
||||
package fpe
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Numeral = uint16
|
||||
|
||||
type Char = uint16
|
||||
|
||||
// Alphabet is the interface for an alphabet.
|
||||
type Alphabet interface {
|
||||
// fpe实际上是可对任意的字符表进行编码,比如JPEG2000,编码范围为0xff8f的序列,且不以0xff结尾。
|
||||
// 但是这里的Encode只处理utf-8的string
|
||||
Encode(s string) ([]Numeral, error)
|
||||
Decode(numString []Numeral) (string, error)
|
||||
|
||||
// 如果是二进制类的数据
|
||||
// EncodeBlob(b []byte) ([]Numeral, error)
|
||||
// DecodeBlob(numString []Numeral) ([]byte, error)
|
||||
|
||||
Radix() int
|
||||
Num(X []Numeral) *big.Int
|
||||
|
||||
// X = Str_radix^m(C)
|
||||
Str(X []Numeral, x *big.Int)
|
||||
}
|
||||
|
||||
var (
|
||||
// ASCII code 32-126, all printable characters.
|
||||
Printable = NewAlphabet(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~")
|
||||
Numeric = NewAlphabet("0123456789")
|
||||
Lower = NewAlphabet("abcdefghijklmnopqrstuvwxyz")
|
||||
Upper = NewAlphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
Alpha = NewAlphabet("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
NumAlpha = NewAlphabet("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
||||
)
|
||||
|
||||
type FPE interface {
|
||||
Encrypt(tweak []byte, X []Numeral) ([]Numeral, error)
|
||||
Decrypt(tweak []byte, X []Numeral) ([]Numeral, error)
|
||||
Alphabet
|
||||
}
|
||||
|
||||
// GenericAlphabet 可以处理asicc字符集
|
||||
type GenericAlphabet struct {
|
||||
tblBuf [128]Char
|
||||
tbl [][]Numeral
|
||||
|
||||
r Radix
|
||||
}
|
||||
|
||||
// Assume the characters in alphabet are all ASCII now.
|
||||
// Unicode characters not supported now.
|
||||
func NewAlphabet(alphabet string) Alphabet {
|
||||
r := len(alphabet)
|
||||
|
||||
res := &GenericAlphabet{}
|
||||
res.r.Set(r)
|
||||
|
||||
tblBuf := res.tblBuf
|
||||
for i := range tblBuf {
|
||||
tblBuf[i] = 128
|
||||
}
|
||||
for _, d := range alphabet {
|
||||
if d > 127 {
|
||||
panic("only ASCII characters are supported now")
|
||||
}
|
||||
tblBuf[d] = Char(d)
|
||||
}
|
||||
tbls := make([][]Numeral, 0)
|
||||
start := -1
|
||||
for i, d := range tblBuf {
|
||||
switch {
|
||||
case d == 128:
|
||||
if start >= 0 {
|
||||
tbls = append(tbls, tblBuf[start:i])
|
||||
start = -1
|
||||
}
|
||||
case d < 128:
|
||||
if start < 0 {
|
||||
start = i
|
||||
}
|
||||
}
|
||||
}
|
||||
res.tbl = tbls
|
||||
return res
|
||||
}
|
||||
|
||||
var numSwitch = false
|
||||
|
||||
// Num implements Alphabet.
|
||||
func (ga *GenericAlphabet) Num(X []Char) *big.Int {
|
||||
return ga.r.Num(X)
|
||||
}
|
||||
|
||||
// x should < radix^(lenX)
|
||||
func (ga *GenericAlphabet) Str(X []Numeral, x *big.Int) {
|
||||
ga.r.Str(X, x)
|
||||
}
|
||||
|
||||
// FromString implements Alphabet.
|
||||
func (ga *GenericAlphabet) Encode(s string) ([]Numeral, error) {
|
||||
res := make([]Numeral, 0, len(s))
|
||||
for _, c := range s {
|
||||
if c>>16 != 0 {
|
||||
return nil, fmt.Errorf("unsupported character %c", c)
|
||||
}
|
||||
|
||||
if idx := index(Char(c), ga.tbl); idx >= 0 {
|
||||
res = append(res, Numeral(idx))
|
||||
} else {
|
||||
return nil, errors.New("bad characters")
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// // Radix implements Alphabet.
|
||||
func (ga *GenericAlphabet) Radix() int {
|
||||
return int(ga.r.r)
|
||||
}
|
||||
|
||||
// ToString implements Alphabet.
|
||||
func (ga *GenericAlphabet) Decode(numString []Numeral) (string, error) {
|
||||
var sb strings.Builder
|
||||
radix := uint16(ga.r.r)
|
||||
for _, d := range numString {
|
||||
if d >= radix {
|
||||
return "", errors.New("bad numeric string")
|
||||
}
|
||||
if _, err := sb.WriteRune(rune(char(d, ga.tbl))); err != nil {
|
||||
return "", errors.New("bad rune")
|
||||
}
|
||||
}
|
||||
return sb.String(), nil
|
||||
}
|
||||
|
||||
var _ Alphabet = &GenericAlphabet{}
|
||||
|
||||
// tbls[i] are continuous Char
|
||||
// tbls[0][0], tbls[0][1], ..., tbls[0][n0],
|
||||
// tbls[1][0], tbls[1][1], ..., tbls[1][n1],
|
||||
// ...
|
||||
func index(c Char, tbls [][]Char) int {
|
||||
n := 0
|
||||
for _, tbl := range tbls {
|
||||
n0 := int(c) - int(tbl[0])
|
||||
if n0 >= 0 && n0 < len(tbl) {
|
||||
return n + n0
|
||||
}
|
||||
n += len(tbl)
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Assume n < radix
|
||||
func char(n Numeral, tbls [][]Char) Char {
|
||||
m := int(n)
|
||||
for _, tbl := range tbls {
|
||||
if m < len(tbl) {
|
||||
return tbl[m]
|
||||
}
|
||||
m -= len(tbl)
|
||||
}
|
||||
panic("numeric great than radix")
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package fpe
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"xdx.jelly/xgcl/grand"
|
||||
"xdx.jelly/xgcl/sm/sm4"
|
||||
)
|
||||
|
||||
func TestNum(t *testing.T) {
|
||||
N := 1000
|
||||
A := make([]Numeral, N)
|
||||
B := make([]Numeral, N)
|
||||
|
||||
for r := 2; r < 100; r++ {
|
||||
radix := NewRadix(r)
|
||||
for i := 1; i <= N; i++ {
|
||||
for j := 0; j < i; j++ {
|
||||
A[j] = Numeral(rand.Int31() % int32(r))
|
||||
}
|
||||
numSwitch = false
|
||||
a0 := radix.Num(A[:i])
|
||||
numSwitch = true
|
||||
a1 := radix.Num(A[:i])
|
||||
assert.Equal(t, a0.Cmp(a1), 0)
|
||||
|
||||
radix.Str(B[:i], a0)
|
||||
assert.Equal(t, A[:i], B[:i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStr(t *testing.T) {
|
||||
f := Alpha
|
||||
// 24948832942366750129003083595837476696096111951836798179448429117431251233398075403977925597490
|
||||
A := []Numeral{
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
}
|
||||
|
||||
for i := 20; i <= len(A); i++ {
|
||||
a := f.Num(A[:i])
|
||||
B := make([]Numeral, i)
|
||||
f.Str(B, a)
|
||||
for j := range B {
|
||||
if B[j] != A[j] {
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkAlphabet-10 1158810 1020 ns/op 88 B/op 3 allocs/op
|
||||
// BenchmarkAlphabet-10 3776529 310.4 ns/op 96 B/op 4 allocs/op
|
||||
func BenchmarkNum(b *testing.B) {
|
||||
f := Alpha
|
||||
A := []Numeral{
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
f.Num(A)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkStr-10 556981 2130 ns/op 104 B/op 2 allocs/op
|
||||
// BenchmarkStr-10 3134851 382.2 ns/op 104 B/op 2 allocs/op
|
||||
func BenchmarkStr(b *testing.B) {
|
||||
f := Alpha
|
||||
A := []Numeral{
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
}
|
||||
|
||||
a := f.Num(A)
|
||||
B := make([]Numeral, len(A))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
f.Str(B, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFF1(t *testing.T) {
|
||||
// fmt.Printf("%x\n", ^uint64(15))
|
||||
// 密钥
|
||||
key := grand.GetRandom(16)
|
||||
// 使用SM4加密算法
|
||||
b, _ := sm4.NewCipher(key)
|
||||
// tweak
|
||||
T := grand.GetRandom(16)
|
||||
|
||||
// interface FPE
|
||||
f := NewFF1(b, NewAlphabet("abcdefghijklmnopqrstuvwxyz "))
|
||||
|
||||
plainString0 := "hello world"
|
||||
plain0, err := f.Encode(plainString0)
|
||||
assert.Nil(t, err)
|
||||
|
||||
cipher, err := f.Encrypt(T, plain0)
|
||||
assert.Nil(t, err)
|
||||
|
||||
cipherString, err := f.Decode(cipher)
|
||||
assert.Nil(t, err)
|
||||
|
||||
fmt.Println("Cipher string:", cipherString)
|
||||
plain1, err := f.Decrypt(T, cipher)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, plain0, plain1)
|
||||
plainString1, err := f.Decode(plain1)
|
||||
assert.Nil(t, err)
|
||||
|
||||
fmt.Println("Decrypt string:", plainString1)
|
||||
assert.Equal(t, plainString0, plainString1)
|
||||
}
|
||||
|
||||
// BenchmarkFF1-10 129781 8683 ns/op 4256 B/op 154 allocs/op
|
||||
// BenchmarkFF1-10 325104 3694 ns/op 2808 B/op 99 allocs/op
|
||||
func BenchmarkFF1(b *testing.B) {
|
||||
key := grand.GetRandom(16)
|
||||
block, _ := sm4.NewCipher(key)
|
||||
T := grand.GetRandom(16)
|
||||
f := NewFF1(block, Printable)
|
||||
|
||||
plainString0 := "hello world"
|
||||
plain0, _ := f.Encode(plainString0)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = f.Encrypt(T, plain0)
|
||||
}
|
||||
}
|
||||
|
||||
// // 密文长度 加密速率(MBps)
|
||||
// // 32 2.55
|
||||
// // 64 3.99
|
||||
// // 128 3.58
|
||||
// // 256 3.82
|
||||
// // 512 3.65
|
||||
// // 1024 3.15
|
||||
// // 2048 2.38
|
||||
// // 4096 1.58
|
||||
// // 8192 0.94
|
||||
func TestSpeed(t *testing.T) {
|
||||
key := grand.GetRandom(16)
|
||||
block, _ := sm4.NewCipher(key)
|
||||
T := grand.GetRandom(16)
|
||||
f := NewFF1(block, Printable)
|
||||
|
||||
fmt.Println("密文长度 加密速率(MBps)")
|
||||
for plainLen, cnt := 32, 10000; plainLen < 10000; plainLen, cnt = plainLen*2, cnt/2 {
|
||||
b := make([]byte, plainLen)
|
||||
for i := range b {
|
||||
b[i] = byte(rand.Uint32()%26 + 'a')
|
||||
}
|
||||
plain := string(b)
|
||||
|
||||
start := time.Now()
|
||||
for i := 0; i < cnt; i++ {
|
||||
n, _ := f.Encode(plain)
|
||||
_, _ = f.Encrypt(T, n)
|
||||
|
||||
}
|
||||
end := time.Now()
|
||||
elapsed := end.Sub(start)
|
||||
fmt.Printf("%4d %.2f\n", plainLen, float64(len(plain)*cnt)/(1024*1024*elapsed.Seconds()))
|
||||
}
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
package fpe
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/big"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
type Radix struct {
|
||||
r int
|
||||
|
||||
e int // the maximum number such that r^e is a uint64
|
||||
|
||||
rBig *big.Int // radix as big
|
||||
|
||||
reBig *big.Int // radix^e as big.Int, the same as rPowers[-1]
|
||||
|
||||
rPowers []big.Int // [r, r^2, ..., r^e]
|
||||
}
|
||||
|
||||
func NewRadix(r int) *Radix {
|
||||
radix := &Radix{}
|
||||
radix.Set(r)
|
||||
return radix
|
||||
}
|
||||
|
||||
func (r *Radix) Set(v int) {
|
||||
if v < 2 || v >= math.MaxUint16 {
|
||||
panic("radix overflow")
|
||||
}
|
||||
r.r = v
|
||||
r.e = 64 / bits.Len32(uint32(v))
|
||||
r.rPowers = make([]big.Int, r.e)
|
||||
r.rPowers[0].SetUint64(uint64(v))
|
||||
for i, vi := 1, uint64(v); i < r.e; i++ {
|
||||
vi *= uint64(v)
|
||||
r.rPowers[i].SetUint64(vi)
|
||||
}
|
||||
r.rBig = &r.rPowers[0]
|
||||
r.reBig = &r.rPowers[r.e-1]
|
||||
}
|
||||
|
||||
func (r *Radix) Radix() int {
|
||||
return r.r
|
||||
}
|
||||
|
||||
func (r *Radix) Num(X []Char) *big.Int {
|
||||
if false {
|
||||
radix := big.NewInt(int64(r.r))
|
||||
n := new(big.Int)
|
||||
for _, d := range X {
|
||||
n.Mul(n, radix)
|
||||
n.Add(n, big.NewInt(int64(d)))
|
||||
}
|
||||
return n
|
||||
} else {
|
||||
radix := uint64(r.r)
|
||||
l := len(X) % r.e
|
||||
|
||||
tail := uint64(0)
|
||||
for i := 0; i < l; i++ {
|
||||
tail *= uint64(radix)
|
||||
tail += uint64(X[i])
|
||||
}
|
||||
|
||||
n := new(big.Int).SetUint64(tail)
|
||||
for i := l; i < len(X); i += r.e {
|
||||
d := uint64(0)
|
||||
for j := 0; j < r.e; j++ {
|
||||
d = d*uint64(radix) + uint64(X[i+j])
|
||||
}
|
||||
|
||||
n.Mul(n, r.reBig)
|
||||
n.Add(n, new(big.Int).SetUint64(d))
|
||||
}
|
||||
return n
|
||||
}
|
||||
}
|
||||
|
||||
// x should < radix^(lenX)
|
||||
func (r *Radix) Str(X []Numeral, x *big.Int) {
|
||||
if false {
|
||||
n := len(X)
|
||||
radix := big.NewInt(int64(r.r))
|
||||
d := new(big.Int)
|
||||
xx := new(big.Int).Set(x) // make a copy
|
||||
for i := 0; i < n; i++ {
|
||||
xx, d = xx.DivMod(xx, radix, d)
|
||||
X[n-i-1] = Numeral(d.Int64())
|
||||
}
|
||||
// if xx is not zero, then the input x must too big
|
||||
if xx.Sign() != 0 {
|
||||
panic("x should less then radix^m")
|
||||
}
|
||||
} else {
|
||||
xx := new(big.Int).Set(x) // make a copy
|
||||
lx := len(X)
|
||||
radix := uint64(r.r)
|
||||
d := new(big.Int)
|
||||
|
||||
// the tail part
|
||||
m := lx % r.e
|
||||
if m > 0 {
|
||||
xx, d = xx.DivMod(xx, &r.rPowers[m-1], d)
|
||||
n := d.Uint64()
|
||||
for i := lx - 1; i >= lx-m; i-- {
|
||||
X[i] = Numeral(n % radix)
|
||||
n /= radix
|
||||
}
|
||||
}
|
||||
|
||||
for i := lx - m; i > 0; i -= r.e {
|
||||
xx, d = xx.DivMod(xx, r.reBig, d)
|
||||
n := d.Uint64()
|
||||
for j := i - 1; j >= i-r.e; j-- {
|
||||
X[j] = uint16(n % radix)
|
||||
n /= radix
|
||||
}
|
||||
}
|
||||
// if xx is not zero, then the input x must too big
|
||||
if xx.Sign() != 0 {
|
||||
panic("x should less then radix^m")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import math
|
||||
|
||||
|
||||
def loggam(x):
|
||||
# double x0, x2, xp, gl, gl0;
|
||||
# long k, n;
|
||||
|
||||
a = [8.333333333333333e-02, -2.777777777777778e-03,
|
||||
7.936507936507937e-04, -5.952380952380952e-04,
|
||||
8.417508417508418e-04, -1.917526917526918e-03,
|
||||
6.410256410256410e-03, -2.955065359477124e-02,
|
||||
1.796443723688307e-01, -1.39243221690590e+00]
|
||||
x *= 1.0
|
||||
x0 = x
|
||||
n = 0
|
||||
if x == 1.0 or x == 2.0:
|
||||
return 0.0
|
||||
|
||||
elif x <= 7.0:
|
||||
n = int(7 - x)
|
||||
x0 = x + n
|
||||
x2 = 1.0 / (x0*x0)
|
||||
xp = 2 * math.pi
|
||||
gl0 = a[9]
|
||||
for k in range(8, -1, -1):
|
||||
gl0 *= x2
|
||||
gl0 += a[k]
|
||||
gl = gl0/x0 + 0.5 * math.log(xp) + (x0-0.5) * math.log(x0) - x0
|
||||
if x <= 7.0:
|
||||
for k in range(1, n + 1):
|
||||
gl -= math.log(x0-1.0)
|
||||
x0 -= 1.0
|
||||
return gl
|
||||
|
||||
print(loggam(2))
|
||||
print(loggam(3))
|
||||
print(loggam(4))
|
||||
print(loggam(5))
|
||||
@@ -0,0 +1,115 @@
|
||||
package ope
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// uniformRand returns a random number in [0, 1]
|
||||
// len(coins) >= 32
|
||||
func uniformRand(coins []byte) float64 {
|
||||
b := int64(0)
|
||||
for i := 0; i < 32; i++ {
|
||||
b <<= 1
|
||||
b |= int64(coins[i])
|
||||
}
|
||||
return float64(b) / float64(uint64(1)<<32-1)
|
||||
}
|
||||
|
||||
// hypergeometric10 returns a sample in hypergeometric distribution.
|
||||
// Pr[X=x] = choose(y, x) * choose(N-y, M-x) / choose(N, M)
|
||||
func hypergeometricSmall(M int64, N int64, y int64, coins []byte) int64 {
|
||||
d1 := N - y
|
||||
d2 := min(M, N-M)
|
||||
|
||||
Y := d2
|
||||
K := y
|
||||
for Y > 0.0 && K != 0 {
|
||||
U := uniformRand(coins)
|
||||
Y -= int64(U + float64(Y)/float64(d1+K))
|
||||
K--
|
||||
}
|
||||
Z := d2 - Y
|
||||
if N-M < M {
|
||||
Z = y - Z
|
||||
}
|
||||
return Z
|
||||
}
|
||||
|
||||
func loggam[T int | int64 | float32 | float64](x T) float64 {
|
||||
return math.Log(math.Gamma(float64(x)))
|
||||
}
|
||||
|
||||
func hypergeometric(M int64, N int64, y int64, coins []byte) int64 {
|
||||
if y > 10 {
|
||||
return hypergeometricSmall(M, N, y, coins)
|
||||
}
|
||||
return hypergeometricBig(M, N, y, coins)
|
||||
}
|
||||
|
||||
func hypergeometricBig(M int64, N int64, y int64, coins []byte) int64 {
|
||||
D1 := 1.7155277699214135
|
||||
D2 := 0.8989161620588988
|
||||
// # long mingoodbad, maxgoodbad, popsize, m, d9;
|
||||
// # double d4, d5, d6, d7, d8, d10, d11;
|
||||
// # long Z;
|
||||
// # double T, W, X, Y;
|
||||
good := M
|
||||
bad := N - M
|
||||
sample := y
|
||||
|
||||
mingoodbad := min(good, bad)
|
||||
popsize := N
|
||||
maxgoodbad := max(good, bad)
|
||||
m := min(sample, popsize-sample)
|
||||
d4 := float64(mingoodbad) / float64(popsize)
|
||||
d5 := 1.0 - d4
|
||||
d6 := float64(m)*d4 + 0.5
|
||||
d7 := math.Sqrt(float64(popsize-m)*float64(sample)*d4*d5/float64(popsize-1) + 0.5)
|
||||
d8 := D1*d7 + D2
|
||||
d9 := (m + 1) * (mingoodbad + 1) / (popsize + 2)
|
||||
d10 := loggam(d9+1) + loggam(mingoodbad-d9+1) + loggam(m-d9+1) + loggam(maxgoodbad-m+d9+1)
|
||||
d11 := min(float64(min(m, mingoodbad)+1), math.Floor(d6+16*d7))
|
||||
// # 16 for 16-decimal-digit precision in D1 and D2
|
||||
|
||||
var Z int64
|
||||
for {
|
||||
X := uniformRand(coins)
|
||||
Y := uniformRand(coins)
|
||||
W := d6 + d8*(Y-0.5)/X
|
||||
|
||||
// # fast rejection:
|
||||
if W < 0.0 || W >= d11 {
|
||||
continue
|
||||
}
|
||||
|
||||
Z = int64(math.Floor(W))
|
||||
T := d10 - (loggam(Z+1) + loggam(mingoodbad-Z+1) + loggam(m-Z+1) + loggam(maxgoodbad-m+Z+1))
|
||||
|
||||
// # fast acceptance:
|
||||
if (X*(4.0-X) - 3.0) <= T {
|
||||
break
|
||||
}
|
||||
|
||||
// # fast rejection:
|
||||
if X*(X-T) >= 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
// # acceptance:
|
||||
if 2.0*math.Log(X) <= T {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// # this is a correction to HRUA* by Ivan Frohne in rv.py
|
||||
if good > bad {
|
||||
Z = m - Z
|
||||
}
|
||||
|
||||
// # another fix from rv.py to allow sample to exceed popsize/2
|
||||
if m < sample {
|
||||
Z = good - Z
|
||||
}
|
||||
|
||||
return Z
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package ope
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"math/big"
|
||||
|
||||
"xdx.jelly/xgcl/grand"
|
||||
)
|
||||
|
||||
// uniformRand returns a random number in [0, 1]
|
||||
func uniformRand() float64 {
|
||||
b := make([]byte, 4)
|
||||
_, _ = grand.GenerateRandom(b)
|
||||
return float64(binary.LittleEndian.Uint32(b)) / float64(uint64(1)<<32-1)
|
||||
}
|
||||
|
||||
func minBig(a, b *big.Int) *big.Int {
|
||||
if a.Cmp(b) < 0 {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func maxBig(a, b *big.Int) *big.Int {
|
||||
if a.Cmp(b) < 0 {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
func floatFromBig(a *big.Int) float64 {
|
||||
r, _ := a.Float64()
|
||||
return r
|
||||
}
|
||||
|
||||
// hypergeometric10 returns a sample in hypergeometric distribution.
|
||||
// Pr[X=x] = choose(y, x) * choose(N-y, M-x) / choose(N, M)
|
||||
func hypergeometricSmall(M *big.Int, N *big.Int, y *big.Int) *big.Int {
|
||||
d1 := new(big.Int).Sub(N, y)
|
||||
nm := new(big.Int).Sub(N, M)
|
||||
d2 := minBig(M, nm)
|
||||
|
||||
Y := new(big.Int).Set(d2)
|
||||
K := y.Int64()
|
||||
for Y.Sign() > 0 && K != 0 {
|
||||
U := uniformRand()
|
||||
Y.Sub(Y, big.NewInt(int64(U+floatFromBig(Y)/(floatFromBig(d1)+float64(K)))))
|
||||
K--
|
||||
}
|
||||
Z := new(big.Int).Sub(d2, Y)
|
||||
if nm.Cmp(M) < 0 {
|
||||
Z.Sub(y, Z)
|
||||
}
|
||||
return Z
|
||||
}
|
||||
|
||||
func loggam(x int64) float64 {
|
||||
return math.Log(math.Gamma(float64(x)))
|
||||
}
|
||||
|
||||
func hypergeometric(M *big.Int, N *big.Int, y *big.Int) *big.Int {
|
||||
if y.BitLen() < 4 {
|
||||
return hypergeometricSmall(M, N, y)
|
||||
}
|
||||
return hypergeometricBig(M, N, y)
|
||||
}
|
||||
|
||||
func hypergeometricBig(M *big.Int, N *big.Int, y *big.Int) *big.Int {
|
||||
D1 := 1.7155277699214135
|
||||
D2 := 0.8989161620588988
|
||||
// # long mingoodbad, maxgoodbad, popsize, m, d9;
|
||||
// # double d4, d5, d6, d7, d8, d10, d11;
|
||||
// # long Z;
|
||||
// # double T, W, X, Y;
|
||||
good := M
|
||||
bad := new(big.Int).Sub(N, M)
|
||||
sample := y
|
||||
|
||||
mingoodbad := minBig(good, bad)
|
||||
popsize := N
|
||||
maxgoodbad := maxBig(good, bad)
|
||||
m := minBig(sample, new(big.Int).Sub(popsize, sample))
|
||||
|
||||
d4 := floatFromBig(mingoodbad) / floatFromBig(popsize)
|
||||
d5 := 1.0 - d4
|
||||
d6 := floatFromBig(m)*d4 + 0.5
|
||||
d7 := math.Sqrt(floatFromBig(new(big.Int).Sub(popsize, m))*floatFromBig(sample)*d4*d5/floatFromBig(new(big.Int).Sub(popsize, one)) + 0.5)
|
||||
d8 := D1*d7 + D2
|
||||
d9 := floatFromBig(new(big.Int).Add(m, one).Mul(new(big.Int).Add(mingoodbad, one))) / (popsize + 2)
|
||||
d10 := loggam(d9+1) + loggam(mingoodbad-d9+1) + loggam(m-d9+1) + loggam(maxgoodbad-m+d9+1)
|
||||
d11 := min(float64(min(m, mingoodbad)+1), math.Floor(d6+16*d7))
|
||||
// # 16 for 16-decimal-digit precision in D1 and D2
|
||||
|
||||
var Z int64
|
||||
for {
|
||||
X := uniformRand()
|
||||
Y := uniformRand()
|
||||
W := d6 + d8*(Y-0.5)/X
|
||||
|
||||
// # fast rejection:
|
||||
if W < 0.0 || W >= d11 {
|
||||
continue
|
||||
}
|
||||
|
||||
Z = int64(math.Floor(W))
|
||||
T := d10 - (loggam(Z+1) + loggam(mingoodbad-Z+1) + loggam(m-Z+1) + loggam(maxgoodbad-m+Z+1))
|
||||
|
||||
// # fast acceptance:
|
||||
if (X*(4.0-X) - 3.0) <= T {
|
||||
break
|
||||
}
|
||||
|
||||
// # fast rejection:
|
||||
if X*(X-T) >= 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
// # acceptance:
|
||||
if 2.0*math.Log(X) <= T {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// # this is a correction to HRUA* by Ivan Frohne in rv.py
|
||||
if good > bad {
|
||||
Z = m - Z
|
||||
}
|
||||
|
||||
// # another fix from rv.py to allow sample to exceed popsize/2
|
||||
if m < sample {
|
||||
Z = good - Z
|
||||
}
|
||||
|
||||
return Z
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
// package ope is the Order-Preserving Encryption
|
||||
// 保序加密, 即保持密文的顺序. 可用于数据库.
|
||||
package ope
|
||||
|
||||
func bytesToCoins(b []byte) []byte {
|
||||
res := make([]byte, 0, 8*len(b))
|
||||
for _, x := range b {
|
||||
for i := 0; i < 8; i++ {
|
||||
res = append(res, x&1)
|
||||
x >>= 1
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// ValueRange reprents the range [start, end]
|
||||
type ValueRange struct {
|
||||
start int64
|
||||
end int64
|
||||
}
|
||||
|
||||
func (v *ValueRange) Set(start int64, end int64) *ValueRange {
|
||||
if start > end {
|
||||
panic("not a valid value range, start must no greater then end")
|
||||
}
|
||||
v.start = start
|
||||
v.end = end
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *ValueRange) Size() int64 {
|
||||
return v.end - v.start + 1
|
||||
}
|
||||
|
||||
func (v *ValueRange) Contains(n int64) bool {
|
||||
return n >= v.start && n <= v.end
|
||||
}
|
||||
|
||||
// func (v *ValueRange) BitSize() int64 {
|
||||
// n := v.Size()
|
||||
// return int64(n.BitLen())
|
||||
// }
|
||||
|
||||
func (v *ValueRange) Clone() *ValueRange {
|
||||
return new(ValueRange).Set(v.start, v.end)
|
||||
}
|
||||
|
||||
// UniformRand returns a number in range uniformly
|
||||
func UniformRand(M *ValueRange, coins []byte) int64 {
|
||||
start, end := M.start, M.end
|
||||
for i := 0; end > start; i++ {
|
||||
mid := (start + end) / 2
|
||||
if coins[i] == 0 {
|
||||
end = mid
|
||||
} else {
|
||||
start = mid + 1
|
||||
}
|
||||
}
|
||||
return start
|
||||
}
|
||||
|
||||
// HyperGeometricRand returns a number x in inRange, satisfying the distribution
|
||||
// Prob[X = x] = HyperGeometric(M, N, y)
|
||||
func HyperGeometricRand(M, N *ValueRange, y int64, coins []byte) int64 {
|
||||
m := M.Size()
|
||||
n := N.Size()
|
||||
|
||||
nsampleIndex := y - N.start + 1
|
||||
if M == N {
|
||||
return M.start + nsampleIndex - 1
|
||||
}
|
||||
|
||||
inSampleNum := hypergeometric(m, n, nsampleIndex, coins)
|
||||
if inSampleNum == 0 {
|
||||
return M.start
|
||||
} else {
|
||||
return M.start + inSampleNum - 1
|
||||
}
|
||||
}
|
||||
|
||||
type OPE struct {
|
||||
}
|
||||
|
||||
func (o *OPE) Init(key []byte) {
|
||||
|
||||
}
|
||||
|
||||
func (o *OPE) Encrypt() {
|
||||
|
||||
}
|
||||
|
||||
func (o *OPE) Decrypts() {
|
||||
|
||||
}
|
||||
|
||||
func tapeGen(key []byte, data []byte) []byte {
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func sampleUniform() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package ope
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"xdx.jelly/xgcl/grand"
|
||||
)
|
||||
|
||||
func choose(y int64, x int64) float64 {
|
||||
a, _ := new(big.Int).MulRange(y-x+1, y).Float64()
|
||||
b, _ := new(big.Int).MulRange(2, x).Float64()
|
||||
return a / b
|
||||
}
|
||||
|
||||
func hyperProb(x int64, M int64, N int64, y int64) float64 {
|
||||
return choose(M, x) * choose(N-M, y-x) / choose(N, y)
|
||||
}
|
||||
|
||||
func TestUniform(t *testing.T) {
|
||||
stats := make([]int, 10)
|
||||
for i := 0; i < 1000000; i++ {
|
||||
f := uniformRand(bytesToCoins(grand.GetRandom(4)))
|
||||
stats[int(f*10)]++
|
||||
}
|
||||
|
||||
fmt.Println(stats)
|
||||
}
|
||||
|
||||
func TestHyperSmall(t *testing.T) {
|
||||
M := int64(4)
|
||||
N := int64(10)
|
||||
y := int64(5)
|
||||
stats := make([]float64, min(y, M)+1)
|
||||
wants := make([]float64, min(y, M)+1)
|
||||
count := 1000000
|
||||
for i := 0; i < count; i++ {
|
||||
x := hypergeometricSmall(M, N, y, bytesToCoins(grand.GetRandom(4)))
|
||||
stats[x] += 1
|
||||
}
|
||||
|
||||
for i := 0; i < len(stats); i++ {
|
||||
stats[i] = stats[i] / float64(count)
|
||||
wants[i] = hyperProb(int64(i), M, N, y)
|
||||
}
|
||||
if true {
|
||||
for i := 0; i < len(stats); i++ {
|
||||
fmt.Printf("%.2f ", stats[i])
|
||||
}
|
||||
fmt.Println()
|
||||
for i := 0; i < len(stats); i++ {
|
||||
fmt.Printf("%.2f ", wants[i])
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
epsilon := 0.001
|
||||
for i := 0; i < len(stats); i++ {
|
||||
assert.Less(t, math.Abs(stats[i]-wants[i]), epsilon)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHyperBig(t *testing.T) {
|
||||
M := int64(30)
|
||||
N := int64(100)
|
||||
y := int64(51)
|
||||
stats := make([]float64, min(y, M)+1)
|
||||
wants := make([]float64, min(y, M)+1)
|
||||
count := 1000000
|
||||
for i := 0; i < count; i++ {
|
||||
x := hypergeometricBig(M, N, y, bytesToCoins(grand.GetRandom(4)))
|
||||
stats[x] += 1
|
||||
}
|
||||
|
||||
for i := 0; i < len(stats); i++ {
|
||||
stats[i] = stats[i] / float64(count)
|
||||
wants[i] = hyperProb(int64(i), M, N, y)
|
||||
}
|
||||
|
||||
epsilon := 0.001
|
||||
for i := 0; i < len(stats); i++ {
|
||||
assert.Less(t, math.Abs(stats[i]-wants[i]), epsilon)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package concentration
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"math/big"
|
||||
|
||||
"xdx.jelly/xgcl/gmath"
|
||||
"xdx.jelly/xgcl/grand"
|
||||
"xdx.jelly/xgcl/sm/sm2"
|
||||
"xdx.jelly/xgcl/sm/sm3"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrClientGenerateAgreementData = fmt.Errorf("client GenerateAgreementData error")
|
||||
)
|
||||
|
||||
type ClientKeyGenContext struct {
|
||||
ks *sm2.PrivateKey
|
||||
ppub *sm2.PublicKey
|
||||
clientPub *sm2.PublicKey
|
||||
}
|
||||
|
||||
func NewClientKeyGenContext() *ClientKeyGenContext {
|
||||
return &ClientKeyGenContext{}
|
||||
}
|
||||
|
||||
// GenerateTempKGC output data, and send to server
|
||||
// in: serverData, server临时公钥U
|
||||
// out: []byte, ppub_x || ppub_y || W_x || W_y || t || check
|
||||
func (c *ClientKeyGenContext) GenerateAgreementData(serverData []byte, clientID []byte) ([]byte, *sm2.PublicKey, error) {
|
||||
var data []byte
|
||||
var err error
|
||||
|
||||
serverTempPubkey := sm2.NewPublicKey()
|
||||
if err = serverTempPubkey.UnmarshalBinary(serverData); err != nil {
|
||||
return []byte{}, nil, ErrClientGenerateAgreementData
|
||||
}
|
||||
|
||||
if c.ks, err = sm2.GenPrivateKey(grand.GetRandom(sm2.ByteSize())); err != nil {
|
||||
return []byte{}, nil, ErrClientGenerateAgreementData
|
||||
}
|
||||
c.ppub = sm2.GenPublicKey(c.ks)
|
||||
data = make([]byte, 0, 128+32+4)
|
||||
data = append(data, gmath.BigIntToNByte(c.ppub.X, 32)...)
|
||||
data = append(data, gmath.BigIntToNByte(c.ppub.Y, 32)...)
|
||||
|
||||
w, err := sm2.GenPrivateKey(grand.GetRandom(sm2.ByteSize()))
|
||||
if err != nil {
|
||||
return []byte{}, nil, ErrClientGenerateAgreementData
|
||||
}
|
||||
W := sm2.GenPublicKey(w)
|
||||
data = append(data, gmath.BigIntToNByte(W.X, 32)...)
|
||||
data = append(data, gmath.BigIntToNByte(W.Y, 32)...)
|
||||
|
||||
// FIXME: W.X.Bytes() may not 32-bytes
|
||||
digest := sm3.Sum(W.Bytes(), sm2.PreComputeWithIdAndPubkey(clientID, c.ppub))
|
||||
// digest := sm3.Sum(W.X.Bytes(), W.Y.Bytes(), sm2.PreComputeWithIdAndPubkey(clientID, c.ppub))
|
||||
t := new(big.Int)
|
||||
t.SetBytes(digest[:])
|
||||
t.Mul(t, c.ks.D)
|
||||
t.Add(t, w.D)
|
||||
t.Mod(t, sm2.OrderN())
|
||||
data = append(data, gmath.BigIntToNByte(t, sm2.ByteSize())...)
|
||||
|
||||
checkSum := crc32.ChecksumIEEE(data)
|
||||
buf := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(buf, checkSum)
|
||||
data = append(data, buf...)
|
||||
|
||||
x, y := sm2.Curve256.ScalarBaseMult(t.Bytes())
|
||||
x, y = sm2.Curve().Add(x, y, serverTempPubkey.X, serverTempPubkey.Y)
|
||||
pk := &sm2.PublicKey{
|
||||
X: x,
|
||||
Y: y,
|
||||
}
|
||||
c.clientPub = sm2.NewPublicKey()
|
||||
c.clientPub.X.Set(x)
|
||||
c.clientPub.Y.Set(y)
|
||||
return data, pk, nil
|
||||
}
|
||||
|
||||
type ClientSignContext struct {
|
||||
k *sm2.PrivateKey
|
||||
r *big.Int
|
||||
}
|
||||
|
||||
func NewClientSignContext() *ClientSignContext {
|
||||
return &ClientSignContext{}
|
||||
}
|
||||
|
||||
// GenerateSignData 生成签名中间数据和r
|
||||
func (c *ClientSignContext) GenerateSignData(serverData []byte, e []byte) (dataToServer []byte, r []byte, err error) {
|
||||
c.k = sm2.NewPrivateKey()
|
||||
c.k.Random(grand.Reader)
|
||||
c.r = new(big.Int)
|
||||
c.r.SetBytes(e)
|
||||
tempPK := sm2.NewPublicKey()
|
||||
tempPK.X.SetBytes(serverData[:32])
|
||||
tempPK.Y.SetBytes(serverData[32:])
|
||||
x, _ := sm2.Curve256.ScalarMult(tempPK.X, tempPK.Y, c.k.Bytes())
|
||||
c.r.Add(c.r, x)
|
||||
c.r.Mod(c.r, sm2.OrderN())
|
||||
|
||||
return gmath.BigIntToNByte(c.k.D, 32), gmath.BigIntToNByte(c.r, 32), nil
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# 合并签名
|
||||
|
||||
## 密钥生成流程
|
||||
|
||||
## 签名流程
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
package concentration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"xdx.jelly/xgcl/gmath"
|
||||
"xdx.jelly/xgcl/grand"
|
||||
"xdx.jelly/xgcl/sm/sm2"
|
||||
)
|
||||
|
||||
func TestSign(t *testing.T) {
|
||||
fmt.Println("============= 生成密钥 =============")
|
||||
clientID := []byte("Teacher Tony")
|
||||
serverKeyGenCTX := NewServerKeyGenContext()
|
||||
clientKeyGenCTX := NewClientKeyGenContext()
|
||||
|
||||
// 1 server调用密码机获取32字节随机数。 GenerateAgreementData传出数据发送给客户端。
|
||||
data, err := serverKeyGenCTX.GenerateAgreementData(grand.GetRandom(sm2.ByteSize()))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
//2 client 调用生成数据和公钥
|
||||
data, clientPK, err := clientKeyGenCTX.GenerateAgreementData(data, clientID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 3 server 计算密钥对
|
||||
sk, serverPK, err := serverKeyGenCTX.ComputeKeyPair(data, clientID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 服务端清除敏感信息
|
||||
serverKeyGenCTX.Clear()
|
||||
|
||||
if !serverPK.Equals(clientPK) {
|
||||
panic("clientPK != serverPK")
|
||||
}
|
||||
fmt.Println("server pubkey: ", serverPK)
|
||||
fmt.Println("client pubkey: ", clientPK)
|
||||
fmt.Printf("sk: %02x\n", sk)
|
||||
fmt.Println()
|
||||
|
||||
fmt.Println("============= 签名 =============")
|
||||
e := grand.GetRandom(32)
|
||||
serverSignCTX := NewServerSignContext()
|
||||
clientSignCTX := NewClientSignContext()
|
||||
|
||||
data, err = serverSignCTX.GenerateSignData(grand.GetRandom(32))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
data, clientR, err := clientSignCTX.GenerateSignData(data, e)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sig, err := serverSignCTX.Sign(data, e, sk)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sk.Clear()
|
||||
fmt.Println("sig: ", sig)
|
||||
fmt.Printf("client's r: %02x\n", clientR)
|
||||
fmt.Println("serverPK verify: ", sm2.Verify(e, serverPK, sig))
|
||||
fmt.Println("clientPK verify: ", sm2.Verify(e, clientPK, sig))
|
||||
if (bytes.Compare(clientR, gmath.BigIntToNByte(sig.R, 32))) != 0 {
|
||||
panic("client r != server r")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestMarshal(t *testing.T) {
|
||||
ctx := NewServerSignContext()
|
||||
ctx.k1 = sm2.NewPrivateKey().Random(grand.Reader)
|
||||
ctx.u = sm2.NewPublicKey().Generate(ctx.k1)
|
||||
ctx.clientPPub = sm2.NewPublicKey().Generate(sm2.NewPrivateKey().Random(grand.Reader))
|
||||
buf, err := ctx.MarshalBinary()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Printf("%x\n\n", buf)
|
||||
|
||||
ctx1 := NewServerSignContext()
|
||||
err = ctx1.UnmarshalBinary(buf)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
buf, err = ctx1.MarshalBinary()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Printf("%x\n\n", buf)
|
||||
|
||||
if ctx1.k1.D.Cmp(ctx.k1.D) != 0 {
|
||||
t.Log()
|
||||
t.Fail()
|
||||
}
|
||||
if !ctx1.u.Equals(ctx.u) {
|
||||
t.Log()
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if !ctx1.clientPPub.Equals(ctx.clientPPub) {
|
||||
t.Log()
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
package concentration
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"math/big"
|
||||
|
||||
"xdx.jelly/xgcl/gmath"
|
||||
"xdx.jelly/xgcl/grand"
|
||||
"xdx.jelly/xgcl/sm/sm2"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrServerKeyGen = fmt.Errorf("GenerateAgreementData error")
|
||||
ErrInputDataInvalid = fmt.Errorf("input data invalid")
|
||||
ErrServerSignError = fmt.Errorf("server sign error")
|
||||
)
|
||||
|
||||
type ServerKeyGenContext struct {
|
||||
d *sm2.PrivateKey
|
||||
u *sm2.PublicKey
|
||||
clientPPub *sm2.PublicKey
|
||||
|
||||
//sk *sm2.PrivateKey
|
||||
//pk *sm2.PublicKey
|
||||
}
|
||||
|
||||
func NewServerKeyGenContext() *ServerKeyGenContext {
|
||||
return &ServerKeyGenContext{
|
||||
d: sm2.NewPrivateKey(),
|
||||
u: sm2.NewPublicKey(),
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateAgreementData 根据客户端发送的数据生成临时数据并发送给客户度
|
||||
func (s *ServerKeyGenContext) GenerateAgreementData(rnd32 []byte) ([]byte, error) {
|
||||
var data []byte
|
||||
var err error
|
||||
|
||||
if len(rnd32) < sm2.ByteSize() {
|
||||
rnd32 = grand.GetRandom(sm2.ByteSize())
|
||||
}
|
||||
if s.d, err = sm2.GenPrivateKey(rnd32); err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
s.u = sm2.GenPublicKey(s.d)
|
||||
if data, err = s.u.MarshalBinary(); err != nil {
|
||||
return []byte{}, ErrServerKeyGen
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// ComputeKeyPair clientData = ppub_x || ppub_y || W_x || W_y || t || check
|
||||
// out key pair
|
||||
func (s *ServerKeyGenContext) ComputeKeyPair(clientData []byte, clientID []byte) (*sm2.PrivateKey, *sm2.PublicKey, error) {
|
||||
if len(clientData) < 64+64+32+4 {
|
||||
return nil, nil, ErrInputDataInvalid
|
||||
}
|
||||
checkSumShouldBe := crc32.ChecksumIEEE(clientData[:64+64+32])
|
||||
checkSum := binary.BigEndian.Uint32(clientData[128+32:])
|
||||
if checkSum != checkSumShouldBe {
|
||||
return nil, nil, ErrInputDataInvalid
|
||||
}
|
||||
|
||||
sk := sm2.NewPrivateKey()
|
||||
sk.SetBytes(clientData[128 : 128+32])
|
||||
sk.D.Add(sk.D, s.d.D)
|
||||
sk.D.Mod(sk.D, sm2.OrderN())
|
||||
if gmath.IsBigInt0(sk.D) {
|
||||
return nil, nil, ErrServerKeyGen
|
||||
}
|
||||
pk := sm2.GenPublicKey(sk)
|
||||
//
|
||||
//ppub := sm2.NewPublicKey()
|
||||
//if err := ppub.UnmarshalBinary(clientData); err != nil{
|
||||
// return nil, nil, ErrServerKeyGen
|
||||
//}
|
||||
//
|
||||
//digest := sm3.Sum(clientData[64:128],sm2.PreComputeWithIdAndPubkey(clientID, ppub))
|
||||
//PublicKey := sm2.Curve256.ScalarMult(ppub.X, ppub.Y, digest[:])
|
||||
return sk, pk, nil
|
||||
}
|
||||
|
||||
func (s *ServerKeyGenContext) Clear() {
|
||||
s.d.Clear()
|
||||
}
|
||||
|
||||
type ServerSignContext struct {
|
||||
k1 *sm2.PrivateKey
|
||||
u *sm2.PublicKey
|
||||
clientPPub *sm2.PublicKey
|
||||
}
|
||||
|
||||
func NewServerSignContext() *ServerSignContext {
|
||||
return &ServerSignContext{
|
||||
sm2.NewPrivateKey(),
|
||||
sm2.NewPublicKey(),
|
||||
sm2.NewPublicKey(),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ServerSignContext) MarshalBinary() ([]byte, error) {
|
||||
if s.k1 == nil {
|
||||
s.k1 = sm2.NewPrivateKey()
|
||||
}
|
||||
if s.u == nil {
|
||||
s.u = sm2.NewPublicKey()
|
||||
}
|
||||
|
||||
if s.clientPPub == nil {
|
||||
s.clientPPub = sm2.NewPublicKey()
|
||||
}
|
||||
|
||||
r := make([]byte, 0, 3*4+(4+sm2.ECCRefMaxLen)+(4+2*sm2.ECCRefMaxLen)+(4+2*sm2.ECCRefMaxLen))
|
||||
var buf []byte
|
||||
var uintBuf = make([]byte, 4)
|
||||
var err error
|
||||
|
||||
if buf, err = s.k1.MarshalBinary(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
binary.BigEndian.PutUint32(uintBuf, uint32(len(buf)))
|
||||
r = append(r, uintBuf...)
|
||||
r = append(r, buf...)
|
||||
|
||||
if buf, err = s.u.MarshalBinary(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
binary.BigEndian.PutUint32(uintBuf, uint32(len(buf)))
|
||||
r = append(r, uintBuf...)
|
||||
r = append(r, buf...)
|
||||
|
||||
if buf, err = s.clientPPub.MarshalBinary(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
binary.BigEndian.PutUint32(uintBuf, uint32(len(buf)))
|
||||
r = append(r, uintBuf...)
|
||||
r = append(r, buf...)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (s *ServerSignContext) UnmarshalBinary(data []byte) error {
|
||||
|
||||
if s.k1 == nil {
|
||||
s.k1 = sm2.NewPrivateKey()
|
||||
}
|
||||
if s.u == nil {
|
||||
s.u = sm2.NewPublicKey()
|
||||
}
|
||||
|
||||
if s.clientPPub == nil {
|
||||
s.clientPPub = sm2.NewPublicKey()
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
if len(data) < 4 {
|
||||
return fmt.Errorf("invalid input of ServerSignContext.UnmarshalBinary")
|
||||
}
|
||||
size := binary.BigEndian.Uint32(data)
|
||||
data = data[4:]
|
||||
if uint32(len(data)) < size {
|
||||
return fmt.Errorf("invalid input of ServerSignContext.UnmarshalBinary")
|
||||
}
|
||||
if err = s.k1.UnmarshalBinary(data[:size]); err != nil {
|
||||
return err
|
||||
}
|
||||
data = data[size:]
|
||||
|
||||
if len(data) < 4 {
|
||||
return fmt.Errorf("invalid input of ServerSignContext.UnmarshalBinary")
|
||||
}
|
||||
size = binary.BigEndian.Uint32(data)
|
||||
data = data[4:]
|
||||
if uint32(len(data)) < size {
|
||||
return fmt.Errorf("invalid input of ServerSignContext.UnmarshalBinary")
|
||||
}
|
||||
if err = s.u.UnmarshalBinary(data[:size]); err != nil {
|
||||
return err
|
||||
}
|
||||
data = data[size:]
|
||||
|
||||
if len(data) < 4 {
|
||||
return fmt.Errorf("invalid input of ServerSignContext.UnmarshalBinary")
|
||||
}
|
||||
size = binary.BigEndian.Uint32(data)
|
||||
data = data[4:]
|
||||
if uint32(len(data)) < size {
|
||||
return fmt.Errorf("invalid input of ServerSignContext.UnmarshalBinary")
|
||||
}
|
||||
if err = s.clientPPub.UnmarshalBinary(data[:size]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ServerSignContext) GenerateSignData(rnd32 []byte) ([]byte, error) {
|
||||
var data []byte = make([]byte, 0, 64)
|
||||
var err error
|
||||
|
||||
if len(rnd32) < sm2.ByteSize() {
|
||||
rnd32 = grand.GetRandom(sm2.ByteSize())
|
||||
}
|
||||
if s.k1, err = sm2.GenPrivateKey(rnd32); err != nil {
|
||||
|
||||
}
|
||||
s.u = sm2.GenPublicKey(s.k1)
|
||||
data = append(data, gmath.BigIntToNByte(s.u.X, 32)...)
|
||||
data = append(data, gmath.BigIntToNByte(s.u.Y, 32)...)
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Sign generate signature
|
||||
// input: clientData 32 bytes
|
||||
func (ssc *ServerSignContext) Sign(clientData []byte, e []byte, sk *sm2.PrivateKey) (*sm2.Signature, error) {
|
||||
k := new(big.Int)
|
||||
k.SetBytes(clientData)
|
||||
k.Mul(k, ssc.k1.D)
|
||||
k.Mod(k, sm2.OrderN())
|
||||
x, _ := sm2.Curve256.ScalarBaseMult(k.Bytes())
|
||||
r := new(big.Int)
|
||||
r.SetBytes(e)
|
||||
r.Add(x, r)
|
||||
r.Mod(r, sm2.OrderN())
|
||||
|
||||
s := new(big.Int)
|
||||
s.Set(sk.D)
|
||||
s.Add(s, gmath.BigInt1)
|
||||
s.ModInverse(s, sm2.OrderN())
|
||||
k.Add(k, r)
|
||||
s.Mul(s, k)
|
||||
s.Sub(s, r)
|
||||
s.Mod(s, sm2.OrderN())
|
||||
sig := &sm2.Signature{
|
||||
R: r,
|
||||
S: s,
|
||||
}
|
||||
return sig, nil
|
||||
}
|
||||
|
||||
func (ssc *ServerSignContext) Clear() {
|
||||
ssc.k1.Clear()
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type SimpleStruct struct {
|
||||
n int
|
||||
}
|
||||
|
||||
func BenchmarkStructureFalseSharing(b *testing.B) {
|
||||
structA := SimpleStruct{}
|
||||
structB := SimpleStruct{}
|
||||
wg := sync.WaitGroup{}
|
||||
M := 1000000
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
for j := 0; j < M; j++ {
|
||||
structA.n += j
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
for j := 0; j < M; j++ {
|
||||
structB.n += j
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
wg.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCopyAVX2(b *testing.B) {
|
||||
a := make([]byte, 128*1024*1024)
|
||||
c := make([]byte, 128*1024*1024)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
CopySlice_AVX2(a, c, len(c))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCopy(b *testing.B) {
|
||||
a := make([]byte, 128*1024*1024)
|
||||
c := make([]byte, 128*1024*1024)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
CopySlice(a, c, len(c))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package main
|
||||
|
||||
func CopySlice_AVX2(dst, src []byte, len int)
|
||||
func CopySlice(dst, src []byte, len int) {
|
||||
for i := 0; i < len; i++ {
|
||||
dst[i] = src[i]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "textflag.h"
|
||||
|
||||
// func CopySlice_AVX2(dst, src []byte, len int)
|
||||
TEXT ·CopySlice_AVX2(SB), NOSPLIT, $0
|
||||
MOVQ dst_data+0(FP), DI
|
||||
MOVQ src_data+24(FP), SI
|
||||
MOVQ len+32(FP), BX
|
||||
MOVQ $0, AX
|
||||
|
||||
LOOP:
|
||||
VMOVDQU 0(SI)(AX*1), Y0
|
||||
VMOVDQU Y0, 0(DI)(AX*1)
|
||||
ADDQ $32, AX
|
||||
CMPQ AX, BX
|
||||
JL LOOP
|
||||
RET
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// +build go1.10
|
||||
|
||||
package main
|
||||
|
||||
//void SayHello(_GoString_ s);
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
C.SayHello("Hello, World\n")
|
||||
}
|
||||
|
||||
//export SayHello
|
||||
func SayHello(s string) {
|
||||
fmt.Print(s)
|
||||
}
|
||||
Reference in New Issue
Block a user