-
Notifications
You must be signed in to change notification settings - Fork 0
/
crypto.go
140 lines (115 loc) · 3.43 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
136
137
138
139
140
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io"
"strings"
)
// GenRandBytes returns securely generated random bytes. It will return
// an error if the system's secure random number generator fails.
func GenRandBytes(n int) ([]byte, error) {
b := make([]byte, n)
_, err := rand.Read(b)
if err != nil {
return nil, fmt.Errorf("failed to generate random bytes: %w", err)
}
return b, nil
}
type AESKeySizeError struct {
length int
}
func (e *AESKeySizeError) Error() string {
return fmt.Sprintf("key must be 32 bytes, not %v bytes", e.length)
}
// EncryptWithAES encrypts plaintext with key using AES-GCM. Returns ciphertext
// and nonce. AESKeyLengthError is returned if key is not 32 bytes. All other
// errors are bubbled up without wrapping.
func EncryptWithAES(
key []byte,
plaintext []byte,
) (ciphertext []byte, nonce []byte, err error) {
keyLength := len(key)
if keyLength != 32 {
return nil, nil, &AESKeySizeError{keyLength}
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, nil, fmt.Errorf("failed to create new cipher: %w", err)
}
nonce = make([]byte, 12)
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return nil, nil, fmt.Errorf("failed to create nonce: %w", err)
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
return nil, nil, fmt.Errorf("failed to create aesgcm: %w", err)
}
return aesgcm.Seal(nil, nonce, plaintext, nil), nonce, nil
}
type PublicKeyParseError struct {
Err error
}
func (e *PublicKeyParseError) Error() string {
return fmt.Sprintf("error parsing public key: %v", e.Err)
}
type RSAOAEPEncryptionError struct {
Err error
}
func (e *RSAOAEPEncryptionError) Error() string {
return fmt.Sprintf("error encrypting with RSA-OAEP: %v", e.Err)
}
var ErrPEMDecode = errors.New("failed to decode PEM formatted block")
var ErrNotPublicKey = errors.New("decoded PEM block not a public key")
var ErrNotRSAPublicKey = errors.New("parsed key is not of type RSA")
var ErrForbiddenKeySize = errors.New("size of given key is forbidden")
// EncryptWithRSA encrypts the given plaintext with the given publicKey. The
// resulting ciphertext is returned. Ecyrption is done with RSA-OAEP.
//
// The public key must be PEM encoded.
//
// For the public key the forms RFC5280 (X.509) and RFC8017 (PKCS #1) are
// supported. In PEM encoded blocks these can be identified with the
// strings "PUBLIC KEY" and "RSA PUBLIC KEY".
//
// Sentinel errors: ErrPEMDecode, ErrNotPublicKey, ErrNotRSAPublicKey, ErrForbiddenKeySize.
//
// Custom error types: PublicKeyParseError and RSAOAEPEncryptionError.
//
// No other errors are bubbled up.
func EncryptWithRSA(publicKey []byte, plaintext []byte) (ciphertext []byte, err error) {
block, _ := pem.Decode(publicKey)
if block == nil {
return nil, ErrPEMDecode
}
if !strings.Contains(block.Type, "PUBLIC KEY") {
return nil, ErrNotPublicKey
}
pub, parseErr := x509.ParsePKIXPublicKey(block.Bytes)
if parseErr != nil {
pub, parseErr = x509.ParsePKCS1PublicKey(block.Bytes)
}
if parseErr != nil {
return nil, &PublicKeyParseError{err}
}
rsaKey, ok := pub.(*rsa.PublicKey)
if !ok {
return nil, ErrNotRSAPublicKey
}
if rsaKey.Size() != 256 {
return nil, ErrForbiddenKeySize
}
ciphertext, err = rsa.EncryptOAEP(
sha256.New(), rand.Reader, rsaKey, plaintext, nil,
)
if err != nil {
return nil, &RSAOAEPEncryptionError{err}
}
return ciphertext, nil
}