cubefs

Форк
0
/
cryptoutil.go 
244 строки · 6.2 Кб
1
// Copyright 2018 The CubeFS Authors.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12
// implied. See the License for the specific language governing
13
// permissions and limitations under the License.
14

15
package cryptoutil
16

17
import (
18
	"bytes"
19
	"crypto/aes"
20
	"crypto/cipher"
21
	"crypto/hmac"
22
	"crypto/md5"
23
	"crypto/rand"
24
	"crypto/sha256"
25
	"crypto/tls"
26
	"crypto/x509"
27
	"encoding/base64"
28
	"encoding/binary"
29
	"fmt"
30
	"io"
31
	rand2 "math/rand"
32
	"net/http"
33
	"strconv"
34
	"time"
35
	"unsafe"
36
)
37

38
func pad(src []byte) []byte {
39
	padding := aes.BlockSize - len(src)%aes.BlockSize
40
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
41
	return append(src, padtext...)
42
}
43

44
func unpad(src []byte) []byte {
45
	length := len(src)
46
	unpadding := int(src[length-1])
47
	return src[:(length - unpadding)]
48
}
49

50
// AesEncryptCBC defines aes encryption with CBC
51
func AesEncryptCBC(key, plaintext []byte) (ciphertext []byte, err error) {
52
	var block cipher.Block
53

54
	if len(plaintext) == 0 {
55
		err = fmt.Errorf("input for encryption is invalid")
56
		return
57
	}
58

59
	paddedText := pad(plaintext)
60

61
	if len(paddedText)%aes.BlockSize != 0 {
62
		err = fmt.Errorf("paddedText [len=%d] is not a multiple of the block size", len(paddedText))
63
		return
64
	}
65

66
	block, err = aes.NewCipher(key)
67
	if err != nil {
68
		return
69
	}
70

71
	ciphertext = make([]byte, aes.BlockSize+len(paddedText))
72
	iv := ciphertext[:aes.BlockSize]
73
	if _, err = io.ReadFull(rand.Reader, iv); err != nil {
74
		return
75
	}
76

77
	cbc := cipher.NewCBCEncrypter(block, iv)
78
	cbc.CryptBlocks(ciphertext[aes.BlockSize:], paddedText)
79

80
	return
81
}
82

83
// AesDecryptCBC defines aes decryption with CBC
84
func AesDecryptCBC(key, ciphertext []byte) (plaintext []byte, err error) {
85
	var block cipher.Block
86

87
	if block, err = aes.NewCipher(key); err != nil {
88
		return
89
	}
90

91
	if len(ciphertext) < aes.BlockSize {
92
		err = fmt.Errorf("ciphertext [len=%d] too short; should greater than blocksize", len(ciphertext))
93
		return
94
	}
95

96
	iv := ciphertext[:aes.BlockSize]
97
	ciphertext = ciphertext[aes.BlockSize:]
98

99
	cbc := cipher.NewCBCDecrypter(block, iv)
100
	cbc.CryptBlocks(ciphertext, ciphertext)
101

102
	plaintext = unpad(ciphertext)
103

104
	return
105
}
106

107
// GenSecretKey generate a secret key according to pair {ts, id}
108
func GenSecretKey(key []byte, ts int64, id string) (secretKey []byte) {
109
	b := make([]byte, 8)
110
	binary.LittleEndian.PutUint64(b, uint64(ts))
111
	data := append(b, []byte(id)...)
112
	secretKey = genKey(key, data)
113
	return
114
}
115

116
func genKey(key []byte, data []byte) (sessionKey []byte) {
117
	h := hmac.New(sha256.New, []byte(key))
118
	h.Write([]byte(data))
119
	sessionKey = h.Sum(nil)
120
	return
121
}
122

123
// AuthGenSessionKeyTS authnode generates a session key according to its master key and current timestamp
124
func AuthGenSessionKeyTS(key []byte) (sessionKey []byte) {
125
	data := []byte(strconv.FormatInt(int64(time.Now().Unix()), 10))
126
	sessionKey = genKey(key, data)
127
	return
128
}
129

130
// Base64Encode encoding using base64
131
func Base64Encode(text []byte) (encodedText string) {
132
	encodedText = base64.StdEncoding.EncodeToString(text)
133
	return
134
}
135

136
// Base64Decode Decoding using base64
137
func Base64Decode(encodedText string) (text []byte, err error) {
138
	text, err = base64.StdEncoding.DecodeString(encodedText)
139
	return
140
}
141

142
// EncodeMessage encode a message with aes encrption, md5 signature
143
func EncodeMessage(plaintext []byte, key []byte) (message string, err error) {
144
	var cipher []byte
145

146
	if len(plaintext) > MaxAllocSize {
147
		return "too max packet", fmt.Errorf("too max packet len %v", len(plaintext))
148
	}
149
	// 8 for random number; 16 for md5 hash
150
	buffer := make([]byte, RandomNumberSize+CheckSumSize+len(plaintext))
151

152
	// add random
153
	random := rand2.Uint64()
154
	binary.LittleEndian.PutUint64(buffer[RandomNumberOffset:], random)
155

156
	// add request body
157
	copy(buffer[MessageOffset:], plaintext)
158

159
	// calculate and add checksum
160
	checksum := md5.Sum(buffer)
161
	copy(buffer[CheckSumOffset:], checksum[:])
162

163
	// encryption with aes CBC with keysize of 256-bit
164
	if cipher, err = AesEncryptCBC(key, buffer); err != nil {
165
		return
166
	}
167
	// base64 encoding
168
	message = base64.StdEncoding.EncodeToString(cipher)
169

170
	return
171
}
172

173
// DecodeMessage decode a message and verify its validity
174
func DecodeMessage(message string, key []byte) (plaintext []byte, err error) {
175
	var (
176
		cipher      []byte
177
		decodedText []byte
178
	)
179

180
	if cipher, err = base64.StdEncoding.DecodeString(message); err != nil {
181
		return
182
	}
183

184
	if decodedText, err = AesDecryptCBC(key, cipher); err != nil {
185
		return
186
	}
187

188
	if len(decodedText) <= MessageMetaDataSize {
189
		err = fmt.Errorf("invalid json format with size [%d] less than message meta data size", len(decodedText))
190
		return
191
	}
192

193
	msgChecksum := make([]byte, CheckSumSize)
194
	copy(msgChecksum, decodedText[CheckSumOffset:CheckSumOffset+CheckSumSize])
195

196
	// calculate checksum
197
	filltext := bytes.Repeat([]byte{byte(0)}, CheckSumSize)
198
	copy(decodedText[CheckSumOffset:], filltext[:])
199
	newChecksum := md5.Sum(decodedText)
200

201
	// verify checksum
202
	if !bytes.Equal(msgChecksum, newChecksum[:]) {
203
		err = fmt.Errorf("checksum not match")
204
	}
205

206
	plaintext = decodedText[MessageOffset:]
207

208
	// fmt.Printf("DecodeMessage CBC: %s\n", plaintext)
209
	return
210
}
211

212
// GenVerifier generate a verifier for replay mitigation in http
213
func GenVerifier(key []byte) (v string, ts int64, err error) {
214
	ts = time.Now().Unix()
215
	tsbuf := make([]byte, unsafe.Sizeof(ts))
216
	binary.LittleEndian.PutUint64(tsbuf, uint64(ts))
217
	if v, err = EncodeMessage(tsbuf, key); err != nil {
218
		panic(err)
219
	}
220
	return
221
}
222

223
// CreateClientX creates a https client
224
func CreateClientX(cert *[]byte) (client *http.Client, err error) {
225
	caCertPool := x509.NewCertPool()
226
	ok := caCertPool.AppendCertsFromPEM(*cert)
227

228
	if !ok {
229
		err = fmt.Errorf("CreateClientX AppendCertsFromPEM fails")
230
		return
231
	}
232

233
	// We don't use PKI to verify client since we have secret key for authentication
234
	client = &http.Client{
235
		Transport: &http.Transport{
236
			TLSClientConfig: &tls.Config{
237
				MinVersion:         tls.VersionTLS12,
238
				RootCAs:            caCertPool,
239
				InsecureSkipVerify: false,
240
			},
241
		},
242
	}
243
	return
244
}
245

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.