github.com/nats-io/jwt/v2@v2.5.6/v1compat/creds_utils.go (about) 1 /* 2 * Copyright 2019-2020 The NATS Authors 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 16 package jwt 17 18 import ( 19 "bytes" 20 "errors" 21 "fmt" 22 "regexp" 23 "strings" 24 25 "github.com/nats-io/nkeys" 26 ) 27 28 // DecorateJWT returns a decorated JWT that describes the kind of JWT 29 func DecorateJWT(jwtString string) ([]byte, error) { 30 gc, err := DecodeGeneric(jwtString) 31 if err != nil { 32 return nil, err 33 } 34 return formatJwt(string(gc.Type), jwtString) 35 } 36 37 func formatJwt(kind string, jwtString string) ([]byte, error) { 38 templ := `-----BEGIN NATS %s JWT----- 39 %s 40 ------END NATS %s JWT------ 41 42 ` 43 w := bytes.NewBuffer(nil) 44 kind = strings.ToUpper(kind) 45 _, err := fmt.Fprintf(w, templ, kind, jwtString, kind) 46 if err != nil { 47 return nil, err 48 } 49 return w.Bytes(), nil 50 } 51 52 // DecorateSeed takes a seed and returns a string that wraps 53 // the seed in the form: 54 // 55 // ************************* IMPORTANT ************************* 56 // NKEY Seed printed below can be used sign and prove identity. 57 // NKEYs are sensitive and should be treated as secrets. 58 // 59 // -----BEGIN USER NKEY SEED----- 60 // SUAIO3FHUX5PNV2LQIIP7TZ3N4L7TX3W53MQGEIVYFIGA635OZCKEYHFLM 61 // ------END USER NKEY SEED------ 62 func DecorateSeed(seed []byte) ([]byte, error) { 63 w := bytes.NewBuffer(nil) 64 ts := bytes.TrimSpace(seed) 65 pre := string(ts[0:2]) 66 kind := "" 67 switch pre { 68 case "SU": 69 kind = "USER" 70 case "SA": 71 kind = "ACCOUNT" 72 case "SO": 73 kind = "OPERATOR" 74 default: 75 return nil, errors.New("seed is not an operator, account or user seed") 76 } 77 header := `************************* IMPORTANT ************************* 78 NKEY Seed printed below can be used to sign and prove identity. 79 NKEYs are sensitive and should be treated as secrets. 80 81 -----BEGIN %s NKEY SEED----- 82 ` 83 _, err := fmt.Fprintf(w, header, kind) 84 if err != nil { 85 return nil, err 86 } 87 w.Write(ts) 88 89 footer := ` 90 ------END %s NKEY SEED------ 91 92 ************************************************************* 93 ` 94 _, err = fmt.Fprintf(w, footer, kind) 95 if err != nil { 96 return nil, err 97 } 98 return w.Bytes(), nil 99 } 100 101 var userConfigRE = regexp.MustCompile(`\s*(?:(?:[-]{3,}.*[-]{3,}\r?\n)([\w\-.=]+)(?:\r?\n[-]{3,}.*[-]{3,}(\r?\n|\z)))`) 102 103 // An user config file looks like this: 104 // -----BEGIN NATS USER JWT----- 105 // eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5... 106 // ------END NATS USER JWT------ 107 // 108 // ************************* IMPORTANT ************************* 109 // NKEY Seed printed below can be used sign and prove identity. 110 // NKEYs are sensitive and should be treated as secrets. 111 // 112 // -----BEGIN USER NKEY SEED----- 113 // SUAIO3FHUX5PNV2LQIIP7TZ3N4L7TX3W53MQGEIVYFIGA635OZCKEYHFLM 114 // ------END USER NKEY SEED------ 115 116 // FormatUserConfig returns a decorated file with a decorated JWT and decorated seed 117 func FormatUserConfig(jwtString string, seed []byte) ([]byte, error) { 118 gc, err := DecodeGeneric(jwtString) 119 if err != nil { 120 return nil, err 121 } 122 if gc.Type != UserClaim { 123 return nil, fmt.Errorf("%q cannot be serialized as a user config", string(gc.Type)) 124 } 125 126 w := bytes.NewBuffer(nil) 127 128 jd, err := formatJwt(string(gc.Type), jwtString) 129 if err != nil { 130 return nil, err 131 } 132 _, err = w.Write(jd) 133 if err != nil { 134 return nil, err 135 } 136 if !bytes.HasPrefix(bytes.TrimSpace(seed), []byte("SU")) { 137 return nil, fmt.Errorf("nkey seed is not an user seed") 138 } 139 140 d, err := DecorateSeed(seed) 141 if err != nil { 142 return nil, err 143 } 144 _, err = w.Write(d) 145 if err != nil { 146 return nil, err 147 } 148 149 return w.Bytes(), nil 150 } 151 152 // ParseDecoratedJWT takes a creds file and returns the JWT portion. 153 func ParseDecoratedJWT(contents []byte) (string, error) { 154 items := userConfigRE.FindAllSubmatch(contents, -1) 155 if len(items) == 0 { 156 return string(contents), nil 157 } 158 // First result should be the user JWT. 159 // We copy here so that if the file contained a seed file too we wipe appropriately. 160 raw := items[0][1] 161 tmp := make([]byte, len(raw)) 162 copy(tmp, raw) 163 return string(tmp), nil 164 } 165 166 // ParseDecoratedNKey takes a creds file, finds the NKey portion and creates a 167 // key pair from it. 168 func ParseDecoratedNKey(contents []byte) (nkeys.KeyPair, error) { 169 var seed []byte 170 171 items := userConfigRE.FindAllSubmatch(contents, -1) 172 if len(items) > 1 { 173 seed = items[1][1] 174 } else { 175 lines := bytes.Split(contents, []byte("\n")) 176 for _, line := range lines { 177 if bytes.HasPrefix(bytes.TrimSpace(line), []byte("SO")) || 178 bytes.HasPrefix(bytes.TrimSpace(line), []byte("SA")) || 179 bytes.HasPrefix(bytes.TrimSpace(line), []byte("SU")) { 180 seed = line 181 break 182 } 183 } 184 } 185 if seed == nil { 186 return nil, errors.New("no nkey seed found") 187 } 188 if !bytes.HasPrefix(seed, []byte("SO")) && 189 !bytes.HasPrefix(seed, []byte("SA")) && 190 !bytes.HasPrefix(seed, []byte("SU")) { 191 return nil, errors.New("doesn't contain a seed nkey") 192 } 193 kp, err := nkeys.FromSeed(seed) 194 if err != nil { 195 return nil, err 196 } 197 return kp, nil 198 } 199 200 // ParseDecoratedUserNKey takes a creds file, finds the NKey portion and creates a 201 // key pair from it. Similar to ParseDecoratedNKey but fails for non-user keys. 202 func ParseDecoratedUserNKey(contents []byte) (nkeys.KeyPair, error) { 203 nk, err := ParseDecoratedNKey(contents) 204 if err != nil { 205 return nil, err 206 } 207 seed, err := nk.Seed() 208 if err != nil { 209 return nil, err 210 } 211 if !bytes.HasPrefix(seed, []byte("SU")) { 212 return nil, errors.New("doesn't contain an user seed nkey") 213 } 214 kp, err := nkeys.FromSeed(seed) 215 if err != nil { 216 return nil, err 217 } 218 return kp, nil 219 }