github.com/gramework/gramework@v1.8.1-0.20231027140105-82555c9057f5/grypto/internal/mcf/mcf.go (about)

     1  package mcf
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/subtle"
     6  	"encoding/base64"
     7  	"errors"
     8  )
     9  
    10  const Divider = '$'
    11  
    12  var (
    13  	ErrorInvalidDecodeInput = errors.New("gramework/grypto: invalid mcf input")
    14  
    15  	splitDivider = []byte{Divider}
    16  )
    17  
    18  // Encode encodes given providerName, params, salt and key into Modular Crypt Format.
    19  func Encode(providerName []byte, params string, salt []byte, key []byte) (res []byte) {
    20  	salt64 := encodeBase64(salt)
    21  	key64 := encodeBase64(key)
    22  	// final res len = len(providerName) + len(params) + len(salt) + len(key) + 4, because we need 4 dividers
    23  	res = append(res, Divider)
    24  	res = append(res, providerName...)
    25  	res = append(res, Divider)
    26  	res = append(res, params...)
    27  	res = append(res, Divider)
    28  	res = append(res, salt64...)
    29  	res = append(res, Divider)
    30  	res = append(res, key64...)
    31  	return res
    32  }
    33  
    34  // encodeBase64 encodes the input bytes into standard base64.
    35  func encodeBase64(in []byte) (out []byte) {
    36  	enc := base64.StdEncoding
    37  	out = make([]byte, enc.EncodedLen(len(in)))
    38  	enc.Encode(out, in)
    39  	return out
    40  }
    41  
    42  func decodeBase64(in []byte) (out []byte, err error) {
    43  	enc := base64.StdEncoding
    44  	out = make([]byte, enc.DecodedLen(len(in)))
    45  	n, err := enc.Decode(out, in)
    46  	return out[:n], err
    47  }
    48  
    49  // Decode given MCF if it contains information about expected pw type
    50  func Decode(mcf []byte, expectedPWType []byte) (providerName []byte, params string, salt []byte, key []byte, err error) {
    51  	if len(mcf) <= 1 || mcf[0] != Divider {
    52  		err = ErrorInvalidDecodeInput
    53  		return
    54  	}
    55  
    56  	parts := bytes.Split(mcf[1:], splitDivider)
    57  
    58  	if len(parts) != 4 {
    59  		err = ErrorInvalidDecodeInput
    60  		return
    61  	}
    62  
    63  	if subtle.ConstantTimeCompare(parts[0], expectedPWType) == 0 {
    64  		err = ErrorInvalidDecodeInput
    65  		return
    66  	}
    67  
    68  	params = string(parts[1])
    69  
    70  	saltDecoded, err := decodeBase64(parts[2])
    71  	if err != nil {
    72  		err = ErrorInvalidDecodeInput
    73  		return
    74  	}
    75  
    76  	salt = saltDecoded
    77  
    78  	keyDecoded, err := decodeBase64(parts[3])
    79  	if err != nil {
    80  		err = ErrorInvalidDecodeInput
    81  		return
    82  	}
    83  	key = keyDecoded
    84  	return
    85  }