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  }