// // Copyright (c) SAS Institute Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package worker import ( "bytes" "context" "crypto" "crypto/rsa" "crypto/x509" "encoding/json" "errors" "io" "io/ioutil" "net/http" "net/url" "time" "github.com/sassoftware/relic/config" "github.com/sassoftware/relic/internal/workerrpc" "github.com/sassoftware/relic/token" ) const ( defaultRetries = 5 defaultTimeout = 60 * time.Second ) func (t *WorkerToken) request(ctx context.Context, path string, rr workerrpc.Request) (*workerrpc.Response, error) { req := &http.Request{ Method: http.MethodPost, URL: &url.URL{Scheme: "http", Host: t.addr, Path: path}, Header: http.Header{"Auth-Cookie": []string{t.cookie}}, } blob, err := json.Marshal(rr) if err != nil { return nil, err } req.GetBody = func() (io.ReadCloser, error) { return ioutil.NopCloser(bytes.NewReader(blob)), nil } return t.doRetry(req.WithContext(ctx)) } func (t *WorkerToken) Ping() error { return t.PingContext(context.Background()) } func (t *WorkerToken) PingContext(ctx context.Context) error { _, err := t.request(ctx, workerrpc.Ping, workerrpc.Request{}) return err } func (t *WorkerToken) Config() *config.TokenConfig { return t.tconf } func (t *WorkerToken) GetKey(keyName string) (token.Key, error) { return t.GetKeyContext(context.Background(), keyName) } func (t *WorkerToken) GetKeyContext(ctx context.Context, keyName string) (token.Key, error) { kconf, err := t.config.GetKey(keyName) if err != nil { return nil, err } res, err := t.request(ctx, workerrpc.GetKey, workerrpc.Request{KeyName: kconf.Name()}) if err != nil { return nil, err } pub, err := x509.ParsePKIXPublicKey(res.Value) if err != nil { return nil, err } return &workerKey{ token: t, kconf: kconf, public: pub, id: res.ID, }, nil } func (t *WorkerToken) Import(keyName string, privKey crypto.PrivateKey) (token.Key, error) { return nil, errors.New("function not implemented for worker token") } func (t *WorkerToken) ImportCertificate(cert *x509.Certificate, labelBase string) error { return errors.New("function not implemented for worker token") } func (t *WorkerToken) Generate(keyName string, keyType token.KeyType, bits uint) (token.Key, error) { return nil, errors.New("function not implemented for worker token") } func (t *WorkerToken) ListKeys(opts token.ListOptions) error { return errors.New("function not implemented for worker token") } type workerKey struct { token *WorkerToken kconf *config.KeyConfig public crypto.PublicKey id []byte } func (k *workerKey) Config() *config.KeyConfig { return k.kconf } func (k *workerKey) Public() crypto.PublicKey { return k.public } func (k *workerKey) GetID() []byte { return k.id } func (k *workerKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { return k.SignContext(context.Background(), digest, opts) } func (k *workerKey) SignContext(ctx context.Context, digest []byte, opts crypto.SignerOpts) ([]byte, error) { rr := workerrpc.Request{ KeyName: k.kconf.Name(), Digest: digest, } if opts != nil { rr.Hash = uint(opts.HashFunc()) if o, ok := opts.(*rsa.PSSOptions); ok { rr.SaltLength = &o.SaltLength } } res, err := k.token.request(ctx, workerrpc.Sign, rr) if err != nil { return nil, err } return res.Value, nil } func (k *workerKey) ImportCertificate(cert *x509.Certificate) error { return errors.New("function not implemented for worker token") }