github.com/cs3org/reva/v2@v2.27.7/pkg/siteacc/credentials/crypto/crypto.go (about) 1 // Copyright 2018-2020 CERN 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 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package crypto 20 21 import ( 22 "crypto/aes" 23 "crypto/cipher" 24 "crypto/rand" 25 "encoding/base64" 26 "io" 27 28 "github.com/pkg/errors" 29 ) 30 31 const ( 32 passphraseLength = 32 33 ) 34 35 // EncodeString encodes a string using AES and returns the base64-encoded result. 36 func EncodeString(s string, passphrase string) (string, error) { 37 if len(s) == 0 || len(passphrase) == 0 { 38 return "", nil 39 } 40 passphrase = normalizePassphrase(passphrase) 41 42 gcm, err := createGCM([]byte(passphrase)) 43 if err != nil { 44 return "", err 45 } 46 47 nonce := make([]byte, gcm.NonceSize()) 48 if _, err = io.ReadFull(rand.Reader, nonce); err != nil { 49 return "", errors.Wrap(err, "unable to generate nonce") 50 } 51 encryptedData := gcm.Seal(nonce, nonce, []byte(s), nil) 52 return base64.StdEncoding.EncodeToString(encryptedData), nil 53 } 54 55 // DecodeString decodes a base64-encoded string encoded with AES. 56 func DecodeString(s string, passphrase string) (string, error) { 57 if len(s) == 0 || len(passphrase) == 0 { 58 return "", nil 59 } 60 data, _ := base64.StdEncoding.DecodeString(s) 61 passphrase = normalizePassphrase(passphrase) 62 63 gcm, err := createGCM([]byte(passphrase)) 64 if err != nil { 65 return "", err 66 } 67 68 nonceSize := gcm.NonceSize() 69 if len(s) < nonceSize { 70 return "", errors.Errorf("input string length too short") 71 } 72 nonce, data := data[:nonceSize], data[nonceSize:] 73 plain, err := gcm.Open(nil, nonce, data, nil) 74 if err != nil { 75 return "", errors.Wrap(err, "unable to decode string") 76 } 77 return string(plain), nil 78 } 79 80 func createGCM(passphrase []byte) (cipher.AEAD, error) { 81 c, err := aes.NewCipher(passphrase) 82 if err != nil { 83 return nil, errors.Wrap(err, "unable to generate cipher") 84 } 85 gcm, err := cipher.NewGCM(c) 86 if err != nil { 87 return nil, errors.Wrap(err, "unable to generate GCM") 88 } 89 return gcm, nil 90 } 91 92 func normalizePassphrase(passphrase string) string { 93 if len(passphrase) > passphraseLength { 94 passphrase = passphrase[:passphraseLength] 95 } else if len(passphrase) < passphraseLength { 96 for i := len(passphrase); i < passphraseLength; i++ { 97 passphrase += "#" 98 } 99 } 100 return passphrase 101 }