initial import
This commit is contained in:
174
core/codec/aesecb.go
Normal file
174
core/codec/aesecb.go
Normal file
@@ -0,0 +1,174 @@
|
||||
package codec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
|
||||
"zero/core/logx"
|
||||
)
|
||||
|
||||
var ErrPaddingSize = errors.New("padding size error")
|
||||
|
||||
type ecb struct {
|
||||
b cipher.Block
|
||||
blockSize int
|
||||
}
|
||||
|
||||
func newECB(b cipher.Block) *ecb {
|
||||
return &ecb{
|
||||
b: b,
|
||||
blockSize: b.BlockSize(),
|
||||
}
|
||||
}
|
||||
|
||||
type ecbEncrypter ecb
|
||||
|
||||
func NewECBEncrypter(b cipher.Block) cipher.BlockMode {
|
||||
return (*ecbEncrypter)(newECB(b))
|
||||
}
|
||||
|
||||
func (x *ecbEncrypter) BlockSize() int { return x.blockSize }
|
||||
|
||||
// why we don't return error is because cipher.BlockMode doesn't allow this
|
||||
func (x *ecbEncrypter) CryptBlocks(dst, src []byte) {
|
||||
if len(src)%x.blockSize != 0 {
|
||||
logx.Error("crypto/cipher: input not full blocks")
|
||||
return
|
||||
}
|
||||
if len(dst) < len(src) {
|
||||
logx.Error("crypto/cipher: output smaller than input")
|
||||
return
|
||||
}
|
||||
|
||||
for len(src) > 0 {
|
||||
x.b.Encrypt(dst, src[:x.blockSize])
|
||||
src = src[x.blockSize:]
|
||||
dst = dst[x.blockSize:]
|
||||
}
|
||||
}
|
||||
|
||||
type ecbDecrypter ecb
|
||||
|
||||
func NewECBDecrypter(b cipher.Block) cipher.BlockMode {
|
||||
return (*ecbDecrypter)(newECB(b))
|
||||
}
|
||||
|
||||
func (x *ecbDecrypter) BlockSize() int {
|
||||
return x.blockSize
|
||||
}
|
||||
|
||||
// why we don't return error is because cipher.BlockMode doesn't allow this
|
||||
func (x *ecbDecrypter) CryptBlocks(dst, src []byte) {
|
||||
if len(src)%x.blockSize != 0 {
|
||||
logx.Error("crypto/cipher: input not full blocks")
|
||||
return
|
||||
}
|
||||
if len(dst) < len(src) {
|
||||
logx.Error("crypto/cipher: output smaller than input")
|
||||
return
|
||||
}
|
||||
|
||||
for len(src) > 0 {
|
||||
x.b.Decrypt(dst, src[:x.blockSize])
|
||||
src = src[x.blockSize:]
|
||||
dst = dst[x.blockSize:]
|
||||
}
|
||||
}
|
||||
|
||||
func EcbDecrypt(key, src []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
logx.Errorf("Decrypt key error: % x", key)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
decrypter := NewECBDecrypter(block)
|
||||
decrypted := make([]byte, len(src))
|
||||
decrypter.CryptBlocks(decrypted, src)
|
||||
|
||||
return pkcs5Unpadding(decrypted, decrypter.BlockSize())
|
||||
}
|
||||
|
||||
func EcbDecryptBase64(key, src string) (string, error) {
|
||||
keyBytes, err := getKeyBytes(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
encryptedBytes, err := base64.StdEncoding.DecodeString(src)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
decryptedBytes, err := EcbDecrypt(keyBytes, encryptedBytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString(decryptedBytes), nil
|
||||
}
|
||||
|
||||
func EcbEncrypt(key, src []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
logx.Errorf("Encrypt key error: % x", key)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
padded := pkcs5Padding(src, block.BlockSize())
|
||||
crypted := make([]byte, len(padded))
|
||||
encrypter := NewECBEncrypter(block)
|
||||
encrypter.CryptBlocks(crypted, padded)
|
||||
|
||||
return crypted, nil
|
||||
}
|
||||
|
||||
func EcbEncryptBase64(key, src string) (string, error) {
|
||||
keyBytes, err := getKeyBytes(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
srcBytes, err := base64.StdEncoding.DecodeString(src)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
encryptedBytes, err := EcbEncrypt(keyBytes, srcBytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString(encryptedBytes), nil
|
||||
}
|
||||
|
||||
func getKeyBytes(key string) ([]byte, error) {
|
||||
if len(key) > 32 {
|
||||
if keyBytes, err := base64.StdEncoding.DecodeString(key); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return keyBytes, nil
|
||||
}
|
||||
}
|
||||
|
||||
return []byte(key), nil
|
||||
}
|
||||
|
||||
func pkcs5Padding(ciphertext []byte, blockSize int) []byte {
|
||||
padding := blockSize - len(ciphertext)%blockSize
|
||||
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||
return append(ciphertext, padtext...)
|
||||
}
|
||||
|
||||
func pkcs5Unpadding(src []byte, blockSize int) ([]byte, error) {
|
||||
length := len(src)
|
||||
unpadding := int(src[length-1])
|
||||
if unpadding >= length || unpadding > blockSize {
|
||||
return nil, ErrPaddingSize
|
||||
}
|
||||
|
||||
return src[:length-unpadding], nil
|
||||
}
|
||||
88
core/codec/dh.go
Normal file
88
core/codec/dh.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package codec
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// see https://www.zhihu.com/question/29383090/answer/70435297
|
||||
// see https://www.ietf.org/rfc/rfc3526.txt
|
||||
// 2048-bit MODP Group
|
||||
|
||||
var (
|
||||
ErrInvalidPriKey = errors.New("invalid private key")
|
||||
ErrInvalidPubKey = errors.New("invalid public key")
|
||||
ErrPubKeyOutOfBound = errors.New("public key out of bound")
|
||||
|
||||
p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16)
|
||||
g, _ = new(big.Int).SetString("2", 16)
|
||||
zero = big.NewInt(0)
|
||||
)
|
||||
|
||||
type DhKey struct {
|
||||
PriKey *big.Int
|
||||
PubKey *big.Int
|
||||
}
|
||||
|
||||
func ComputeKey(pubKey, priKey *big.Int) (*big.Int, error) {
|
||||
if pubKey == nil {
|
||||
return nil, ErrInvalidPubKey
|
||||
}
|
||||
|
||||
if pubKey.Sign() <= 0 && p.Cmp(pubKey) <= 0 {
|
||||
return nil, ErrPubKeyOutOfBound
|
||||
}
|
||||
|
||||
if priKey == nil {
|
||||
return nil, ErrInvalidPriKey
|
||||
}
|
||||
|
||||
return new(big.Int).Exp(pubKey, priKey, p), nil
|
||||
}
|
||||
|
||||
func GenerateKey() (*DhKey, error) {
|
||||
var err error
|
||||
var x *big.Int
|
||||
|
||||
for {
|
||||
x, err = rand.Int(rand.Reader, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if zero.Cmp(x) < 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
key := new(DhKey)
|
||||
key.PriKey = x
|
||||
key.PubKey = new(big.Int).Exp(g, x, p)
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func NewPublicKey(bs []byte) *big.Int {
|
||||
return new(big.Int).SetBytes(bs)
|
||||
}
|
||||
|
||||
func (k *DhKey) Bytes() []byte {
|
||||
if k.PubKey == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
byteLen := (p.BitLen() + 7) >> 3
|
||||
ret := make([]byte, byteLen)
|
||||
copyWithLeftPad(ret, k.PubKey.Bytes())
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func copyWithLeftPad(dst, src []byte) {
|
||||
padBytes := len(dst) - len(src)
|
||||
for i := 0; i < padBytes; i++ {
|
||||
dst[i] = 0
|
||||
}
|
||||
copy(dst[padBytes:], src)
|
||||
}
|
||||
73
core/codec/dh_test.go
Normal file
73
core/codec/dh_test.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package codec
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDiffieHellman(t *testing.T) {
|
||||
key1, err := GenerateKey()
|
||||
assert.Nil(t, err)
|
||||
key2, err := GenerateKey()
|
||||
assert.Nil(t, err)
|
||||
|
||||
pubKey1, err := ComputeKey(key1.PubKey, key2.PriKey)
|
||||
assert.Nil(t, err)
|
||||
pubKey2, err := ComputeKey(key2.PubKey, key1.PriKey)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, pubKey1, pubKey2)
|
||||
}
|
||||
|
||||
func TestDiffieHellman1024(t *testing.T) {
|
||||
old := p
|
||||
p, _ = new(big.Int).SetString("F488FD584E49DBCD20B49DE49107366B336C380D451D0F7C88B31C7C5B2D8EF6F3C923C043F0A55B188D8EBB558CB85D38D334FD7C175743A31D186CDE33212CB52AFF3CE1B1294018118D7C84A70A72D686C40319C807297ACA950CD9969FABD00A509B0246D3083D66A45D419F9C7CBD894B221926BAABA25EC355E92F78C7", 16)
|
||||
defer func() {
|
||||
p = old
|
||||
}()
|
||||
|
||||
key1, err := GenerateKey()
|
||||
assert.Nil(t, err)
|
||||
key2, err := GenerateKey()
|
||||
assert.Nil(t, err)
|
||||
|
||||
pubKey1, err := ComputeKey(key1.PubKey, key2.PriKey)
|
||||
assert.Nil(t, err)
|
||||
pubKey2, err := ComputeKey(key2.PubKey, key1.PriKey)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, pubKey1, pubKey2)
|
||||
}
|
||||
|
||||
func TestDiffieHellmanMiddleManAttack(t *testing.T) {
|
||||
key1, err := GenerateKey()
|
||||
assert.Nil(t, err)
|
||||
keyMiddle, err := GenerateKey()
|
||||
assert.Nil(t, err)
|
||||
key2, err := GenerateKey()
|
||||
assert.Nil(t, err)
|
||||
|
||||
const aesByteLen = 32
|
||||
pubKey1, err := ComputeKey(keyMiddle.PubKey, key1.PriKey)
|
||||
assert.Nil(t, err)
|
||||
src := []byte(`hello, world!`)
|
||||
encryptedSrc, err := EcbEncrypt(pubKey1.Bytes()[:aesByteLen], src)
|
||||
assert.Nil(t, err)
|
||||
pubKeyMiddle, err := ComputeKey(key1.PubKey, keyMiddle.PriKey)
|
||||
assert.Nil(t, err)
|
||||
decryptedSrc, err := EcbDecrypt(pubKeyMiddle.Bytes()[:aesByteLen], encryptedSrc)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, string(src), string(decryptedSrc))
|
||||
|
||||
pubKeyMiddle, err = ComputeKey(key2.PubKey, keyMiddle.PriKey)
|
||||
assert.Nil(t, err)
|
||||
encryptedSrc, err = EcbEncrypt(pubKeyMiddle.Bytes()[:aesByteLen], decryptedSrc)
|
||||
assert.Nil(t, err)
|
||||
pubKey2, err := ComputeKey(keyMiddle.PubKey, key2.PriKey)
|
||||
assert.Nil(t, err)
|
||||
decryptedSrc, err = EcbDecrypt(pubKey2.Bytes()[:aesByteLen], encryptedSrc)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, string(src), string(decryptedSrc))
|
||||
}
|
||||
33
core/codec/gzip.go
Normal file
33
core/codec/gzip.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package codec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"io"
|
||||
)
|
||||
|
||||
func Gzip(bs []byte) []byte {
|
||||
var b bytes.Buffer
|
||||
|
||||
w := gzip.NewWriter(&b)
|
||||
w.Write(bs)
|
||||
w.Close()
|
||||
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
func Gunzip(bs []byte) ([]byte, error) {
|
||||
r, err := gzip.NewReader(bytes.NewBuffer(bs))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
var c bytes.Buffer
|
||||
_, err = io.Copy(&c, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.Bytes(), nil
|
||||
}
|
||||
23
core/codec/gzip_test.go
Normal file
23
core/codec/gzip_test.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package codec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGzip(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < 10000; i++ {
|
||||
fmt.Fprint(&buf, i)
|
||||
}
|
||||
|
||||
bs := Gzip(buf.Bytes())
|
||||
actual, err := Gunzip(bs)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, len(bs) < buf.Len())
|
||||
assert.Equal(t, buf.Bytes(), actual)
|
||||
}
|
||||
18
core/codec/hmac.go
Normal file
18
core/codec/hmac.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package codec
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
)
|
||||
|
||||
func Hmac(key []byte, body string) []byte {
|
||||
h := hmac.New(sha256.New, key)
|
||||
io.WriteString(h, body)
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
func HmacBase64(key []byte, body string) string {
|
||||
return base64.StdEncoding.EncodeToString(Hmac(key, body))
|
||||
}
|
||||
149
core/codec/rsa.go
Normal file
149
core/codec/rsa.go
Normal file
@@ -0,0 +1,149 @@
|
||||
package codec
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrPrivateKey = errors.New("private key error")
|
||||
ErrPublicKey = errors.New("failed to parse PEM block containing the public key")
|
||||
ErrNotRsaKey = errors.New("key type is not RSA")
|
||||
)
|
||||
|
||||
type (
|
||||
RsaDecrypter interface {
|
||||
Decrypt(input []byte) ([]byte, error)
|
||||
DecryptBase64(input string) ([]byte, error)
|
||||
}
|
||||
|
||||
RsaEncrypter interface {
|
||||
Encrypt(input []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
rsaBase struct {
|
||||
bytesLimit int
|
||||
}
|
||||
|
||||
rsaDecrypter struct {
|
||||
rsaBase
|
||||
privateKey *rsa.PrivateKey
|
||||
}
|
||||
|
||||
rsaEncrypter struct {
|
||||
rsaBase
|
||||
publicKey *rsa.PublicKey
|
||||
}
|
||||
)
|
||||
|
||||
func NewRsaDecrypter(file string) (RsaDecrypter, error) {
|
||||
content, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(content)
|
||||
if block == nil {
|
||||
return nil, ErrPrivateKey
|
||||
}
|
||||
|
||||
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &rsaDecrypter{
|
||||
rsaBase: rsaBase{
|
||||
bytesLimit: privateKey.N.BitLen() >> 3,
|
||||
},
|
||||
privateKey: privateKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *rsaDecrypter) Decrypt(input []byte) ([]byte, error) {
|
||||
return r.crypt(input, func(block []byte) ([]byte, error) {
|
||||
return rsaDecryptBlock(r.privateKey, block)
|
||||
})
|
||||
}
|
||||
|
||||
func (r *rsaDecrypter) DecryptBase64(input string) ([]byte, error) {
|
||||
if len(input) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
base64Decoded, err := base64.StdEncoding.DecodeString(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.Decrypt(base64Decoded)
|
||||
}
|
||||
|
||||
func NewRsaEncrypter(key []byte) (RsaEncrypter, error) {
|
||||
block, _ := pem.Decode(key)
|
||||
if block == nil {
|
||||
return nil, ErrPublicKey
|
||||
}
|
||||
|
||||
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch pubKey := pub.(type) {
|
||||
case *rsa.PublicKey:
|
||||
return &rsaEncrypter{
|
||||
rsaBase: rsaBase{
|
||||
// https://www.ietf.org/rfc/rfc2313.txt
|
||||
// The length of the data D shall not be more than k-11 octets, which is
|
||||
// positive since the length k of the modulus is at least 12 octets.
|
||||
bytesLimit: (pubKey.N.BitLen() >> 3) - 11,
|
||||
},
|
||||
publicKey: pubKey,
|
||||
}, nil
|
||||
default:
|
||||
return nil, ErrNotRsaKey
|
||||
}
|
||||
}
|
||||
|
||||
func (r *rsaEncrypter) Encrypt(input []byte) ([]byte, error) {
|
||||
return r.crypt(input, func(block []byte) ([]byte, error) {
|
||||
return rsaEncryptBlock(r.publicKey, block)
|
||||
})
|
||||
}
|
||||
|
||||
func (r *rsaBase) crypt(input []byte, cryptFn func([]byte) ([]byte, error)) ([]byte, error) {
|
||||
var result []byte
|
||||
inputLen := len(input)
|
||||
|
||||
for i := 0; i*r.bytesLimit < inputLen; i++ {
|
||||
start := r.bytesLimit * i
|
||||
var stop int
|
||||
if r.bytesLimit*(i+1) > inputLen {
|
||||
stop = inputLen
|
||||
} else {
|
||||
stop = r.bytesLimit * (i + 1)
|
||||
}
|
||||
bs, err := cryptFn(input[start:stop])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result = append(result, bs...)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func rsaDecryptBlock(privateKey *rsa.PrivateKey, block []byte) ([]byte, error) {
|
||||
return rsa.DecryptPKCS1v15(rand.Reader, privateKey, block)
|
||||
}
|
||||
|
||||
func rsaEncryptBlock(publicKey *rsa.PublicKey, msg []byte) ([]byte, error) {
|
||||
return rsa.EncryptPKCS1v15(rand.Reader, publicKey, msg)
|
||||
}
|
||||
Reference in New Issue
Block a user