-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathcore.go
149 lines (137 loc) · 4.43 KB
/
core.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package bitcoin_ecies
import (
"bytes"
"crypto"
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"encoding/base64"
"errors"
"github.com/decred/dcrd/dcrec/secp256k1"
"reflect"
)
func AppendPKCS7Padding(data []byte, blockSize int) []byte {
padding := blockSize - len(data)%blockSize
return append(data, bytes.Repeat([]byte{byte(padding)}, padding)...)
}
func StripPKCS7Padding(data []byte, blockSize int) ([]byte, error) {
length := len(data)
if length%blockSize != 0 || length == 0 {
return nil, errors.New("invalid padding length")
}
padding := int(data[length-1])
if padding > blockSize {
return nil, errors.New("invalid padding byte (large)")
}
for _, v := range data[len(data)-padding:] {
if int(v) != padding {
return nil, errors.New("invalid padding byte (inconsistent)")
}
}
return data[:(length - padding)], nil
}
func AESEncryptWithIV(data, key, iv []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
data = AppendPKCS7Padding(data, block.BlockSize())
blockModel := cipher.NewCBCEncrypter(block, iv)
cipherText := make([]byte, len(data))
blockModel.CryptBlocks(cipherText, data)
return cipherText, nil
}
func AESDecryptWithIV(data, key, iv []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
blockModel := cipher.NewCBCDecrypter(block, iv)
plantText := make([]byte, len(data))
blockModel.CryptBlocks(plantText, data)
plantText, err = StripPKCS7Padding(plantText, block.BlockSize())
if err != nil {
return nil, err
}
return plantText, nil
}
//
// ECIES encryption/decryption methods; AES-128-CBC with PKCS7 is used as the cipher; hmac-sha256 is used as the mac
//
func EncryptMessage(message string, pubKey []byte) (string, error) {
publicKey, err := secp256k1.ParsePubKey(pubKey)
if err != nil {
return "", err
}
// Generate an ephemeral EC private key in order to derive shared secret(ECDH key)
ephemeralPrivateKey, err := secp256k1.GeneratePrivateKey()
if err != nil {
return "", err
}
// Derive ECDH key
x, y := publicKey.Curve.ScalarMult(publicKey.X, publicKey.Y, ephemeralPrivateKey.D.Bytes())
ecdhKey := secp256k1.NewPublicKey(x, y).SerializeCompressed()
// SHA512(ECDH_KEY), then we have
// key_e and iv used in AES
// key_m used in HMAC.SHA256
sha512 := crypto.SHA512.New()
sha512.Write(ecdhKey)
key := sha512.Sum(nil)
iv, keyE, keyM := key[0:16], key[16:32], key[32:]
// Make the AES encryption
cipherText, err := AESEncryptWithIV([]byte(message), keyE, iv)
if err != nil {
return "", err
}
ephemeralPublicKey := ephemeralPrivateKey.PubKey()
// encrypted = magic_bytes(4 bytes) + ephemeral_public_key(33 bytes) + cipher(16 bytes at least)
encrypted := append(append([]byte("BIE1"), ephemeralPublicKey.SerializeCompressed()...), cipherText...)
// mac = HMAC_SHA256(encrypted) 32 bytes
hmacSHA256 := hmac.New(crypto.SHA256.New, keyM)
hmacSHA256.Write(encrypted)
mac := hmacSHA256.Sum(nil)
// Give out base64(encrypted + mac), at least 85 bytes
return base64.StdEncoding.EncodeToString(append(encrypted, mac...)), nil
}
func DecryptMessage(message string, privKey []byte) (string, error) {
encrypted, err := base64.StdEncoding.DecodeString(message)
if err != nil {
return "", nil
}
if len(encrypted) < 85 {
return "", errors.New("invalid encrypted text: length")
}
magic := encrypted[:4]
ephemeralPubKey := encrypted[4:37]
cipherText := encrypted[37 : len(encrypted)-32]
mac := encrypted[len(encrypted)-32:]
if string(magic[:]) != "BIE1" {
return "", errors.New("invalid cipher text: invalid magic bytes")
}
privateKey, _ := secp256k1.PrivKeyFromBytes(privKey)
ephemeralPublicKey, err := secp256k1.ParsePubKey(ephemeralPubKey)
if err != nil {
return "", err
}
// Restore ECDH key
x, y := ephemeralPublicKey.Curve.ScalarMult(ephemeralPublicKey.X, ephemeralPublicKey.Y, privateKey.D.Bytes())
ecdhKey := secp256k1.NewPublicKey(x, y).SerializeCompressed()
// Restore key_e, iv and key_m
sha512 := crypto.SHA512.New()
sha512.Write(ecdhKey)
key := sha512.Sum(nil)
iv, keyE, keyM := key[0:16], key[16:32], key[32:]
// Verify mac
hmacSHA256 := hmac.New(crypto.SHA256.New, keyM)
hmacSHA256.Write(encrypted[:len(encrypted)-32])
macRecalculated := hmacSHA256.Sum(nil)
if !reflect.DeepEqual(mac, macRecalculated) {
return "", errors.New("incorrect password")
}
// Make the AES decryption
plain, err := AESDecryptWithIV(cipherText, keyE, iv)
if err != nil {
return "", err
}
return string(plain[:]), nil
}