This repository has been archived by the owner on Jul 7, 2020. It is now read-only.
forked from keybase/client
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pdpka.go
173 lines (143 loc) · 4.73 KB
/
pdpka.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
package libkb
import (
"crypto/rand"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"time"
"github.com/keybase/client/go/protocol/keybase1"
)
// PDPKA is a "Passphrase-Derived Public Key Authentication". In this case, it's a
// armored, packed, signature that's been output by our signing interface.
type PDPKA string
// PDPKALoginPackage contains all relevant PDPKA versions in use at this
// time. For now, versions 4 and 5.
type PDPKALoginPackage struct {
pdpka5 PDPKA
pdpka4 PDPKA
}
type loginIdentifier interface {
value() string
}
type loginIdentifierEmail string
func (l loginIdentifierEmail) value() string { return string(l) }
type loginIdentifierUsername string
func (l loginIdentifierUsername) value() string { return string(l) }
type loginIdentifierUID keybase1.UID
func (l loginIdentifierUID) value() string { return keybase1.UID(l).String() }
func (p PDPKA) String() string { return string(p) }
type authPayload struct {
Body struct {
Auth struct {
Nonce string `json:"nonce"`
Session string `json:"session"`
} `json:"auth"`
Key struct {
Email string `json:"email,omitempty"`
Host string `json:"host"`
Kid string `json:"kid"`
UID string `json:"uid,omitempty"`
Username string `json:"username,omitempty"`
} `json:"key"`
Type string `json:"type"`
Version int `json:"version"`
} `json:"body"`
Ctime int `json:"ctime"`
ExpireIn int `json:"expire_in"`
Tag string `json:"tag"`
}
func seedToPDPKAKey(seed []byte) (ret NaclSigningKeyPair, err error) {
if len(seed) != NaclSigningKeySecretSize {
return ret, fmt.Errorf("wrong size secret in computePDPKA (%d != %d)", len(seed), NaclSigningKeySecretSize)
}
var secret [NaclSigningKeySecretSize]byte
copy(secret[:], seed)
return MakeNaclSigningKeyPairFromSecret(secret)
}
func seedToPDPKAKID(seed []byte) (ret keybase1.KID, err error) {
var signingKey NaclSigningKeyPair
signingKey, err = seedToPDPKAKey(seed)
if err != nil {
return ret, err
}
return signingKey.GetKID(), nil
}
func computePDPKA(li loginIdentifier, seed []byte, loginSession []byte) (ret PDPKA, err error) {
var nonce [16]byte
if _, err = rand.Read(nonce[:]); err != nil {
return ret, err
}
var ap authPayload
ap.Body.Auth.Nonce = hex.EncodeToString(nonce[:])
ap.Body.Auth.Session = base64.StdEncoding.EncodeToString(loginSession)
var signingKey NaclSigningKeyPair
signingKey, err = seedToPDPKAKey(seed)
if err != nil {
return ret, err
}
ap.Body.Key.Kid = signingKey.GetKID().String()
ap.Body.Key.Host = CanonicalHost
ap.Body.Type = "auth"
ap.Body.Version = 1
ap.Tag = "signature"
ap.Ctime = int(time.Now().Unix())
ap.ExpireIn = 60 * 60 * 24 // good for one day, to deal with clock skew
switch li.(type) {
case loginIdentifierEmail:
ap.Body.Key.Email = li.value()
case loginIdentifierUsername:
ap.Body.Key.Username = li.value()
case loginIdentifierUID:
ap.Body.Key.UID = li.value()
}
var jsonRaw []byte
if jsonRaw, err = json.Marshal(ap); err != nil {
return ret, err
}
var sig string
if sig, _, err = signingKey.SignToString(jsonRaw); err != nil {
return ret, err
}
ret = PDPKA(sig)
return ret, nil
}
func computeLoginPackageFromUID(u keybase1.UID, ps *PassphraseStream, loginSession []byte) (ret PDPKALoginPackage, err error) {
return computeLoginPackage(loginIdentifierUID(u), ps, loginSession)
}
func computeLoginPackageFromEmailOrUsername(eou string, ps *PassphraseStream, loginSession []byte) (ret PDPKALoginPackage, err error) {
var li loginIdentifier
if CheckUsername.F(eou) {
li = loginIdentifierUsername(eou)
} else if CheckEmail.F(eou) {
li = loginIdentifierEmail(eou)
} else {
return ret, fmt.Errorf("expected an email or username; got neither (%s)", eou)
}
return computeLoginPackage(li, ps, loginSession)
}
func computeLoginPackage(li loginIdentifier, ps *PassphraseStream, loginSession []byte) (ret PDPKALoginPackage, err error) {
if ps == nil {
return ret, errors.New("computeLoginPackage failed due to nil PassphraseStream")
}
ret.pdpka5, err = computePDPKA(li, ps.EdDSASeed(), loginSession)
if err != nil {
return ret, err
}
ret.pdpka4, err = computePDPKA(li, ps.PWHash(), loginSession)
if err != nil {
return ret, err
}
return ret, nil
}
// PopulateArgs populates the given HTTP args with parameters in this PDPKA package.
// Right now that includes v4 and v5 of the PDPKA login system.
func (lp PDPKALoginPackage) PopulateArgs(h *HTTPArgs) {
h.Add("pdpka4", S{string(lp.pdpka4)})
h.Add("pdpka5", S{string(lp.pdpka5)})
}
// PDPKA4 gets the v4 of the PDPKA token for this login package
func (lp PDPKALoginPackage) PDPKA4() PDPKA { return lp.pdpka4 }
// PDPKA5 gets the v4 of the PDPKA token for this login package
func (lp PDPKALoginPackage) PDPKA5() PDPKA { return lp.pdpka5 }