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 }