refactor: Move functions from kms pkg to new libs/crypto pkg (#1650)

pull/1667/head
Jim 4 years ago committed by GitHub
parent a86ee59bcd
commit dd2c3807cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2,10 +2,6 @@ package oidc
import (
"context"
"crypto/ed25519"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"fmt"
"net/url"
"sort"
@ -13,7 +9,7 @@ import (
"github.com/hashicorp/boundary/internal/auth/oidc/store"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/libs/crypto"
"github.com/hashicorp/boundary/internal/oplog"
wrapping "github.com/hashicorp/go-kms-wrapping"
"github.com/hashicorp/go-kms-wrapping/structwrapping"
@ -236,17 +232,14 @@ func (a *AuthMethod) hmacClientSecret(ctx context.Context, cipher wrapping.Wrapp
if cipher == nil {
return errors.New(ctx, errors.InvalidParameter, op, "missing cipher")
}
reader, err := kms.NewDerivedReader(cipher, 32, []byte(a.PublicId), nil)
// this operation currently uses the legacy WithEd25519 option for hmac'ing.
// we should likely deprecate this and introduce a new "crypto version" of
// this attribute.
hm, err := crypto.HmacSha256(ctx, []byte(a.ClientSecret), cipher, []byte(a.PublicId), nil, crypto.WithBase64Encoding(), crypto.WithEd25519())
if err != nil {
return errors.Wrap(ctx, err, op)
}
key, _, err := ed25519.GenerateKey(reader)
if err != nil {
return errors.New(ctx, errors.Encrypt, op, "unable to generate derived key")
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Code(errors.Encryption)))
}
mac := hmac.New(sha256.New, key)
_, _ = mac.Write([]byte(a.ClientSecret))
a.ClientSecretHmac = base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
a.ClientSecretHmac = hm
return nil
}

@ -11,6 +11,7 @@ import (
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/libs/crypto"
wrapping "github.com/hashicorp/go-kms-wrapping"
"github.com/hashicorp/go-kms-wrapping/wrappers/aead"
@ -189,7 +190,7 @@ func requestWrappingWrapper(ctx context.Context, k *kms.Kms, scopeId, authMethod
}
// okay, I guess we need to derive a new key for this combo of oidcWrapper and authMethod
reader, err := kms.NewDerivedReader(oidcWrapper, 32, []byte(authMethodId), []byte(scopeId))
reader, err := crypto.NewDerivedReader(oidcWrapper, 32, []byte(authMethodId), []byte(scopeId))
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}

@ -2,15 +2,12 @@ package vault
import (
"context"
"crypto/ed25519"
"crypto/hmac"
"crypto/sha256"
"database/sql"
"github.com/hashicorp/boundary/internal/credential/vault/store"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/libs/crypto"
"github.com/hashicorp/boundary/internal/oplog"
wrapping "github.com/hashicorp/go-kms-wrapping"
"github.com/hashicorp/go-kms-wrapping/structwrapping"
@ -103,17 +100,14 @@ func (c *ClientCertificate) hmacCertificateKey(ctx context.Context, cipher wrapp
if cipher == nil {
return errors.New(ctx, errors.InvalidParameter, op, "missing cipher")
}
reader, err := kms.NewDerivedReader(cipher, 32, []byte(c.StoreId), nil)
// this operation currently uses the legacy WithEd25519 option for hmac'ing.
// we should likely deprecate this and introduce a new "crypto version" of
// this attribute.
hm, err := crypto.HmacSha256(ctx, c.CertificateKey, cipher, []byte(c.StoreId), nil, crypto.WithEd25519())
if err != nil {
return errors.Wrap(ctx, err, op)
}
key, _, err := ed25519.GenerateKey(reader)
if err != nil {
return errors.New(ctx, errors.Encrypt, op, "unable to generate derived key")
}
mac := hmac.New(sha256.New, key)
_, _ = mac.Write(c.CertificateKey)
c.CertificateKeyHmac = mac.Sum(nil)
c.CertificateKeyHmac = []byte(hm)
return nil
}

@ -2,18 +2,16 @@ package vault
import (
"context"
"crypto/hmac"
"crypto/sha256"
"database/sql"
"time"
"github.com/hashicorp/boundary/internal/credential/vault/store"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/libs/crypto"
"github.com/hashicorp/boundary/internal/oplog"
wrapping "github.com/hashicorp/go-kms-wrapping"
"github.com/hashicorp/go-kms-wrapping/structwrapping"
"golang.org/x/crypto/blake2b"
"google.golang.org/protobuf/proto"
)
@ -74,16 +72,15 @@ func newToken(storeId string, token TokenSecret, accessor []byte, expiration tim
accessorCopy := make([]byte, len(accessor))
copy(accessorCopy, accessor)
key := blake2b.Sum256(accessorCopy)
mac := hmac.New(sha256.New, key[:])
_, _ = mac.Write(tokenCopy)
hmac := mac.Sum(nil)
hmac, err := crypto.HmacSha256WithPrk(context.Background(), tokenCopy, accessorCopy)
if err != nil {
return nil, errors.WrapDeprecated(err, op, errors.WithCode(errors.Encrypt))
}
t := &Token{
expiration: expiration.Round(time.Second),
Token: &store.Token{
StoreId: storeId,
TokenHmac: hmac,
TokenHmac: []byte(hmac),
Token: tokenCopy,
Status: string(CurrentToken),
},

@ -2,9 +2,7 @@ package kms
import (
"context"
"crypto/sha256"
"fmt"
"io"
"sync"
"github.com/hashicorp/boundary/internal/db"
@ -13,7 +11,6 @@ import (
wrapping "github.com/hashicorp/go-kms-wrapping"
"github.com/hashicorp/go-kms-wrapping/wrappers/aead"
"github.com/hashicorp/go-kms-wrapping/wrappers/multiwrapper"
"golang.org/x/crypto/hkdf"
)
// ExternalWrappers holds wrappers defined outside of Boundary, e.g. in its
@ -371,41 +368,3 @@ func (k *Kms) loadDek(ctx context.Context, scopeId string, purpose KeyPurpose, r
return multi, nil
}
// DerivedReader returns a reader from which keys can be read, using the
// given wrapper, reader length limit, salt and context info. Salt and info can
// be nil.
//
// Example:
// reader, _ := NewDerivedReader(wrapper, userId, jobId)
// key := ed25519.GenerateKey(reader)
func NewDerivedReader(wrapper wrapping.Wrapper, lenLimit int64, salt, info []byte) (*io.LimitedReader, error) {
const op = "kms.NewDerivedReader"
if wrapper == nil {
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "missing wrapper")
}
if lenLimit < 20 {
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "lenLimit must be >= 20")
}
var aeadWrapper *aead.Wrapper
switch w := wrapper.(type) {
case *multiwrapper.MultiWrapper:
raw := w.WrapperForKeyID("__base__")
var ok bool
if aeadWrapper, ok = raw.(*aead.Wrapper); !ok {
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "unexpected wrapper type from multiwrapper base")
}
case *aead.Wrapper:
if w.GetKeyBytes() == nil {
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "aead wrapper missing bytes")
}
aeadWrapper = w
default:
return nil, errors.NewDeprecated(errors.InvalidParameter, op, "unknown wrapper type")
}
reader := hkdf.New(sha256.New, aeadWrapper.GetKeyBytes(), salt, info)
return &io.LimitedReader{
R: reader,
N: lenLimit,
}, nil
}

@ -3,19 +3,12 @@ package kms
import (
"context"
"crypto/rand"
"crypto/sha256"
"io"
"testing"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/types/scope"
wrapping "github.com/hashicorp/go-kms-wrapping"
"github.com/hashicorp/go-kms-wrapping/wrappers/aead"
"github.com/hashicorp/go-uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/hkdf"
)
func TestKms_KeyId(t *testing.T) {
@ -73,101 +66,3 @@ func TestKms_KeyId(t *testing.T) {
_, err = kmsCache.GetWrapper(ctx, scope.Global.String(), KeyPurposeDatabase, WithKeyId("foo"))
require.Error(err)
}
func TestNewDerivedReader(t *testing.T) {
wrapper := db.TestWrapper(t)
type args struct {
wrapper wrapping.Wrapper
lenLimit int64
salt []byte
info []byte
}
tests := []struct {
name string
args args
want *io.LimitedReader
wantErr bool
wantErrCode errors.Code
wantErrContains string
}{
{
name: "valid-with-salt",
args: args{
wrapper: wrapper,
lenLimit: 32,
info: nil,
salt: []byte("salt"),
},
want: &io.LimitedReader{
R: hkdf.New(sha256.New, wrapper.(*aead.Wrapper).GetKeyBytes(), []byte("salt"), nil),
N: 32,
},
},
{
name: "valid-with-salt-info",
args: args{
wrapper: wrapper,
lenLimit: 32,
info: []byte("info"),
salt: []byte("salt"),
},
want: &io.LimitedReader{
R: hkdf.New(sha256.New, wrapper.(*aead.Wrapper).GetKeyBytes(), []byte("salt"), []byte("info")),
N: 32,
},
},
{
name: "nil-wrapper",
args: args{
wrapper: nil,
lenLimit: 10,
info: []byte("info"),
salt: []byte("salt"),
},
wantErr: true,
wantErrCode: errors.InvalidParameter,
wantErrContains: "missing wrapper",
},
{
name: "too-short",
args: args{
wrapper: wrapper,
lenLimit: 10,
info: []byte("info"),
salt: []byte("salt"),
},
wantErr: true,
wantErrCode: errors.InvalidParameter,
wantErrContains: "lenLimit must be >= 20",
},
{
name: "wrapper-with-no-bytes",
args: args{
wrapper: &aead.Wrapper{},
lenLimit: 32,
info: nil,
salt: []byte("salt"),
},
wantErr: true,
wantErrCode: errors.InvalidParameter,
wantErrContains: "missing bytes",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
got, err := NewDerivedReader(tt.args.wrapper, tt.args.lenLimit, tt.args.salt, tt.args.info)
if tt.wantErr {
require.Error(err)
assert.Truef(errors.Match(errors.T(errors.InvalidParameter), err), "unexpected error: %s", err)
if tt.wantErrContains != "" {
assert.Contains(err.Error(), tt.wantErrContains)
}
return
}
require.NoError(err)
assert.Equal(tt.want, got)
})
}
}

@ -0,0 +1,50 @@
package crypto
import (
"crypto/sha256"
"fmt"
"io"
wrapping "github.com/hashicorp/go-kms-wrapping"
"github.com/hashicorp/go-kms-wrapping/wrappers/aead"
"github.com/hashicorp/go-kms-wrapping/wrappers/multiwrapper"
"golang.org/x/crypto/hkdf"
)
// DerivedReader returns a reader from which keys can be read, using the
// given wrapper, reader length limit, salt and context info. Salt and info can
// be nil.
//
// Example:
// reader, _ := NewDerivedReader(wrapper, userId, jobId)
// key := ed25519.GenerateKey(reader)
func NewDerivedReader(wrapper wrapping.Wrapper, lenLimit int64, salt, info []byte) (*io.LimitedReader, error) {
const op = "crypto.NewDerivedReader"
if wrapper == nil {
return nil, fmt.Errorf("%s: missing wrapper: %w", op, ErrInvalidParameter)
}
if lenLimit < 20 {
return nil, fmt.Errorf("%s: lenLimit must be >= 20: %w", op, ErrInvalidParameter)
}
var aeadWrapper *aead.Wrapper
switch w := wrapper.(type) {
case *multiwrapper.MultiWrapper:
raw := w.WrapperForKeyID("__base__")
var ok bool
if aeadWrapper, ok = raw.(*aead.Wrapper); !ok {
return nil, fmt.Errorf("%s: unexpected wrapper type from multiwrapper base: %w", op, ErrInvalidParameter)
}
case *aead.Wrapper:
if w.GetKeyBytes() == nil {
return nil, fmt.Errorf("%s: aead wrapper missing bytes: %w", op, ErrInvalidParameter)
}
aeadWrapper = w
default:
return nil, fmt.Errorf("%s: unknown wrapper type: %w", op, ErrInvalidParameter)
}
reader := hkdf.New(sha256.New, aeadWrapper.GetKeyBytes(), salt, info)
return &io.LimitedReader{
R: reader,
N: lenLimit,
}, nil
}

@ -0,0 +1,111 @@
package crypto
import (
"crypto/sha256"
"io"
"testing"
wrapping "github.com/hashicorp/go-kms-wrapping"
"github.com/hashicorp/go-kms-wrapping/wrappers/aead"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/hkdf"
)
func TestNewDerivedReader(t *testing.T) {
wrapper := TestWrapper(t)
type args struct {
wrapper wrapping.Wrapper
lenLimit int64
salt []byte
info []byte
}
tests := []struct {
name string
args args
want *io.LimitedReader
wantErr bool
wantErrCode error
wantErrContains string
}{
{
name: "valid-with-salt",
args: args{
wrapper: wrapper,
lenLimit: 32,
info: nil,
salt: []byte("salt"),
},
want: &io.LimitedReader{
R: hkdf.New(sha256.New, wrapper.(*aead.Wrapper).GetKeyBytes(), []byte("salt"), nil),
N: 32,
},
},
{
name: "valid-with-salt-info",
args: args{
wrapper: wrapper,
lenLimit: 32,
info: []byte("info"),
salt: []byte("salt"),
},
want: &io.LimitedReader{
R: hkdf.New(sha256.New, wrapper.(*aead.Wrapper).GetKeyBytes(), []byte("salt"), []byte("info")),
N: 32,
},
},
{
name: "nil-wrapper",
args: args{
wrapper: nil,
lenLimit: 10,
info: []byte("info"),
salt: []byte("salt"),
},
wantErr: true,
wantErrCode: ErrInvalidParameter,
wantErrContains: "missing wrapper",
},
{
name: "too-short",
args: args{
wrapper: wrapper,
lenLimit: 10,
info: []byte("info"),
salt: []byte("salt"),
},
wantErr: true,
wantErrCode: ErrInvalidParameter,
wantErrContains: "lenLimit must be >= 20",
},
{
name: "wrapper-with-no-bytes",
args: args{
wrapper: &aead.Wrapper{},
lenLimit: 32,
info: nil,
salt: []byte("salt"),
},
wantErr: true,
wantErrCode: ErrInvalidParameter,
wantErrContains: "missing bytes",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
got, err := NewDerivedReader(tt.args.wrapper, tt.args.lenLimit, tt.args.salt, tt.args.info)
if tt.wantErr {
require.Error(err)
assert.ErrorIsf(err, tt.wantErrCode, "unexpected error: %s", err)
if tt.wantErrContains != "" {
assert.Contains(err.Error(), tt.wantErrContains)
}
return
}
require.NoError(err)
assert.Equal(tt.want, got)
})
}
}

@ -0,0 +1,9 @@
package crypto
import (
"errors"
)
var (
ErrInvalidParameter = errors.New("Invalid parameter")
)

@ -0,0 +1,94 @@
package crypto
import (
"context"
"crypto/ed25519"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"fmt"
"io"
wrapping "github.com/hashicorp/go-kms-wrapping"
"golang.org/x/crypto/blake2b"
)
// HmacSha256WithPrk will HmacSha256 using the provided prk. See HmacSha256 for
// options supported.
func HmacSha256WithPrk(ctx context.Context, data, prk []byte, opt ...Option) (string, error) {
opt = append(opt, WithPrk(prk))
return HmacSha256(ctx, data, nil, nil, nil, opt...)
}
// HmacSha256 the provided data. Supports WithPrefix, WithEd25519 and WithPrk
// options. WithEd25519 is a "legacy" way to complete this operation and should
// not be used in new operations unless backward compatibility is needed. The
// WithPrefix option will prepend the prefix to the hmac-sha256 value.
func HmacSha256(ctx context.Context, data []byte, cipher wrapping.Wrapper, salt, info []byte, opt ...Option) (string, error) {
const op = "crypto.HmacSha256"
opts, err := getOpts(opt...)
if err != nil {
return "", fmt.Errorf("%s: unable to get options: %w", op, err)
}
if data == nil {
return "", fmt.Errorf("%s: missing data: %w", op, ErrInvalidParameter)
}
if cipher == nil && opts.withPrk == nil {
return "", fmt.Errorf("%s: you must specify either a wrapper or prk: %w", op, ErrInvalidParameter)
}
if cipher != nil && opts.withPrk != nil {
return "", fmt.Errorf("%s: you cannot specify both a wrapper or prk: %w", op, ErrInvalidParameter)
}
if opts.withEd25519 && opts.withPrk != nil {
return "", fmt.Errorf("%s: you cannot specify both ed25519 and a prk: %w", op, ErrInvalidParameter)
}
var key [32]byte
switch {
case opts.withPrk != nil:
key = blake2b.Sum256(opts.withPrk)
case opts.withEd25519:
reader, err := NewDerivedReader(cipher, 32, salt, info)
if err != nil {
return "", fmt.Errorf("%s: %w", op, err)
}
edKey, _, err := ed25519.GenerateKey(reader)
if err != nil {
return "", fmt.Errorf("%s: unable to generate derived key: %w", op, ErrInvalidParameter)
}
n := copy(key[:], edKey)
if n != 32 {
return "", fmt.Errorf("%s: expected to copy 32 bytes and got: %d", op, n)
}
default:
reader, err := NewDerivedReader(cipher, 32, salt, info)
if err != nil {
return "", fmt.Errorf("%s: %w", op, err)
}
readerKey := make([]byte, 32)
n, err := io.ReadFull(reader, readerKey)
if err != nil {
return "", fmt.Errorf("%s: %w", op, err)
}
if n != 32 {
return "", fmt.Errorf("%s: expected to read 32 bytes and got: %d", op, n)
}
key = blake2b.Sum256(readerKey)
}
mac := hmac.New(sha256.New, key[:])
_, _ = mac.Write(data)
hmac := mac.Sum(nil)
var hmacString string
switch opts.withBase64Encoding {
case true:
hmacString = base64.RawURLEncoding.EncodeToString(hmac)
case false:
hmacString = string(hmac)
}
if opts.withPrefix != "" {
return opts.withPrefix + hmacString, nil
}
return hmacString, nil
}

@ -0,0 +1,168 @@
package crypto
import (
"context"
"crypto/ed25519"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"io"
"testing"
wrapping "github.com/hashicorp/go-kms-wrapping"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/blake2b"
)
func Test_HmacSha256(t *testing.T) {
testCtx := context.Background()
testWrapper := TestWrapper(t)
tests := []struct {
name string
data []byte
wrapper wrapping.Wrapper
salt []byte
info []byte
opts []Option
wantHmac string
wantErr bool
wantErrIs error
}{
{
name: "missing data",
wrapper: testWrapper,
wantErr: true,
wantErrIs: ErrInvalidParameter,
},
{
name: "missing wrapper",
data: []byte("test"),
wantErr: true,
wantErrIs: ErrInvalidParameter,
},
{
name: "prk-and-ed25519",
data: []byte("test"),
wrapper: testWrapper,
opts: []Option{WithPrk([]byte("prk")), WithEd25519()},
wantErr: true,
wantErrIs: ErrInvalidParameter,
},
{
name: "prk-and-wrapper",
data: []byte("test"),
wrapper: testWrapper,
opts: []Option{WithPrk([]byte("prk")), WithEd25519()},
wantErr: true,
wantErrIs: ErrInvalidParameter,
},
{
name: "blake2b-with-prefix",
data: []byte("test"),
wrapper: testWrapper,
opts: []Option{WithPrefix("prefix:")},
wantHmac: testWithBlake2b(t, []byte("test"), testWrapper, nil, nil, WithPrefix("prefix:")),
},
{
name: "blake2b-with-prefix-with-bas64",
data: []byte("test"),
wrapper: testWrapper,
opts: []Option{WithPrefix("prefix:"), WithBase64Encoding()},
wantHmac: testWithBlake2b(t, []byte("test"), testWrapper, nil, nil, WithPrefix("prefix:"), WithBase64Encoding()),
},
{
name: "with-prk",
data: []byte("test"),
opts: []Option{WithPrk([]byte("prk-0123456789012345678901234567890"))},
wantHmac: testWithBlake2b(t, []byte("test"), testWrapper, nil, nil, WithPrk([]byte("prk-0123456789012345678901234567890"))),
},
{
name: "withEd25519",
data: []byte("test"),
wrapper: testWrapper,
opts: []Option{WithEd25519()},
wantHmac: testWithEd25519(t, []byte("test"), testWrapper, nil, nil),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
hm, err := HmacSha256(testCtx, tt.data, tt.wrapper, tt.salt, tt.info, tt.opts...)
if tt.wantErr {
require.Error(err)
if tt.wantErrIs != nil {
assert.ErrorIs(err, tt.wantErrIs)
}
return
}
require.NoError(err)
assert.Equal(tt.wantHmac, hm)
})
}
t.Run("HmacSha256WithPrk", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
hm, err := HmacSha256WithPrk(testCtx, []byte("test"), []byte("prk-0123456789012345678901234567890"))
require.NoError(err)
want := testWithBlake2b(t, []byte("test"), testWrapper, nil, nil, WithPrk([]byte("prk-0123456789012345678901234567890")))
assert.Equal(want, hm)
})
}
func testWithEd25519(t *testing.T, data []byte, w wrapping.Wrapper, salt, info []byte, opt ...Option) string {
t.Helper()
require := require.New(t)
reader, err := NewDerivedReader(w, 32, salt, info)
require.NoError(err)
edKey, _, err := ed25519.GenerateKey(reader)
require.NoError(err)
var key [32]byte
n := copy(key[:], edKey)
require.Equal(n, 32)
return testHmac(t, key[:], data, opt...)
}
func testWithBlake2b(t *testing.T, data []byte, w wrapping.Wrapper, salt, info []byte, opt ...Option) string {
t.Helper()
require := require.New(t)
require.NotNil(data)
require.NotNil(w)
opts, err := getOpts(opt...)
require.NoError(err)
var key [32]byte
switch {
case opts.withPrk != nil:
key = blake2b.Sum256(opts.withPrk)
default:
reader, err := NewDerivedReader(w, 32, salt, info)
require.NoError(err)
readerKey := make([]byte, 32)
n, err := io.ReadFull(reader, readerKey)
require.NoError(err)
require.Equal(n, 32)
key = blake2b.Sum256(readerKey)
}
return testHmac(t, key[:], data, opt...)
}
func testHmac(t *testing.T, key, data []byte, opt ...Option) string {
t.Helper()
require := require.New(t)
mac := hmac.New(sha256.New, key)
_, _ = mac.Write(data)
hmac := mac.Sum(nil)
var hmacString string
opts, err := getOpts(opt...)
require.NoError(err)
switch opts.withBase64Encoding {
case true:
hmacString = base64.RawURLEncoding.EncodeToString(hmac)
case false:
hmacString = string(hmac)
}
if opts.withPrefix != "" {
return opts.withPrefix + hmacString
}
return hmacString
}

@ -0,0 +1,63 @@
package crypto
// getOpts - iterate the inbound Options and return a struct.
func getOpts(opt ...Option) (*options, error) {
opts := getDefaultOptions()
for _, o := range opt {
if o != nil {
if err := o(opts); err != nil {
return nil, err
}
}
}
return opts, nil
}
// Option - how Options are passed as arguments.
type Option func(*options) error
// options = how options are represented
type options struct {
withPrefix string
withPrk []byte
withEd25519 bool
withBase64Encoding bool
}
func getDefaultOptions() *options {
return &options{}
}
// WithPrefix allows an optional prefix to be specified for the data returned
func WithPrefix(prefix string) Option {
return func(o *options) error {
o.withPrefix = prefix
return nil
}
}
// WithPrk allows an optional PRK (pseudorandom key) to be specified for an
// operation. If you're using this option with HmacSha256, you might consider
// using HmacSha256WithPrk instead.
func WithPrk(prk []byte) Option {
return func(o *options) error {
o.withPrk = prk
return nil
}
}
// WithEd25519 allows an optional request to use ed25519 during the operation
func WithEd25519() Option {
return func(o *options) error {
o.withEd25519 = true
return nil
}
}
// WithBase64Encoding allows an optional request to base64 encode the data returned
func WithBase64Encoding() Option {
return func(o *options) error {
o.withBase64Encoding = true
return nil
}
}

@ -0,0 +1,45 @@
package crypto
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// Test_GetOpts provides unit tests for GetOpts and all the options
func Test_GetOpts(t *testing.T) {
t.Parallel()
t.Run("WithPrefix", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
opts, err := getOpts(WithPrefix("test"))
require.NoError(err)
testOpts := getDefaultOptions()
testOpts.withPrefix = "test"
assert.Equal(opts, testOpts)
})
t.Run("WithPrk", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
opts, err := getOpts(WithPrk([]byte("test")))
require.NoError(err)
testOpts := getDefaultOptions()
testOpts.withPrk = []byte("test")
assert.Equal(opts, testOpts)
})
t.Run("WithEd25519", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
opts, err := getOpts(WithEd25519())
require.NoError(err)
testOpts := getDefaultOptions()
testOpts.withEd25519 = true
assert.Equal(opts, testOpts)
})
t.Run("WithBase64Encoding", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
opts, err := getOpts(WithBase64Encoding())
require.NoError(err)
testOpts := getDefaultOptions()
testOpts.withBase64Encoding = true
assert.Equal(opts, testOpts)
})
}

@ -0,0 +1,33 @@
package crypto
import (
"crypto/rand"
"encoding/base64"
"testing"
wrapping "github.com/hashicorp/go-kms-wrapping"
"github.com/hashicorp/go-kms-wrapping/wrappers/aead"
)
// TestWrapper initializes an AEAD wrapping.Wrapper for testing
func TestWrapper(t *testing.T) wrapping.Wrapper {
rootKey := make([]byte, 32)
n, err := rand.Read(rootKey)
if err != nil {
t.Fatal(err)
}
if n != 32 {
t.Fatal(n)
}
root := aead.NewWrapper(nil)
_, err = root.SetConfig(map[string]string{
"key_id": base64.StdEncoding.EncodeToString(rootKey),
})
if err != nil {
t.Fatal(err)
}
if err := root.SetAESGCMKeyBytes(rootKey); err != nil {
t.Fatal(err)
}
return root
}

@ -4,7 +4,7 @@ import (
"crypto/ed25519"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/libs/crypto"
wrapping "github.com/hashicorp/go-kms-wrapping"
)
@ -19,8 +19,11 @@ func DeriveED25519Key(wrapper wrapping.Wrapper, userId, jobId string) (ed25519.P
if jobId != "" {
jId = []byte(jobId)
}
if wrapper == nil {
return nil, nil, errors.NewDeprecated(errors.InvalidParameter, op, "missing wrapper")
}
reader, err := kms.NewDerivedReader(wrapper, 32, uId, jId)
reader, err := crypto.NewDerivedReader(wrapper, 32, uId, jId)
if err != nil {
return nil, nil, errors.WrapDeprecated(err, op)
}

@ -6,7 +6,7 @@ import (
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/libs/crypto"
wrapping "github.com/hashicorp/go-kms-wrapping"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -39,7 +39,7 @@ func TestDeriveED25519Key(t *testing.T) {
jobId: "jobId",
},
want: func() keys {
reader, err := kms.NewDerivedReader(wrapper, 32, []byte("userId"), []byte("jobId"))
reader, err := crypto.NewDerivedReader(wrapper, 32, []byte("userId"), []byte("jobId"))
require.NoError(t, err)
pub, priv, err := ed25519.GenerateKey(reader)
require.NoError(t, err)

Loading…
Cancel
Save