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) }