-
Notifications
You must be signed in to change notification settings - Fork 1
/
parameters.go
187 lines (153 loc) · 4.78 KB
/
parameters.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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
package cpace
import (
"fmt"
"github.com/bytemare/cryptotools/encoding"
"github.com/bytemare/cryptotools/group/ciphersuite"
"github.com/bytemare/cryptotools/hash"
"github.com/bytemare/cryptotools/utils"
)
const (
dsiFormat = "%s%s-%d" // "CPace[Group]-[i]"
encodingLength = 1
)
// Parameters identifies the components of a Ciphersuite.
type Parameters struct {
Group ciphersuite.Identifier `json:"group"`
Hash hash.Identifier `json:"hash"`
*Info `json:"info"`
}
// Init initialises the parameters with information relative to the communication peers, and returns p.
// This enables pre-computation of the common initialization state between the peers and ready-to-use offline storage.
func (p *Parameters) Init(ida, idb, ad []byte) *Parameters {
p.Info = &Info{
Ida: ida,
Idb: idb,
Ad: ad,
Dsi1: []byte(fmt.Sprintf(dsiFormat, cpace, p.Group, 1)),
Dsi2: []byte(fmt.Sprintf(dsiFormat, cpace, p.Group, 2)),
}
return p
}
func (p *Parameters) new(role Role) *CPace {
h2gDST := []byte(cpace + p.Group.String())
return &CPace{
role: role,
group: p.Group.Get(h2gDST),
parameters: p,
}
}
// Initiator returns a pointer to a CPace structure for the protocol's initiator role.
func (p *Parameters) Initiator() *CPace {
return p.new(Initiator)
}
// Responder returns a pointer to a CPace structure for the protocol's responder role.
func (p *Parameters) Responder() *CPace {
return p.new(Responder)
}
// Serialize returns a byte string serialization of p.
func (p *Parameters) Serialize() []byte {
var i []byte
if p.Info == nil {
i = nil
} else {
i = p.Info.Serialize()
}
return utils.Concatenate(0, []byte{byte(p.Group), byte(p.Hash)}, i)
}
// DeserializeParameters attempts to decode input into a Parameter structure.
// Out-of-bounds panics are recovered from and returned as errors with field specification.
func DeserializeParameters(input []byte) (*Parameters, error) {
if len(input) < 2 {
return nil, errEncodingShort
}
g := input[0]
if !ciphersuite.Identifier(g).Available() {
return nil, errEncodingCiphersuite
}
h := input[1]
if !hash.Identifier(h).Available() {
return nil, errEncodingHash
}
i, err := DeserializeInfo(input[2:])
if err != nil {
return nil, err
}
return &Parameters{
Group: ciphersuite.Identifier(g),
Hash: hash.Identifier(h),
Info: i,
}, nil
}
// Info holds the CPace initialization state. It can be pre-computed, stored, and reused.
type Info struct {
// Ida is the initiator's identifier.
Ida []byte `json:"ida"`
// Idb is the responder's identifier.
Idb []byte `json:"idb"`
// Ad is additional data to be used for the channel identifier.
Ad []byte `json:"ad"`
// Domain separation identifiers.
Dsi1 []byte `json:"dsi1"`
Dsi2 []byte `json:"dsi2"`
}
// Serialize returns a byte string serialization of i.
func (i *Info) Serialize() []byte {
// todo: bounds check on length of these arrays. Wait for definition.
return utils.Concatenate(0,
serialize(i.Ida, encodingLength),
serialize(i.Idb, encodingLength),
serialize(i.Ad, encodingLength),
serialize(i.Dsi1, encodingLength),
serialize(i.Dsi2, encodingLength),
)
}
func serialize(input []byte, length int) []byte {
return append(encoding.I2OSP(len(input), length), input...)
}
func deserialize(in []byte, start, length int) (b []byte, offset int, err error) {
defer func() {
if recover() != nil {
err = errDecodingBounds
}
}()
step := start + length
l := encoding.OS2IP(in[start:step])
b = in[step : step+l]
return b, step + l, nil
}
// DeserializeInfo attempts to decode input into an Info structure.
// Out-of-bounds panics are recovered from and returned as errors with field specification.
// Nil input returns nil Info pointer without error.
func DeserializeInfo(input []byte) (*Info, error) {
if len(input) == 0 {
return nil, nil
}
offset := 0
ida, offset, err := deserialize(input, offset, encodingLength)
if err != nil {
return nil, fmt.Errorf("error decoding info - failed at offset %d (%s): %w", offset, "ida", err)
}
idb, offset, err := deserialize(input, offset, encodingLength)
if err != nil {
return nil, fmt.Errorf("error decoding info - failed at offset %d (%s): %w", offset, "idb", err)
}
ad, offset, err := deserialize(input, offset, encodingLength)
if err != nil {
return nil, fmt.Errorf("error decoding info - failed at offset %d (%s): %w", offset, "ad", err)
}
dsi1, offset, err := deserialize(input, offset, encodingLength)
if err != nil {
return nil, fmt.Errorf("error decoding info - failed at offset %d (%s): %w", offset, "dsi1", err)
}
dsi2, offset, err := deserialize(input, offset, encodingLength)
if err != nil {
return nil, fmt.Errorf("error decoding info - failed at offset %d (%s): %w", offset, "dsi2", err)
}
return &Info{
Ida: ida,
Idb: idb,
Ad: ad,
Dsi1: dsi1,
Dsi2: dsi2,
}, nil
}