Skip to content
This repository has been archived by the owner on Feb 16, 2023. It is now read-only.

Commit

Permalink
Merge pull request #65 from secrethub/release-0.18.0
Browse files Browse the repository at this point in the history
Release v0.18.0
  • Loading branch information
SimonBarendse authored Mar 20, 2019
2 parents 3d9da8c + 4fdf395 commit 437559d
Show file tree
Hide file tree
Showing 18 changed files with 274 additions and 125 deletions.
18 changes: 18 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,22 @@ linters:
- golint
- staticcheck
- vet
- unused
- gosimple
- stylecheck
- structcheck
- varcheck
- interfacer
- unconvert
- ineffassign
- deadcode
- gocyclo
- typecheck
- depguard
- unparam
- nakedret
- gofmt
- misspell
- prealloc
- goconst
disable-all: true
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
# Go SecretHub
# Go client for SecretHub


[![GoDoc](http://img.shields.io/badge/godoc-reference-blue.svg)][godoc]
[![Travis CI](https://travis-ci.org/secrethub/secrethub-go.svg?branch=master)][travis-ci]
[![GolangCI](https://golangci.com/badges/github.com/secrethub/secrethub-go.svg)][golang-ci]
[![Go Report Card](https://goreportcard.com/badge/github.com/secrethub/secrethub-go)][goreportcard]

The official [SecretHub][secrethub] Go client library.
[SecretHub][secrethub] is a developer tool to help you keep database passwords, API tokens, and other secrets out of IT automation scripts.

> SecretHub is a developer tool to help you keep database passwords, API tokens, and other secrets out of IT automation scripts.
`secrethub-go` provides a client for various SecretHub APIs.

<img src="https://secrethub.io/img/secrethub-gopher.png" alt="Gopher" width="200px"/>
<img src="https://secrethub.io/img/secrethub-gopher.png" alt="Gopher" width="160px"/>

## Installation

Expand Down
15 changes: 8 additions & 7 deletions internals/api/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,14 @@ var (

// User represents a SecretHub user.
type User struct {
AccountID *uuid.UUID `json:"account_id"`
PublicKey []byte `json:"public_key"`
Username string `json:"username"`
FullName string `json:"full_name"`
Email string `json:"user_email,omitempty"` // Optional, private information is only returned for yourself
CreatedAt *time.Time `json:"created_at,omitempty"` // Optional, private information is only returned for yourself
LastLoginAt *time.Time `json:"last_login_at,omitempty"` // Optional, private information is only returned for yourself
AccountID *uuid.UUID `json:"account_id"`
PublicKey []byte `json:"public_key"`
Username string `json:"username"`
FullName string `json:"full_name"`
Email string `json:"user_email,omitempty"` // Optional, private information is only returned for yourself
EmailVerified bool `json:"email_verified,omitempty"` // Optional, private information is only returned for yourself
CreatedAt *time.Time `json:"created_at,omitempty"` // Optional, private information is only returned for yourself
LastLoginAt *time.Time `json:"last_login_at,omitempty"` // Optional, private information is only returned for yourself
}

// PrettyName returns a printable string with the username and full name.
Expand Down
19 changes: 10 additions & 9 deletions internals/crypto/ciphertext.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,25 +100,26 @@ func (ec encodedCiphertext) metadata() (encodedCiphertextMetadata, error) {

// newEncodedCiphertextMetadata creates a new encodedCiphertextMetadata from a map of metadata.
// Input of {"param": "foo", "second": "bar"} outputs "param=foo,second=bar".
func newEncodedCiphertextMetadata(metadataList map[string]string) encodedCiphertextMetadata {
metadata := ""

func newEncodedCiphertextMetadata(metadata map[string]string) encodedCiphertextMetadata {
// Sort all the keys of the metadataList so that metadata is always in alphabetical order.
var keys []string
for k := range metadataList {
keys = append(keys, k)
keys := make([]string, len(metadata))
i := 0
for k := range metadata {
keys[i] = k
i++
}
sort.Strings(keys)

res := ""
for _, k := range keys {
separator := ""
if len(metadata) > 0 {
if len(res) > 0 {
separator = ","
}
metadata = fmt.Sprintf("%s%s%s=%s", metadata, separator, k, metadataList[k])
res = fmt.Sprintf("%s%s%s=%s", res, separator, k, metadata[k])
}

return encodedCiphertextMetadata(metadata)
return encodedCiphertextMetadata(res)
}

// getValue returns a value from metadata.
Expand Down
2 changes: 1 addition & 1 deletion internals/crypto/ciphertext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func TestRSAAES_Success(t *testing.T) {
t.Fatal(err)
}

if bytes.Equal(ciphertext.aes.Data, input) {
if bytes.Equal(ciphertext.AES.Data, input) {
t.Error("encrypted data equals the original data")
}

Expand Down
140 changes: 85 additions & 55 deletions internals/crypto/rsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ func (pub RSAPublicKey) Encrypt(data []byte) (CiphertextRSAAES, error) {
}

return CiphertextRSAAES{
aes: aesData,
rsa: rsaData,
AES: aesData,
RSA: rsaData,
}, nil
}

Expand Down Expand Up @@ -229,12 +229,12 @@ func NewRSAPrivateKey(privateKey *rsa.PrivateKey) RSAPrivateKey {
// then uses the decrypted symmetric key to decrypt the rest of the ciphertext
// with the AES-GCM algorithm.
func (prv RSAPrivateKey) Decrypt(ciphertext CiphertextRSAAES) ([]byte, error) {
aesKeyData, err := prv.Unwrap(ciphertext.rsa)
aesKeyData, err := prv.Unwrap(ciphertext.RSA)
if err != nil {
return nil, err
}

return NewSymmetricKey(aesKeyData).Decrypt(ciphertext.aes)
return NewSymmetricKey(aesKeyData).Decrypt(ciphertext.AES)
}

// Unwrap uses the private key to decrypt a small ciphertext that has been encrypted
Expand Down Expand Up @@ -354,77 +354,92 @@ func (prv RSAPrivateKey) ExportPrivateKeyWithPassphrase(pass string) ([]byte, er

// CiphertextRSAAES represents data encrypted with AES-GCM, where the AES key is encrypted with RSA-OAEP.
type CiphertextRSAAES struct {
aes CiphertextAES
rsa CiphertextRSA
AES CiphertextAES
RSA CiphertextRSA
}

// MarshalJSON encodes the ciphertext in a string.
func (ct CiphertextRSAAES) MarshalJSON() ([]byte, error) {
data := base64.StdEncoding.EncodeToString(ct.aes.Data)
// EncodeToString encodes the ciphertext in a string.
func (ct CiphertextRSAAES) EncodeToString() string {
data := base64.StdEncoding.EncodeToString(ct.AES.Data)

metadata := newEncodedCiphertextMetadata(map[string]string{
"nonce": base64.StdEncoding.EncodeToString(ct.aes.Nonce),
"key": base64.StdEncoding.EncodeToString(ct.rsa.Data),
"nonce": base64.StdEncoding.EncodeToString(ct.AES.Nonce),
"key": base64.StdEncoding.EncodeToString(ct.RSA.Data),
})

return json.Marshal(fmt.Sprintf("%s$%s$%s", algorithmRSAAES, data, metadata))
return fmt.Sprintf("%s$%s$%s", algorithmRSAAES, data, metadata)
}

// UnmarshalJSON decodes a string into a ciphertext.
func (ct *CiphertextRSAAES) UnmarshalJSON(b []byte) error {
var s string
err := json.Unmarshal(b, &s)
if err != nil {
return err
}

if s == "" {
return nil
}
// MarshalJSON encodes the ciphertext in JSON.
func (ct CiphertextRSAAES) MarshalJSON() ([]byte, error) {
return json.Marshal(ct.EncodeToString())
}

// DecodeCiphertextRSAAESFromString decodes an encoded ciphertext string to an CiphertextRSAAES.
func DecodeCiphertextRSAAESFromString(s string) (CiphertextRSAAES, error) {
encoded, err := newEncodedCiphertext(s)
if err != nil {
return err
return CiphertextRSAAES{}, err
}

algorithm, err := encoded.algorithm()
if err != nil {
return errio.Error(err)
return CiphertextRSAAES{}, errio.Error(err)
}

if algorithm != algorithmRSAAES {
return ErrWrongAlgorithm
return CiphertextRSAAES{}, ErrWrongAlgorithm
}

encryptedData, err := encoded.data()
if err != nil {
return errio.Error(err)
return CiphertextRSAAES{}, errio.Error(err)
}

metadata, err := encoded.metadata()
if err != nil {
return errio.Error(err)
return CiphertextRSAAES{}, errio.Error(err)
}

aesNonce, err := metadata.getDecodedValue("nonce")
if err != nil {
return errio.Error(err)
return CiphertextRSAAES{}, errio.Error(err)
}

aesKey, err := metadata.getDecodedValue("key")
if err != nil {
return errio.Error(err)
return CiphertextRSAAES{}, errio.Error(err)
}

ct.aes = CiphertextAES{
Data: encryptedData,
Nonce: aesNonce,
return CiphertextRSAAES{
AES: CiphertextAES{
Data: encryptedData,
Nonce: aesNonce,
},
RSA: CiphertextRSA{
Data: aesKey,
},
}, nil
}

// UnmarshalJSON decodes JSON into a ciphertext.
func (ct *CiphertextRSAAES) UnmarshalJSON(b []byte) error {
if len(b) == 0 {
return nil
}

ct.rsa = CiphertextRSA{
Data: aesKey,
var s string
err := json.Unmarshal(b, &s)
if err != nil {
return err
}

ciphertext, err := DecodeCiphertextRSAAESFromString(s)
if err != nil {
return err
}

*ct = ciphertext
return nil
}

Expand All @@ -433,45 +448,60 @@ type CiphertextRSA struct {
Data []byte
}

// MarshalJSON encodes the ciphertext in a string.
func (ct CiphertextRSA) MarshalJSON() ([]byte, error) {
// EncodeToString encodes the ciphertext in a string.
func (ct CiphertextRSA) EncodeToString() string {
encodedKey := base64.StdEncoding.EncodeToString(ct.Data)

return json.Marshal(fmt.Sprintf("%s$%s$", algorithmRSA, encodedKey))
return fmt.Sprintf("%s$%s$", algorithmRSA, encodedKey)
}

// UnmarshalJSON decodes a string into a ciphertext.
func (ct *CiphertextRSA) UnmarshalJSON(b []byte) error {
var s string
err := json.Unmarshal(b, &s)
if err != nil {
return err
}

if s == "" {
return nil
}
// MarshalJSON encodes the ciphertext in JSON.
func (ct CiphertextRSA) MarshalJSON() ([]byte, error) {
return json.Marshal(ct.EncodeToString())
}

// DecodeCiphertextRSAFromString decodes an encoded ciphertext string to an CiphertextRSA.
func DecodeCiphertextRSAFromString(s string) (CiphertextRSA, error) {
encoded, err := newEncodedCiphertext(s)
if err != nil {
return err
return CiphertextRSA{}, err
}

algorithm, err := encoded.algorithm()
if err != nil {
return errio.Error(err)
return CiphertextRSA{}, errio.Error(err)
}

if algorithm != algorithmRSA {
return ErrWrongAlgorithm
return CiphertextRSA{}, ErrWrongAlgorithm
}

encryptedData, err := encoded.data()
if err != nil {
return errio.Error(err)
return CiphertextRSA{}, errio.Error(err)
}

ct.Data = encryptedData
return CiphertextRSA{
Data: encryptedData,
}, nil
}

// UnmarshalJSON decodes JSON into a ciphertext.
func (ct *CiphertextRSA) UnmarshalJSON(b []byte) error {
if len(b) == 0 {
return nil
}

var s string
err := json.Unmarshal(b, &s)
if err != nil {
return err
}

ciphertext, err := DecodeCiphertextRSAFromString(s)
if err != nil {
return err
}

*ct = ciphertext
return nil
}
10 changes: 9 additions & 1 deletion internals/crypto/rsa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,15 @@ func TestCiphertextRSA_MarshalJSON(t *testing.T) {
}

for name, tc := range cases {
t.Run(name, func(t *testing.T) {
t.Run(name+" encoded", func(t *testing.T) {
// Act
actual := tc.ciphertext.EncodeToString()

// Assert
assert.Equal(t, actual, tc.expected)
})

t.Run(name+" json", func(t *testing.T) {
// Act
actual, err := tc.ciphertext.MarshalJSON()
assert.OK(t, err)
Expand Down
6 changes: 3 additions & 3 deletions internals/crypto/scrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const (
// Read more about the parameters, how they work and how to determine their values
// in this blog post: https://blog.filippo.io/the-scrypt-parameters/
//
// Also, this the value recommented in the official GoDocs.
// Also, this the value recommended in the official GoDocs.
DefaultScryptN = 1 << 15

// DefaultScryptR determines the sequential read size of the scrypt
Expand All @@ -44,7 +44,7 @@ const (
// different memory characteristics. Use the N parameter instead to
// increase or decrease work.
//
// The value has been set to 8, which is the value recommented in
// The value has been set to 8, which is the value recommended in
// the official GoDocs.
DefaultScryptR = 8

Expand All @@ -54,7 +54,7 @@ const (
// be used to decrease the wall-clock-time of the key derivation
// function. Use the N parameter for that.
//
// The value has been set to 1, which is the value recommented in
// The value has been set to 1, which is the value recommended in
// the official GoDocs.
DefaultScryptP = 1
)
Expand Down
2 changes: 1 addition & 1 deletion internals/crypto/scrypt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ func TestIsPowerOfTwo(t *testing.T) {
}
}

// Below we test the assumption that increasing the salt lenght does
// Below we test the assumption that increasing the salt length does
// not significantly increase the execution time of key derivation
// function. The output of the benchmarks is documented below:
//
Expand Down
Loading

0 comments on commit 437559d

Please sign in to comment.