From: qydysky Date: Thu, 24 Apr 2025 19:49:18 +0000 (+0800) Subject: 1 (#48) X-Git-Tag: v0.28.20250424194925 X-Git-Url: http://127.0.0.1:8081/?a=commitdiff_plain;h=aa6e47943e45f49ee90f1999ce4e9a3e00905d5d;p=part%2F.git 1 (#48) * 1 * 1 --- diff --git a/.github/workflows/test1.yml b/.github/workflows/test1.yml index e68d549..1922382 100644 --- a/.github/workflows/test1.yml +++ b/.github/workflows/test1.yml @@ -41,7 +41,7 @@ jobs: go test -count 1 -timeout 10s -v -race github.com/qydysky/part/slice go test -count 1 -timeout 5s -v -race github.com/qydysky/part/bools go test -count 1 -timeout 5s -v -race github.com/qydysky/part/errors - go test -count 1 -timeout 5s -v -race github.com/qydysky/part/crypto + go test -count 1 -timeout 5s -v -race github.com/qydysky/part/crypto/... go test -count 1 -timeout 5s -v -race github.com/qydysky/part/flag -sss=ss -i32=32 -f34=34 -btrue=true -d1m=1m w-test: @@ -80,7 +80,7 @@ jobs: go test -count 1 -timeout 10s -v -race github.com/qydysky/part/slice go test -count 1 -timeout 5s -v -race github.com/qydysky/part/bools go test -count 1 -timeout 5s -v -race github.com/qydysky/part/errors - go test -count 1 -timeout 5s -v -race github.com/qydysky/part/crypto + go test -count 1 -timeout 5s -v -race github.com/qydysky/part/crypto/... go test -count 1 -timeout 5s -v -race github.com/qydysky/part/flag -sss=ss -i32=32 -f34=34 -btrue=true -d1m=1m u-test: @@ -119,5 +119,5 @@ jobs: go test -count 1 -timeout 10s -v -race github.com/qydysky/part/slice go test -count 1 -timeout 5s -v -race github.com/qydysky/part/bools go test -count 1 -timeout 5s -v -race github.com/qydysky/part/errors - go test -count 1 -timeout 5s -v -race github.com/qydysky/part/crypto + go test -count 1 -timeout 5s -v -race github.com/qydysky/part/crypto/... go test -count 1 -timeout 5s -v -race github.com/qydysky/part/flag -sss=ss -i32=32 -f34=34 -btrue=true -d1m=1m diff --git a/crypto/EasyCrypt.go b/crypto/EasyCrypt.go index bc3694e..b335dfe 100644 --- a/crypto/EasyCrypt.go +++ b/crypto/EasyCrypt.go @@ -16,6 +16,7 @@ var ( ErrPemType = errors.New(`ErrPemType`) ) +// Deprecated: 使用 asymmetric 包 func NewKey() (pri, pub []byte, e error) { if p1, e := ecdh.X25519().GenerateKey(rand.Reader); e != nil { return nil, nil, e @@ -30,6 +31,7 @@ func NewKey() (pri, pub []byte, e error) { } } +// Deprecated: 使用 asymmetric 包 func Encrypt(msg, pubKey []byte) (b []byte, e error) { c := ecdh.X25519() var ( @@ -70,6 +72,7 @@ func Encrypt(msg, pubKey []byte) (b []byte, e error) { return } +// Deprecated: 使用 asymmetric 包 func Decrypt(b, priKey []byte) (msg []byte, e error) { var ( q1 *ecdh.PublicKey diff --git a/crypto/asymmetric/mlkem.go b/crypto/asymmetric/mlkem.go new file mode 100644 index 0000000..ac133a2 --- /dev/null +++ b/crypto/asymmetric/mlkem.go @@ -0,0 +1,73 @@ +package part + +import ( + "crypto/mlkem" + "encoding/pem" + + pc "github.com/qydysky/part/crypto" +) + +type Mlkem struct{} + +var MlkemF pc.Asymmetric = Mlkem{} + +func (t Mlkem) GetType() string { + return `MLKEM` +} +func (t Mlkem) CheckType(b *pem.Block) (ok bool, isPriKey bool) { + isPriKey = b.Type == t.GetType()+PriKeySuf + if !isPriKey { + ok = b.Type == t.GetType()+PubKeySuf + } else { + ok = true + } + return +} + +func (t Mlkem) Decrypt(priKey *pem.Block) (dec pc.AsymmetricDec, e error) { + if priKey.Type != t.GetType()+PriKeySuf { + return nil, ErrType + } else if d, err := mlkem.NewDecapsulationKey1024(priKey.Bytes); err != nil { + return nil, err + } else { + return func(sym pc.Symmetric, b, exchangeTxt []byte) (msg []byte, e error) { + if sharedKey, err := d.Decapsulate(exchangeTxt); err != nil { + return nil, err + } else { + return sym.Decrypt(b, sharedKey) + } + }, nil + } +} + +func (t Mlkem) Encrypt(pubKey *pem.Block) (enc pc.AsymmetricEnc, e error) { + if pubKey.Type != t.GetType()+PubKeySuf { + return nil, ErrType + } else if d, err := mlkem.NewEncapsulationKey1024(pubKey.Bytes); err != nil { + return nil, err + } else { + return func(sym pc.Symmetric, msg []byte) (b, exchangeTxt []byte, e error) { + sharedKey, ciphertext := d.Encapsulate() + b, e = sym.Encrypt(msg, sharedKey) + if e != nil { + return nil, nil, e + } + return b, ciphertext, nil + }, nil + } +} + +func (t Mlkem) NewKey() (pri, pub *pem.Block, e error) { + var d *mlkem.DecapsulationKey1024 + d, e = mlkem.GenerateKey1024() + if e != nil { + return + } + return &pem.Block{ + Type: t.GetType() + PriKeySuf, + Bytes: d.Bytes(), + }, &pem.Block{ + Type: t.GetType() + PubKeySuf, + Bytes: d.EncapsulationKey().Bytes(), + }, nil +} diff --git a/crypto/asymmetric/mlkem_test.go b/crypto/asymmetric/mlkem_test.go new file mode 100644 index 0000000..11cadad --- /dev/null +++ b/crypto/asymmetric/mlkem_test.go @@ -0,0 +1,39 @@ +package part + +import ( + "bytes" + "crypto/rand" + "testing" + + pcs "github.com/qydysky/part/crypto/symmetric" +) + +func Test_Mlkem(t *testing.T) { + var buf = make([]byte, 100) + if n, e := rand.Read(buf); e != nil { + t.Fatal(e) + } else { + buf = buf[:n] + } + + m := MlkemF + sym := pcs.Chacha20poly1305F + if pri, pub, e := MlkemF.NewKey(); e != nil { + t.Fatal(e) + } else { + if enc, e := m.Encrypt(pub); e != nil { + t.Fatal(e) + } else if b, ex, e := enc(sym, buf); e != nil { + t.Fatal() + } else { + b, ex = Unpack(Pack(b, ex)) + if dec, e := m.Decrypt(pri); e != nil { + t.Fatal(e) + } else if msg, e := dec(sym, b, ex); e != nil { + t.Fatal(e) + } else if !bytes.Equal(msg, buf) { + t.Fatal() + } + } + } +} diff --git a/crypto/asymmetric/util.go b/crypto/asymmetric/util.go new file mode 100644 index 0000000..7452ec0 --- /dev/null +++ b/crypto/asymmetric/util.go @@ -0,0 +1,51 @@ +package part + +import ( + "encoding/pem" + "errors" + + pc "github.com/qydysky/part/crypto" +) + +var ( + PriKeySuf string = ` PRIVATE KEY` + PubKeySuf string = ` PUBLIC KEY` + ErrType error = errors.New(`ErrType`) +) + +func ChoseAsymmetricByPem(b *pem.Block) pc.Asymmetric { + if ok, _ := X25519F.CheckType(b); ok { + return X25519F + } else if ok, _ := MlkemF.CheckType(b); ok { + return MlkemF + } else { + return nil + } +} + +func Pack(b, exchangeTxt []byte) (a []byte) { + buf := make([]byte, 4+len(exchangeTxt)+len(b)) + n := copy(buf, itob32(int32(len(exchangeTxt)))) + n += copy(buf[n:], exchangeTxt) + copy(buf[n:], b) + return buf +} + +func Unpack(a []byte) (b, exchangeTxt []byte) { + exL := btoi32(a[:4]) + return a[4+exL:], a[4 : 4+exL] +} + +func itob32(v int32) []byte { + //binary.BigEndian.PutUint32 + b := make([]byte, 4) + b[0] = byte(v >> 24) + b[1] = byte(v >> 16) + b[2] = byte(v >> 8) + b[3] = byte(v) + return b +} + +func btoi32(bu []byte) uint32 { + return uint32(bu[3]) | uint32(bu[2])<<8 | uint32(bu[1])<<16 | uint32(bu[0])<<24 +} diff --git a/crypto/asymmetric/x25519.go b/crypto/asymmetric/x25519.go new file mode 100644 index 0000000..c55ff21 --- /dev/null +++ b/crypto/asymmetric/x25519.go @@ -0,0 +1,91 @@ +package part + +import ( + "crypto/ecdh" + "crypto/rand" + "encoding/pem" + + pc "github.com/qydysky/part/crypto" +) + +var X25519F pc.Asymmetric = X25519{} + +type X25519 struct{} + +// CheckType implements part.Asymmetric. +func (t X25519) CheckType(b *pem.Block) (ok bool, isPriKey bool) { + isPriKey = b.Type == t.GetType()+PriKeySuf + if !isPriKey { + ok = b.Type == t.GetType()+PubKeySuf + } else { + ok = true + } + return +} + +func (t X25519) GetType() string { + return `ECDH` // 为了保证向后兼容,此处为ECDH +} + +func (t X25519) Decrypt(priKey *pem.Block) (dec pc.AsymmetricDec, e error) { + if priKey.Type != t.GetType()+PriKeySuf { + return nil, ErrType + } + + var p2 *ecdh.PrivateKey + if p2, e = ecdh.X25519().NewPrivateKey(priKey.Bytes); e != nil { + return + } + + return func(sym pc.Symmetric, b, exchangeTxt []byte) (msg []byte, e error) { + if q1, err := ecdh.X25519().NewPublicKey(exchangeTxt); err != nil { + return nil, err + } else if key, err := p2.ECDH(q1); err != nil { + return nil, err + } else { + return sym.Decrypt(b, key) + } + }, nil +} + +func (t X25519) Encrypt(pubKey *pem.Block) (enc pc.AsymmetricEnc, e error) { + if pubKey.Type != t.GetType()+PubKeySuf { + return nil, ErrType + } + + var ( + p1 *ecdh.PrivateKey + q1 *ecdh.PublicKey + q2 *ecdh.PublicKey + key []byte + ) + if p1, e = ecdh.X25519().GenerateKey(rand.Reader); e != nil { + return + } + q1 = p1.PublicKey() + if q2, e = ecdh.X25519().NewPublicKey(pubKey.Bytes); e != nil { + return + } else if key, e = p1.ECDH(q2); e != nil { + return + } else { + return func(sym pc.Symmetric, msg []byte) (b []byte, exchangeTxt []byte, e error) { + b, e = sym.Encrypt(msg, key) + exchangeTxt = q1.Bytes() + return + }, nil + } +} + +func (t X25519) NewKey() (pri, pub *pem.Block, e error) { + if d, e := ecdh.X25519().GenerateKey(rand.Reader); e != nil { + return nil, nil, e + } else { + return &pem.Block{ + Type: t.GetType() + PriKeySuf, + Bytes: d.Bytes(), + }, &pem.Block{ + Type: t.GetType() + PubKeySuf, + Bytes: d.PublicKey().Bytes(), + }, nil + } +} diff --git a/crypto/asymmetric/x25519_test.go b/crypto/asymmetric/x25519_test.go new file mode 100644 index 0000000..5ac19a3 --- /dev/null +++ b/crypto/asymmetric/x25519_test.go @@ -0,0 +1,39 @@ +package part + +import ( + "bytes" + "crypto/rand" + "testing" + + pcs "github.com/qydysky/part/crypto/symmetric" +) + +func Test_X25519(t *testing.T) { + var buf = make([]byte, 100) + if n, e := rand.Read(buf); e != nil { + t.Fatal(e) + } else { + buf = buf[:n] + } + + m := X25519F + sym := pcs.Chacha20poly1305F + if pri, pub, e := m.NewKey(); e != nil { + t.Fatal(e) + } else { + if enc, e := m.Encrypt(pub); e != nil { + t.Fatal(e) + } else if b, ex, e := enc(sym, buf); e != nil { + t.Fatal() + } else { + b, ex = Unpack(Pack(b, ex)) + if dec, e := m.Decrypt(pri); e != nil { + t.Fatal(e) + } else if msg, e := dec(sym, b, ex); e != nil { + t.Fatal(e) + } else if !bytes.Equal(msg, buf) { + t.Fatal() + } + } + } +} diff --git a/crypto/crypto.go b/crypto/crypto.go new file mode 100644 index 0000000..b40daca --- /dev/null +++ b/crypto/crypto.go @@ -0,0 +1,25 @@ +package part + +import ( + "encoding/pem" +) + +type Asymmetric interface { + GetType() string + CheckType(b *pem.Block) (ok bool, isPriKey bool) + NewKey() (pri, pub *pem.Block, e error) + Encrypt(pubKey *pem.Block) (enc AsymmetricEnc, e error) + Decrypt(priKey *pem.Block) (dec AsymmetricDec, e error) +} + +// func(sym Symmetric, msg []byte) (b, exchangeTxt []byte, e error) +type AsymmetricEnc func(sym Symmetric, msg []byte) (b, exchangeTxt []byte, e error) + +// func(sym Symmetric, b, exchangeTxt []byte) (msg []byte, e error) +type AsymmetricDec func(sym Symmetric, b, exchangeTxt []byte) (msg []byte, e error) + +type Symmetric interface { + GetType() string + Encrypt(msg, key []byte) (b []byte, e error) + Decrypt(b, key []byte) (msg []byte, e error) +} diff --git a/crypto/symmetric/chacha20poly1305.go b/crypto/symmetric/chacha20poly1305.go new file mode 100644 index 0000000..804c8e6 --- /dev/null +++ b/crypto/symmetric/chacha20poly1305.go @@ -0,0 +1,42 @@ +package part + +import ( + "crypto/rand" + + pcrypto "github.com/qydysky/part/crypto" + "golang.org/x/crypto/chacha20poly1305" +) + +type Chacha20poly1305 struct{} + +var Chacha20poly1305F pcrypto.Symmetric = Chacha20poly1305{} + +// GetType implements part.Symmetric. +func (c Chacha20poly1305) GetType() string { + return `CHACHA20POLY1305` +} + +// Decrypt implements part.Symmetric. +func (c Chacha20poly1305) Decrypt(b []byte, key []byte) (msg []byte, e error) { + if aead, err := chacha20poly1305.NewX(key); err != nil { + return nil, err + } else { + nonce, ciphertext := b[:aead.NonceSize()], b[aead.NonceSize():] + return aead.Open(nil, nonce, ciphertext, nil) + } +} + +// Encrypt implements part.Symmetric. +func (c Chacha20poly1305) Encrypt(msg []byte, key []byte) (b []byte, e error) { + if aead, err := chacha20poly1305.NewX(key); err != nil { + return nil, err + } else { + nonce := make([]byte, aead.NonceSize(), aead.NonceSize()+len(msg)+aead.Overhead()) + if n, err := rand.Read(nonce); err != nil { + return nil, err + } else { + nonce = nonce[:n] + return aead.Seal(nonce, nonce, msg, nil), nil + } + } +}