-
Notifications
You must be signed in to change notification settings - Fork 2
/
hazmat.go
132 lines (118 loc) · 4.63 KB
/
hazmat.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
package sss
// #include "hazmat.h"
//
// /*
// * For the same reason as for `sss_combine_shares` in `sss.go`, we have to
// * wrap the symbols with incompatible C types (`const`).
// */
//
// void
// sss_create_keyshares_go_wrapper(sss_Keyshare *out, uint8_t key[32],
// uint8_t n, uint8_t k)
// {
// return sss_create_keyshares(out, (const uint8_t*) key, n, k);
// }
//
// void
// sss_combine_keyshares_go_wrapper(uint8_t key[32], sss_Keyshare *keyshares,
// uint8_t k)
// {
// return sss_combine_keyshares(key, (const sss_Keyshare*) keyshares, k);
// }
import "C"
import (
"errors"
"fmt"
"unsafe"
)
// CreateKeyshares splits the secret `key` into `n` keyshares with a treshold
// of `k`.
// `key` must be a slice of exactly 32 bytes
// `n` and `k` must be numbers from 1 to 255 (inclusive)
// `k` may not be larger than `n` (because this would make it impossible to
// ever restore the secret.
//
// This function returns a tuple `(keyshares, err)`. The caller must check if
// `err` is not `nil`, as this indicates an error in the parameters. If the
// assertions on the arguments (see above) are satisfied, this function will
// never return an error.
// When `err` is `nil`, `keyshares` will be a slice of slices ([][]byte),
// containing `n` keyshare buffers of each exactly 33 bytes long.
//
// This function is called `CreateKeyshares` for a reason. Only share actual
// cryptographic keys with this function, meaning octet strings of 256 bits
// in length, picked uniformly from its key space. If the key is predictable,
// this reduces the security of the scheme. Also, an attacker can trivially
// alter shares such that combining the keyshares will result in an arbitrary
// (attacker-chosen) different key. This can be fixed by using some kind of
// integrity check on the actual payload.
func CreateKeyshares(key []byte, count int, threshold int) ([][]byte, error) {
if len(key) != 32 {
return nil, errors.New("`key` must be 32 bytes long")
}
checkNK(count, threshold)
// Convert n and k to bytes
var ctyN, ctyK C.uint8_t = C.uint8_t(count), C.uint8_t(threshold)
// Create a temporary buffer to hold the keyshares
cKeyshares := make([]byte, count*C.sizeof_sss_Keyshare)
// Create the keyshares
C.sss_create_keyshares(
(*C.sss_Keyshare)(unsafe.Pointer(&cKeyshares[0])),
(*C.uint8_t)(unsafe.Pointer(&key[0])),
ctyN, ctyK)
// Move the keyshares into a Go-friendly slice of slices
keyshares := make([][]byte, count)
for i := 0; i < count; i++ {
keyshares[i] = cKeyshares[i*C.sizeof_sss_Keyshare : (i+1)*C.sizeof_sss_Keyshare]
}
return keyshares, nil
}
// CombineKeyshares restores the key as split by `CreateShares`, using the
// keyshares given in `keyshares`.
// `keyshares` must be a slice of keyshare slices, each of which must be
// exactly 33 bytes long.
//
// This function returns a tuple `(key, err)`. The caller must check if `err`
// is not `nil`, as this indicates an error. Iff `err` is `nil`, `key` will
// be the restored key. The restored key will be 32 bytes long.
//
// Opposed to `CombineShares`, this function will always succeed to restore the
// key. That is, it will try its best to restore the key using the shares, but
// can never know if this has succeeded. With other words:
//
// If you give a number of shares that is less than the threshold specified in
// `CreateKeyshares`, you will get a corrupted key.
// If you give shares that have been tampered with, you will get a corrupted
// key.
//
// Note that a key restored by this function has not been checked for integrity
// whatsoever. A restored key may as well just be garbage. However, a corrupted
// key still contains a lot of information about the keyshares that were used
// to construct it. So treat *all* keys that are generated by this function as
// secret keys (no matter if they are corrupted or not).
func CombineKeyshares(keyshares [][]byte) ([]byte, error) {
k := len(keyshares)
if err := checkCombineK(k); err != nil {
return nil, err
}
// Create a temporary buffer to hold the keyshares
cKeyshares := make([]byte, k*C.sss_KEYSHARE_LEN)
for i, keyshare := range keyshares {
if len(keyshare) != C.sss_KEYSHARE_LEN {
return nil, fmt.Errorf("keyshare %d has an invalid length", i)
}
// Memcpy the share into our shares buffer
copy(cKeyshares[i*C.sss_KEYSHARE_LEN:(i+1)*C.sss_KEYSHARE_LEN],
keyshares[i][:])
}
// Create a new slice for the key
key := make([]byte, 32)
// Convert k to uint8_t
ctyK := C.uint8_t(k)
// Restore the key
C.sss_combine_keyshares_go_wrapper(
(*C.uint8_t)(unsafe.Pointer(&key[0])),
(*C.sss_Keyshare)(unsafe.Pointer(&cKeyshares[0])),
ctyK)
return key, nil
}