-
Notifications
You must be signed in to change notification settings - Fork 40
/
shared_secret.go
101 lines (85 loc) · 3 KB
/
shared_secret.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
package config
import (
"crypto/aes"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/pkg/errors"
"github.com/smartcontractkit/libocr/commontypes"
"github.com/smartcontractkit/libocr/offchainreporting2plus/types"
"golang.org/x/crypto/curve25519"
)
const SharedSecretSize = 16 // A 128-bit symmetric key
type EncryptedSharedSecret [SharedSecretSize]byte
// SharedSecretEncryptions is the encryptions of SharedConfig.SharedSecret,
// using each oracle's SharedSecretEncryptionPublicKey.
//
// We use a custom encryption scheme to be more space-efficient (compared to
// standard AEAD schemes, nacl crypto_box, etc...), which saves gas in
// transmission to the OCR2Aggregator.
type SharedSecretEncryptions struct {
// (secret key chosen by dealer) * g, X25519 point
DiffieHellmanPoint [curve25519.PointSize]byte
// keccak256 of plaintext sharedSecret.
//
// Since SharedSecretEncryptions are shared through a smart contract, each
// oracle will see the same SharedSecretHash. After decryption, oracles can
// check their sharedSecret against SharedSecretHash to prevent the dealer
// from equivocating
SharedSecretHash common.Hash
// Encryptions of the shared secret with one entry for each oracle. The
// i-th oracle can recover the key as follows:
//
// 1. key := Keccak256(DH(DiffieHellmanPoint, process' secret key))[:16]
// 2. sharedSecret := AES128DecryptBlock(key, Encryptions[i])
//
// See Decrypt for details.
Encryptions []EncryptedSharedSecret
}
func (e SharedSecretEncryptions) Equal(e2 SharedSecretEncryptions) bool {
if len(e.Encryptions) != len(e2.Encryptions) {
return false
}
encsEqual := true
for i := range e.Encryptions {
encsEqual = encsEqual && e.Encryptions[i] == e2.Encryptions[i]
}
return encsEqual &&
e.DiffieHellmanPoint == e2.DiffieHellmanPoint &&
e.SharedSecretHash == e2.SharedSecretHash
}
// Decrypt one block with AES-128
func aesDecryptBlock(key, ciphertext []byte) [16]byte {
if len(key) != 16 {
// assertion
panic("key has wrong length")
}
if len(ciphertext) != 16 {
// assertion
panic("ciphertext has wrong length")
}
cipher, err := aes.NewCipher(key)
if err != nil {
// assertion
panic(fmt.Sprintf("Unexpected error during aes.NewCipher: %v", err))
}
var plaintext [16]byte
cipher.Decrypt(plaintext[:], ciphertext)
return plaintext
}
// Decrypt returns the sharedSecret
func (e SharedSecretEncryptions) Decrypt(oid commontypes.OracleID, k types.OffchainKeyring) (*[SharedSecretSize]byte, error) {
if len(e.Encryptions) <= int(oid) {
return nil, errors.New("oid out of range of SharedSecretEncryptions.Encryptions")
}
dhPoint, err := k.ConfigDiffieHellman(e.DiffieHellmanPoint)
if err != nil {
return nil, err
}
key := crypto.Keccak256(dhPoint[:])[:16]
sharedSecret := aesDecryptBlock(key, e.Encryptions[int(oid)][:])
if common.BytesToHash(crypto.Keccak256(sharedSecret[:])) != e.SharedSecretHash {
return nil, errors.Errorf("decrypted sharedSecret has wrong hash")
}
return &sharedSecret, nil
}