github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/creds/creds.go (about)

     1  // Copyright 2019 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package creds
    16  
    17  import (
    18  	"context"
    19  	"crypto/sha512"
    20  	"encoding/base32"
    21  	"encoding/base64"
    22  	"errors"
    23  	"fmt"
    24  	"time"
    25  
    26  	"golang.org/x/crypto/ed25519"
    27  	"gopkg.in/square/go-jose.v2"
    28  	"gopkg.in/square/go-jose.v2/jwt"
    29  
    30  	"github.com/dolthub/dolt/go/libraries/utils/set"
    31  	"github.com/dolthub/dolt/go/store/util/datetime"
    32  )
    33  
    34  const (
    35  	pubKeySize  = 32
    36  	privKeySize = 64
    37  )
    38  
    39  const (
    40  	B32CharEncoding = "0123456789abcdefghijklmnopqrstuv"
    41  
    42  	B32EncodedPubKeyLen = 52
    43  	B32EncodedKeyIdLen  = 45
    44  
    45  	JWTKIDHeader           = "kid"
    46  	JWTAlgHeader           = "alg"
    47  	DoltTokenVersionHeader = "dolt_token_version"
    48  )
    49  
    50  var B32CredsByteSet = set.NewByteSet([]byte(B32CharEncoding))
    51  var B32CredsEncoding = base32.NewEncoding(B32CharEncoding).WithPadding(base32.NoPadding)
    52  var EmptyCreds = DoltCreds{}
    53  
    54  var ErrBadB32CredsEncoding = errors.New("bad base32 credentials encoding")
    55  var ErrCredsNotFound = errors.New("credentials not found")
    56  
    57  type DoltCreds struct {
    58  	PubKey  []byte
    59  	PrivKey []byte
    60  	KeyID   []byte
    61  }
    62  
    63  func PubKeyStrToKIDStr(pub string) (string, error) {
    64  	data, err := B32CredsEncoding.DecodeString(pub)
    65  
    66  	if err != nil {
    67  		return "", err
    68  	}
    69  
    70  	return PubKeyToKIDStr(data), nil
    71  }
    72  
    73  func PubKeyToKID(pub []byte) []byte {
    74  	kidBytes := sha512.Sum512_224(pub)
    75  	return kidBytes[:]
    76  }
    77  
    78  func PubKeyToKIDStr(pub []byte) string {
    79  	kidBytes := PubKeyToKID(pub)
    80  	kid := B32CredsEncoding.EncodeToString(kidBytes)
    81  	return kid
    82  }
    83  
    84  func GenerateCredentials() (DoltCreds, error) {
    85  	pub, priv, err := ed25519.GenerateKey(nil)
    86  
    87  	if err == nil {
    88  		kid := PubKeyToKID(pub)
    89  		return DoltCreds{pub, priv, kid}, nil
    90  	}
    91  
    92  	return DoltCreds{}, err
    93  }
    94  
    95  func (dc DoltCreds) HasPrivKey() bool {
    96  	return len(dc.PrivKey) > 0
    97  }
    98  
    99  func (dc DoltCreds) IsPrivKeyValid() bool {
   100  	return len(dc.PrivKey) == privKeySize
   101  }
   102  
   103  func (dc DoltCreds) IsPubKeyValid() bool {
   104  	return len(dc.PubKey) == pubKeySize
   105  }
   106  
   107  func (dc DoltCreds) PubKeyBase32Str() string {
   108  	return B32CredsEncoding.EncodeToString(dc.PubKey)
   109  }
   110  
   111  func (dc DoltCreds) PrivKeyBase32Str() string {
   112  	return B32CredsEncoding.EncodeToString(dc.PubKey)
   113  }
   114  
   115  func (dc DoltCreds) KeyIDBase32Str() string {
   116  	return B32CredsEncoding.EncodeToString(dc.KeyID)
   117  }
   118  
   119  func (dc DoltCreds) Sign(data []byte) []byte {
   120  	return ed25519.Sign(dc.PrivKey, data)
   121  }
   122  
   123  type RPCCreds struct {
   124  	PrivKey    ed25519.PrivateKey
   125  	KeyID      string
   126  	Audience   string
   127  	Issuer     string
   128  	Subject    string
   129  	RequireTLS bool
   130  }
   131  
   132  func (c *RPCCreds) toBearerToken() (string, error) {
   133  	key := jose.SigningKey{Algorithm: jose.EdDSA, Key: c.PrivKey}
   134  	opts := &jose.SignerOptions{ExtraHeaders: map[jose.HeaderKey]interface{}{
   135  		JWTKIDHeader:           c.KeyID,
   136  		DoltTokenVersionHeader: "2023.01",
   137  	}}
   138  
   139  	signer, err := jose.NewSigner(key, opts)
   140  	if err != nil {
   141  		return "", err
   142  	}
   143  
   144  	jwtBuilder := jwt.Signed(signer)
   145  	jwtBuilder = jwtBuilder.Claims(jwt.Claims{
   146  		Audience: []string{c.Audience},
   147  		Issuer:   c.Issuer,
   148  		Subject:  c.Subject,
   149  		Expiry:   jwt.NewNumericDate(datetime.Now().Add(30 * time.Second)),
   150  	})
   151  
   152  	return jwtBuilder.CompactSerialize()
   153  }
   154  
   155  func (c *RPCCreds) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
   156  	t, err := c.toBearerToken()
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  	return map[string]string{
   161  		"authorization": "Bearer " + t,
   162  	}, nil
   163  }
   164  
   165  func (c *RPCCreds) RequireTransportSecurity() bool {
   166  	return c.RequireTLS
   167  }
   168  
   169  const ClientIssuer = "dolt-client.dolthub.com"
   170  
   171  func (dc DoltCreds) RPCCreds(audience string) *RPCCreds {
   172  	b32KIDStr := dc.KeyIDBase32Str()
   173  	return &RPCCreds{
   174  		PrivKey:    ed25519.PrivateKey(dc.PrivKey),
   175  		KeyID:      b32KIDStr,
   176  		Audience:   audience,
   177  		Issuer:     ClientIssuer,
   178  		Subject:    "doltClientCredentials/" + b32KIDStr,
   179  		RequireTLS: false,
   180  	}
   181  }
   182  
   183  type DoltCredsForPass struct {
   184  	Username string
   185  	Password string
   186  }
   187  
   188  type RPCCredsForPass struct {
   189  	RequireTLS       bool
   190  	UserPassContents string
   191  }
   192  
   193  func (dcp DoltCredsForPass) ToBase64Str() string {
   194  	bStr := []byte(fmt.Sprintf("%s:%s", dcp.Username, dcp.Password))
   195  	return base64.StdEncoding.EncodeToString(bStr)
   196  }
   197  
   198  func (dc DoltCredsForPass) RPCCreds() *RPCCredsForPass {
   199  	return &RPCCredsForPass{
   200  		RequireTLS:       false,
   201  		UserPassContents: dc.ToBase64Str(),
   202  	}
   203  }
   204  
   205  func (c *RPCCredsForPass) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
   206  	return map[string]string{
   207  		"authorization": "Basic " + c.UserPassContents,
   208  	}, nil
   209  }
   210  
   211  func (c *RPCCredsForPass) RequireTransportSecurity() bool {
   212  	return c.RequireTLS
   213  }