-
Notifications
You must be signed in to change notification settings - Fork 0
/
crypto.go
135 lines (121 loc) · 3.82 KB
/
crypto.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
package main
// This entirety of this file has come from https://github.com/mvdan/bitw/blob/master/crypto.go
// Small changes have been made to satisfy the linter.
// Copyright (c) 2019, Daniel Martí <[email protected]>
// Under the BSD 3-Clause license
// https://github.com/mvdan/bitw/blob/d4600876932c7e27feb32b17c83c8f933388c30f/LICENSE
import (
"crypto/aes"
"crypto/cipher"
cryptorand "crypto/rand"
"crypto/sha256"
"errors"
"fmt"
"io"
"math"
"math/big"
"golang.org/x/crypto/hkdf"
)
type dhGroup struct {
g, p, pMinus1 *big.Int
}
var bigOne = big.NewInt(1)
func (dg *dhGroup) NewKeypair() (private, public *big.Int, err error) {
for {
if private, err = cryptorand.Int(cryptorand.Reader, dg.pMinus1); err != nil {
return nil, nil, err
}
if private.Sign() > 0 {
break
}
}
public = new(big.Int).Exp(dg.g, private, dg.p)
return private, public, nil
}
func (dg *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) {
if theirPublic.Cmp(bigOne) <= 0 || theirPublic.Cmp(dg.pMinus1) >= 0 {
return nil, errors.New("DH parameter out of bounds")
}
return new(big.Int).Exp(theirPublic, myPrivate, dg.p), nil
}
func rfc2409SecondOakleyGroup() *dhGroup {
p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16)
return &dhGroup{
g: new(big.Int).SetInt64(2),
p: p,
pMinus1: new(big.Int).Sub(p, bigOne),
}
}
func (dg *dhGroup) keygenHKDFSHA256AES128(theirPublic *big.Int, myPrivate *big.Int) ([]byte, error) {
sharedSecret, err := dg.diffieHellman(theirPublic, myPrivate)
if err != nil {
return nil, err
}
r := hkdf.New(sha256.New, sharedSecret.Bytes(), nil, nil)
aesKey := make([]byte, 16)
if _, err := io.ReadFull(r, aesKey); err != nil {
return nil, err
}
return aesKey, nil
}
func unauthenticatedAESCBCEncrypt(data, key []byte) (iv, ciphertext []byte, _ error) {
data = padPKCS7(data, aes.BlockSize)
block, err := aes.NewCipher(key)
if err != nil {
return nil, nil, err
}
ivSize := aes.BlockSize
iv = make([]byte, ivSize)
ciphertext = make([]byte, len(data))
if _, err := io.ReadFull(cryptorand.Reader, iv); err != nil {
return nil, nil, err
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext, data)
return iv, ciphertext, nil
}
func unauthenticatedAESCBCDecrypt(iv, ciphertext, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
if len(iv) != aes.BlockSize {
return nil, errors.New("iv length does not match AES block size")
}
if len(ciphertext)%aes.BlockSize != 0 {
return nil, errors.New("ciphertext is not a multiple of AES block size")
}
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(ciphertext, ciphertext) // decrypt in-place
data, err := unpadPKCS7(ciphertext, aes.BlockSize)
if err != nil {
return nil, err
}
return data, nil
}
func unpadPKCS7(src []byte, size int) ([]byte, error) {
n := src[len(src)-1]
if len(src)%size != 0 {
return nil, fmt.Errorf("expected PKCS7 padding for block size %d, but have %d bytes", size, len(src))
}
if len(src) <= int(n) {
return nil, fmt.Errorf("cannot unpad %d bytes out of a total of %d", n, len(src))
}
src = src[:len(src)-int(n)]
return src, nil
}
func padPKCS7(src []byte, size int) []byte {
// Note that we always pad, even if rem==0. This is because unpad must
// always remove at least one byte to be unambiguous.
rem := len(src) % size
n := size - rem
if n > math.MaxUint8 {
panic(fmt.Sprintf("cannot pad over %d bytes, but got %d", math.MaxUint8, n))
}
padded := make([]byte, len(src)+n)
copy(padded, src)
for i := len(src); i < len(padded); i++ {
padded[i] = byte(n)
}
return padded
}