go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/server/loginsessions/internal/crypto.go (about) 1 // Copyright 2022 The LUCI Authors. 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 internal 16 17 import ( 18 "context" 19 "crypto/rand" 20 "fmt" 21 22 "google.golang.org/protobuf/proto" 23 24 "go.chromium.org/luci/server/loginsessions/internal/statepb" 25 "go.chromium.org/luci/server/secrets" 26 ) 27 28 const aeadContextState = "loginsessions:state" 29 30 // RandomBlob generates a completely random byte string of given length. 31 func RandomBlob(bytes int) []byte { 32 blob := make([]byte, bytes) 33 if _, err := rand.Read(blob); err != nil { 34 panic(fmt.Sprintf("failed to generate random string: %s", err)) 35 } 36 return blob 37 } 38 39 // RandomAlphaNum generates a random alphanumeric string of given length. 40 // 41 // Its entropy is ~6*size random bits. 42 func RandomAlphaNum(size int) string { 43 // Note: there are exactly 64 symbols here with two extra '0' to stay in the 44 // alphanumeric range. 45 const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678900" 46 blob := RandomBlob(size) 47 for i := range blob { 48 blob[i] = alphabet[blob[i]&63] 49 } 50 return string(blob) 51 } 52 53 // EncryptState serializes, encrypts and base64-encodes OpenIDState. 54 func EncryptState(ctx context.Context, msg *statepb.OpenIDState) (string, error) { 55 plain, err := proto.Marshal(msg) 56 if err != nil { 57 return "", err 58 } 59 return secrets.URLSafeEncrypt(ctx, plain, []byte(aeadContextState)) 60 } 61 62 // DecryptState is the reverse of EncryptState. 63 func DecryptState(ctx context.Context, enc string) (*statepb.OpenIDState, error) { 64 plain, err := secrets.URLSafeDecrypt(ctx, enc, []byte(aeadContextState)) 65 if err != nil { 66 return nil, err 67 } 68 var msg statepb.OpenIDState 69 if err := proto.Unmarshal(plain, &msg); err != nil { 70 return nil, err 71 } 72 return &msg, nil 73 }