Skip to content

Commit e4fb35f

Browse files
committed
Updated bitwarden
1 parent 6caca8a commit e4fb35f

File tree

11 files changed

+479
-4
lines changed

11 files changed

+479
-4
lines changed

go.mod

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
module github.com/mutablelogic/go-client
22

3-
go 1.21.5
3+
go 1.22
4+
5+
toolchain go1.22.3
46

57
require (
8+
github.com/andreburgaud/crypt2go v1.5.0
69
github.com/djthorpe/go-errors v1.0.3
7-
github.com/djthorpe/go-tablewriter v0.0.0-20240507095049-af1f79a27517
10+
github.com/djthorpe/go-tablewriter v0.0.2
811
github.com/pkg/errors v0.9.1
912
github.com/stretchr/testify v1.9.0
13+
github.com/xdg-go/pbkdf2 v1.0.0
1014
)
1115

1216
require (
1317
github.com/davecgh/go-spew v1.1.1 // indirect
18+
github.com/mattn/go-runewidth v0.0.15 // indirect
1419
github.com/pmezard/go-difflib v1.0.0 // indirect
20+
github.com/rivo/uniseg v0.4.7 // indirect
1521
gopkg.in/yaml.v3 v3.0.1 // indirect
1622
)

go.sum

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
1+
github.com/andreburgaud/crypt2go v1.5.0 h1:7hz8l9WjaMEtAUL4+nMm64Of7HzUr1H4JhmNof7BCLc=
2+
github.com/andreburgaud/crypt2go v1.5.0/go.mod h1:ZEu8s+aLbZdRNdSHr//o6gCSMYKgT24sjNX6r4uAI8U=
13
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
24
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
35
github.com/djthorpe/go-errors v1.0.3 h1:GZeMPkC1mx2vteXLI/gvxZS0Ee9zxzwD1mcYyKU5jD0=
46
github.com/djthorpe/go-errors v1.0.3/go.mod h1:HtfrZnMd6HsX75Mtbv9Qcnn0BqOrrFArvCaj3RMnZhY=
5-
github.com/djthorpe/go-tablewriter v0.0.0-20240507095049-af1f79a27517 h1:UOUbtOLKMmupwpbYq1YG28pXDHJOlrznNX2vavHGhKk=
6-
github.com/djthorpe/go-tablewriter v0.0.0-20240507095049-af1f79a27517/go.mod h1:M0TwrksgC73IdXcj9ZXtpuw2Tkl7jdQi/mFPOo1I/4M=
7+
github.com/djthorpe/go-tablewriter v0.0.2 h1:3TuafWr/m+7/Cfq2CrpkewVIp11fEgr80VxokPWhOVA=
8+
github.com/djthorpe/go-tablewriter v0.0.2/go.mod h1:e+aurk5hOhszDXN42nlnZZ8nBT+8vU6/Qro1avS2cMI=
9+
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
10+
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
711
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
812
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
913
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1014
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
15+
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
16+
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
17+
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
1118
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
1219
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
20+
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
21+
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
1322
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
1423
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
1524
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

pkg/bitwarden/auth.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package bitwarden
2+
3+
///////////////////////////////////////////////////////////////////////////////
4+
// TYPES
5+
6+
type Register struct {
7+
Name string `json:"name"`
8+
Email string `json:"email"`
9+
MasterPasswordHash string `json:"masterPasswordHash"`
10+
MasterPasswordHint string `json:"masterPasswordHint"`
11+
Key string `json:"key"`
12+
Kdf uint `json:"kdf"`
13+
KdfIterations uint `json:"kdfIterations"`
14+
}
15+
16+
///////////////////////////////////////////////////////////////////////////////
17+
// PUBLIC METHODS
18+
19+
func (c *Client) Register(name, email, password string) {
20+
// TODO
21+
}

pkg/bitwarden/client.go

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
bitwarden implements an API client for bitwarden
3+
*/
4+
package bitwarden
5+
6+
import (
7+
// Packages
8+
"runtime"
9+
10+
"github.com/mutablelogic/go-client/pkg/client"
11+
12+
// Namespace imports
13+
. "github.com/djthorpe/go-errors"
14+
)
15+
16+
///////////////////////////////////////////////////////////////////////////////
17+
// TYPES
18+
19+
type Client struct {
20+
*client.Client
21+
}
22+
23+
type Session struct {
24+
Kdf struct {
25+
Iterations int `json:"kdfIterations"`
26+
} `json:"kdf"`
27+
Key []byte `json:"key"`
28+
Email string `json:"email"`
29+
Password string `json:"password"`
30+
}
31+
32+
type reqPrelogin struct {
33+
Email string `json:"email"`
34+
}
35+
36+
type reqToken struct {
37+
GrantType string `json:"grant_type"`
38+
Email string `json:"username"`
39+
Password string `json:"password"`
40+
Scope string `json:"scope"`
41+
ClientId string `json:"client_id"`
42+
DeviceType string `json:"deviceType"`
43+
DeviceName string `json:"deviceName"`
44+
DeviceIdentifier string `json:"deviceIdentifier"`
45+
DevicePushToken string `json:"devicePushToken"`
46+
TwoFactorToken string `json:"twoFactorToken,omitempty"`
47+
TwoFactorProvider int `json:"twoFactorProvider,omitempty"`
48+
TwoFactorRemember int `json:"twoFactorRemember,omitempty"`
49+
}
50+
51+
type respToken struct {
52+
AccessToken string `json:"access_token"`
53+
ExpiresIn int `json:"expires_in"`
54+
TokenType string `json:"token_type"`
55+
RefreshToken string `json:"refresh_token"`
56+
Key string `json:"key"`
57+
}
58+
59+
///////////////////////////////////////////////////////////////////////////////
60+
// GLOBALS
61+
62+
const (
63+
baseUrl = "https://api.bitwarden.com"
64+
identityUrl = "https://identity.bitwarden.com"
65+
iconUrl = "https://icons.bitwarden.net"
66+
)
67+
68+
const (
69+
deviceName = "api"
70+
loginScope = "api offline_access"
71+
loginApiKeyScope = "api"
72+
)
73+
74+
///////////////////////////////////////////////////////////////////////////////
75+
// LIFECYCLE
76+
77+
func New(opts ...client.ClientOpt) (*Client, error) {
78+
// Create client
79+
client, err := client.New(append(opts, client.OptEndpoint(baseUrl))...)
80+
if err != nil {
81+
return nil, err
82+
}
83+
84+
// Return the client
85+
return &Client{client}, nil
86+
}
87+
88+
///////////////////////////////////////////////////////////////////////////////
89+
// PUBLIC METHODS
90+
91+
// Prelogin returns a session for logging in for a given email and password
92+
func (c *Client) Prelogin(email, password string) (*Session, error) {
93+
var request reqPrelogin
94+
var response Session
95+
96+
// Prelogin
97+
request.Email = email
98+
payload, err := client.NewJSONRequest(request, client.ContentTypeJson)
99+
if err != nil {
100+
return nil, err
101+
} else if err := c.Do(payload.Post(), &response.Kdf, client.OptPath("accounts/prelogin")); err != nil {
102+
return nil, err
103+
} else if response.Kdf.Iterations == 0 {
104+
return nil, ErrUnexpectedResponse
105+
}
106+
107+
// Create keys for encryption and decryption
108+
response.Email = email
109+
response.Key = MakeInternalKey(email, password, response.Kdf.Iterations)
110+
response.Password = HashedPassword(email, password, response.Kdf.Iterations)
111+
112+
// Return success
113+
return &response, nil
114+
}
115+
116+
// GetToken returns a token for a session
117+
func (c *Client) LoginToken(session *Session) error {
118+
var request reqToken
119+
120+
request.GrantType = "password"
121+
request.Email = session.Email
122+
request.Password = session.Password
123+
request.Scope = loginScope
124+
request.ClientId = "browser"
125+
request.DeviceType = deviceType()
126+
request.DeviceName = deviceName
127+
request.DeviceIdentifier = "00000000-0000-0000-0000-000000000000" // TODO
128+
}
129+
130+
///////////////////////////////////////////////////////////////////////////////
131+
// PRIVATE METHODS
132+
133+
func deviceType() string {
134+
switch runtime.GOOS {
135+
case "linux":
136+
return "8" // Linux Desktop
137+
case "darwin":
138+
return "7" // MacOS Desktop
139+
case "windows":
140+
return "6" // Windows Desktop
141+
default:
142+
return "14" // Unknown Browser
143+
}
144+
}

pkg/bitwarden/client_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package bitwarden_test
2+
3+
import (
4+
"encoding/json"
5+
"os"
6+
"testing"
7+
8+
// Packages
9+
bitwarden "github.com/mutablelogic/go-client/pkg/bitwarden"
10+
opts "github.com/mutablelogic/go-client/pkg/client"
11+
assert "github.com/stretchr/testify/assert"
12+
)
13+
14+
func Test_client_001(t *testing.T) {
15+
assert := assert.New(t)
16+
client, err := bitwarden.New(opts.OptTrace(os.Stderr, true))
17+
assert.NoError(err)
18+
assert.NotNil(client)
19+
t.Log(client)
20+
}
21+
22+
func Test_client_002(t *testing.T) {
23+
assert := assert.New(t)
24+
client, err := bitwarden.New(opts.OptTrace(os.Stderr, true))
25+
assert.NoError(err)
26+
27+
session, err := client.Prelogin("nobody@example.com", "p4ssw0rd")
28+
assert.NoError(err)
29+
assert.NotNil(session)
30+
31+
data, _ := json.MarshalIndent(session, "", " ")
32+
t.Log(string(data))
33+
}

0 commit comments

Comments
 (0)