init: v1.0.0

This commit is contained in:
yaole
2026-05-27 23:03:00 +08:00
commit 8d97f750eb
466 changed files with 80067 additions and 0 deletions
+52
View File
@@ -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
View File
@@ -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
View File
@@ -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")
}
+182
View File
@@ -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
View File
@@ -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")
}
}
}
+38
View File
@@ -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))
+115
View File
@@ -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
}
+135
View File
@@ -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
View File
@@ -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() {
}
+87
View File
@@ -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)
}
}
+107
View File
@@ -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
}
+7
View File
@@ -0,0 +1,7 @@
# 合并签名
## 密钥生成流程
## 签名流程
+114
View File
@@ -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()
}
}
+245
View File
@@ -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()
}
+54
View File
@@ -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))
}
}
+8
View File
@@ -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]
}
}
+17
View File
@@ -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
+19
View File
@@ -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)
}
+7
View File
@@ -0,0 +1,7 @@
# fpe - Format Preserving Encryption
保留格式加密
usage: TODO
# ope - Order Preserving Encryption
保序加密
TODO