Skip to content

Commit 82fdb28

Browse files
committed
backend/pipkey: m new funcs & types
1 parent 29d6a82 commit 82fdb28

1 file changed

Lines changed: 56 additions & 17 deletions

File tree

intra/backend/ipn_pipkeygen.go

Lines changed: 56 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ const (
3333
delim = ":"
3434
minmsgsize = 32 // min msg size in bytes; >= pipkey.c.prefixLen
3535
tokensize = 32 // token size in bytes
36+
bidsize = 64 // blind id size in bytes; hmac(msg, pubkey)
37+
blindsize = 256 // blinded message size in bytes
3638
hashfn = crypto.SHA384 // 48 byte hash fn for RSA-PSS
39+
40+
pipkeyOpaqueCtx = "pipkey-message-opaque" // context for opaque messages
3741
)
3842

3943
var (
@@ -42,6 +46,10 @@ var (
4246
)
4347

4448
type PipKeyProvider interface {
49+
// Bid uniquely identifies a blinded PipKeyProvider.
50+
// PipKeyProviders created from same blinded PipKeyState have the same identity.
51+
// If this PipKeyProvider is not yet blinded, it returns nil.
52+
Bid() *Gostr
4553
// Blind generates id:blindMsg:blindingFactor:salt:msg
4654
// id is a 64 byte hmac tying blindMsg to the public key
4755
// blindMsg is a 256 byte blinded message
@@ -53,37 +61,48 @@ type PipKeyProvider interface {
5361
Finalize(blingSig *Gostr) (*PipKey, error)
5462
}
5563

64+
// nb: PipToken inherits fields from Gostr but not its methods.
65+
66+
// PipToken is a 32 byte random token for bespoke auth.
5667
type PipToken Gostr
5768

69+
type PipMsg string
70+
71+
// Opaque returns a 32 byte hex derived from the PipMsg.
72+
func (p PipMsg) Opaque() *Gostr {
73+
oq := hmac256([]byte(pipkeyOpaqueCtx), []byte(p))
74+
return StrOf(byte2hex(oq))
75+
}
76+
5877
type PipKey struct {
5978
// hex encoded 32 byte msg (random)
60-
Msg string
79+
Msg PipMsg
6180
// hex encoded 256 byte sig (unblinded signature)
6281
Sig string
6382
// hex encoded 32 byte sha256(sig) (msg signature hash)
6483
SigHash string
6584
}
6685

6786
type PipKeyState struct {
68-
// hex encoded 64 byte id, tied to Msg and pubjwk.
69-
Id string
87+
// hex encoded 64 byte id that identifies BlindMsg.
88+
Bid string
7089
// hex encoded 256 byte blind(Msg)
7190
BlindMsg string
7291
// hex encoded blinding factor (up to 256 bytes)
7392
R string
7493
// hex encoded 48 byte salt (random)
7594
Salt string
7695
// hex encoded 32 byte (client) msg (usually, random)
77-
Msg string
96+
Msg PipMsg
7897
}
7998

8099
func newPipKeyState(id, blindMsg, r, salt, msg string) *PipKeyState {
81100
return &PipKeyState{
82-
Id: id,
101+
Bid: id,
83102
BlindMsg: blindMsg,
84103
R: r,
85104
Salt: salt,
86-
Msg: msg,
105+
Msg: PipMsg(msg),
87106
}
88107
}
89108

@@ -101,15 +120,15 @@ func NewPipKeyStateFrom(v *Gostr) (*PipKeyState, error) {
101120
if len(parts) == 1 {
102121
// if there's only one part, it's the message
103122
return &PipKeyState{
104-
Msg: parts[0],
123+
Msg: PipMsg(parts[0]),
105124
}, nil
106125
} else if len(parts) == 5 {
107126
return &PipKeyState{
108-
Id: parts[0],
127+
Bid: parts[0],
109128
BlindMsg: parts[1],
110129
R: parts[2],
111130
Salt: parts[3],
112-
Msg: parts[4],
131+
Msg: PipMsg(parts[4]),
113132
}, nil
114133

115134
}
@@ -131,16 +150,16 @@ func (p *PipKeyState) v() string {
131150
return ""
132151
}
133152

134-
if len(p.BlindMsg) != 256 {
135-
return p.Msg // may be empty, but that's ok
153+
if len(p.BlindMsg) != blindsize {
154+
return string(p.Msg) // may be empty, but that's ok
136155
}
137156

138157
return strings.Join([]string{
139-
p.Id,
158+
p.Bid,
140159
p.BlindMsg,
141160
p.R,
142161
p.Salt,
143-
p.Msg,
162+
string(p.Msg),
144163
},
145164
delim,
146165
)
@@ -180,6 +199,8 @@ type pkgen struct {
180199
blindMsg []byte // 256 bytes blindMsg derived from msg, r, salt
181200
}
182201

202+
var _ PipKeyProvider = (*pkgen)(nil)
203+
183204
// NewPipKeyProvider creates a new PipKeyProvider instance.
184205
// pubjwk: JWK string of the public key of the RSA-PSS signer (for which modulus must be 2048 bits, and hash-fn must be SHA384).
185206
// msgOrExistingState: if empty, a new PipKeyProvider is created with a random message, if not empty, it's the state of an existing PipKey.
@@ -282,7 +303,25 @@ func newPipKey(bjwk []byte, msgOrExistingState string, msgOnly bool) (PipKeyProv
282303
return k, nil
283304
}
284305

285-
// Implements PipKey.
306+
// Bid implements PipKeyProvider.
307+
func (k *pkgen) Bid() *Gostr {
308+
k.mu.Lock()
309+
defer k.mu.Unlock()
310+
311+
if k.id == nil {
312+
log.E("pipkey: who: not blinded")
313+
return nil
314+
}
315+
316+
if len(k.id) != 64 {
317+
log.E("pipkey: who: invalid id size %d", len(k.id))
318+
return nil
319+
}
320+
321+
return StrOf(byte2hex(k.id))
322+
}
323+
324+
// Blind implements PipKeyProvider.
286325
func (k *pkgen) Blind() (*PipKeyState, error) {
287326
k.mu.Lock()
288327
defer k.mu.Unlock()
@@ -310,7 +349,7 @@ func (k *pkgen) Blind() (*PipKeyState, error) {
310349
k.id = hmac256(k.blindMsg, k.pubkey.N.Bytes()) // must match with server-side impl
311350
k.state = &verifierState
312351

313-
if len(k.id) != 64 || len(k.blindMsg) != 256 || len(r.Bytes()) > 256 || len(salt) != 48 || len(k.msg) != 32 {
352+
if len(k.id) != bidsize || len(k.blindMsg) != blindsize || len(r.Bytes()) > 256 || len(salt) != 48 || len(k.msg) != minmsgsize {
314353
log.E("pipkey: blind: invalid state; id %d, blindMsg %d, r %d, salt %d, msg %d",
315354
len(k.id), len(k.blindMsg), len(r.Bytes()), len(salt), len(k.msg))
316355
return nil, brsa.ErrUnexpectedSize
@@ -326,7 +365,7 @@ func (k *pkgen) Blind() (*PipKeyState, error) {
326365
), nil
327366
}
328367

329-
// Implements PipKey.
368+
// Finalize implements PipKeyProvider.
330369
func (k *pkgen) Finalize(blindSig *Gostr) (*PipKey, error) {
331370
return k.finalize(blindSig.V())
332371
}
@@ -355,7 +394,7 @@ func (k *pkgen) finalize(blindSig string) (*PipKey, error) {
355394
hashedsigbytes := sha256sum(sigbytes)
356395

357396
return &PipKey{
358-
Msg: byte2hex(k.msg),
397+
Msg: PipMsg(byte2hex(k.msg)),
359398
Sig: byte2hex(sigbytes),
360399
SigHash: byte2hex(hashedsigbytes),
361400
}, nil

0 commit comments

Comments
 (0)