github.com/aldelo/common@v1.5.1/wrapper/apc/paydata.go (about)

     1  package apc
     2  
     3  /*
     4   * Copyright 2020-2023 Aldelo, LP
     5   *
     6   * Licensed under the Apache License, Version 2.0 (the "License");
     7   * you may not use this file except in compliance with the License.
     8   * You may obtain a copy of the License at
     9   *
    10   *     http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   * Unless required by applicable law or agreed to in writing, software
    13   * distributed under the License is distributed on an "AS IS" BASIS,
    14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15   * See the License for the specific language governing permissions and
    16   * limitations under the License.
    17   */
    18  
    19  // =================================================================================================================
    20  // AWS CREDENTIAL:
    21  //		use $> aws configure (to set aws access key and secret to target machine)
    22  //		Store AWS Access ID and Secret Key into Default Profile Using '$ aws configure' cli
    23  //
    24  // To Install & Setup AWS CLI on Host:
    25  //		1) https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-linux.html
    26  //				On Ubuntu, if host does not have zip and unzip:
    27  //					$> sudo apt install zip
    28  //					$> sudo apt install unzip
    29  //				On Ubuntu, to install AWS CLI v2:
    30  //					$> curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
    31  //					$> unzip awscliv2.zip
    32  //					$> sudo ./aws/install
    33  //		2) $> aws configure set region awsRegionName --profile default
    34  // 		3) $> aws configure
    35  //				follow prompts to enter Access ID and Secret Key
    36  //
    37  // AWS Region Name Reference:
    38  //		us-west-2, us-east-1, ap-northeast-1, etc
    39  //		See: https://docs.aws.amazon.com/general/latest/gr/rande.html
    40  // =================================================================================================================
    41  
    42  import (
    43  	"context"
    44  	"errors"
    45  	awshttp2 "github.com/aldelo/common/wrapper/aws"
    46  	"github.com/aldelo/common/wrapper/aws/awsregion"
    47  	"github.com/aldelo/common/wrapper/xray"
    48  	"github.com/aws/aws-sdk-go/aws"
    49  	"github.com/aws/aws-sdk-go/aws/session"
    50  	pycryptoData "github.com/aws/aws-sdk-go/service/paymentcryptographydata"
    51  	awsxray "github.com/aws/aws-xray-sdk-go/xray"
    52  	"net/http"
    53  )
    54  
    55  // ================================================================================================================
    56  // STRUCTS
    57  // ================================================================================================================
    58  
    59  // PaymentCryptoData struct encapsulates the AWS PaymentCryptography Data access functionality
    60  type PaymentCryptoData struct {
    61  	// define the AWS region that PaymentCryptography is located at
    62  	AwsRegion awsregion.AWSRegion
    63  
    64  	// custom http2 client options
    65  	HttpOptions *awshttp2.HttpClientSettings
    66  
    67  	// define PaymentCryptography Data key or key alias
    68  	KeyArn string
    69  
    70  	// store aws session object
    71  	sess *session.Session
    72  
    73  	// store PaymentCryptography Data client object
    74  	pyDataClient *pycryptoData.PaymentCryptographyData
    75  
    76  	_parentSegment *xray.XRayParentSegment
    77  }
    78  
    79  // ================================================================================================================
    80  // STRUCTS FUNCTIONS
    81  // ================================================================================================================
    82  
    83  // ----------------------------------------------------------------------------------------------------------------
    84  // utility functions
    85  // ----------------------------------------------------------------------------------------------------------------
    86  
    87  // Connect will establish a connection to the PaymentCryptography Data service
    88  func (k *PaymentCryptoData) Connect(parentSegment ...*xray.XRayParentSegment) (err error) {
    89  	if xray.XRayServiceOn() {
    90  		if len(parentSegment) > 0 {
    91  			k._parentSegment = parentSegment[0]
    92  		}
    93  
    94  		seg := xray.NewSegment("PaymentCryptoData-Connect", k._parentSegment)
    95  		defer seg.Close()
    96  		defer func() {
    97  			_ = seg.Seg.AddMetadata("KDS-AWS-Region", k.AwsRegion)
    98  
    99  			if err != nil {
   100  				_ = seg.Seg.AddError(err)
   101  			}
   102  		}()
   103  
   104  		err = k.connectInternal()
   105  
   106  		if err == nil {
   107  			awsxray.AWS(k.pyDataClient.Client)
   108  		}
   109  
   110  		return err
   111  	} else {
   112  		return k.connectInternal()
   113  	}
   114  }
   115  
   116  // Connect will establish a connection to the PaymentCryptography Data service
   117  func (k *PaymentCryptoData) connectInternal() error {
   118  	// clean up prior session reference
   119  	k.sess = nil
   120  
   121  	if !k.AwsRegion.Valid() || k.AwsRegion == awsregion.UNKNOWN {
   122  		return errors.New("Connect To PaymentCryptoData Failed: (AWS Session Error) " + "Region is Required")
   123  	}
   124  
   125  	// create custom http2 client if needed
   126  	var httpCli *http.Client
   127  	var httpErr error
   128  
   129  	if k.HttpOptions == nil {
   130  		k.HttpOptions = new(awshttp2.HttpClientSettings)
   131  	}
   132  
   133  	// use custom http2 client
   134  	h2 := &awshttp2.AwsHttp2Client{
   135  		Options: k.HttpOptions,
   136  	}
   137  
   138  	if httpCli, httpErr = h2.NewHttp2Client(); httpErr != nil {
   139  		return errors.New("Connect to PaymentCryptoData Failed: (AWS Session Error) " + "Create Custom Http2 Client Errored = " + httpErr.Error())
   140  	}
   141  
   142  	// establish aws session connection and keep session object in struct
   143  	if sess, err := session.NewSession(
   144  		&aws.Config{
   145  			Region:     aws.String(k.AwsRegion.Key()),
   146  			HTTPClient: httpCli,
   147  		}); err != nil {
   148  		// aws session error
   149  		return errors.New("Connect To PaymentCryptoData Failed: (AWS Session Error) " + err.Error())
   150  	} else {
   151  		// aws session obtained
   152  		k.sess = sess
   153  
   154  		// create cached objects for shared use
   155  		k.pyDataClient = pycryptoData.New(k.sess)
   156  
   157  		if k.pyDataClient == nil {
   158  			return errors.New("Connect To PaymentCryptoData Client Failed: (New PaymentCryptography Client Connection) " + "Connection Object Nil")
   159  		}
   160  
   161  		// session stored to struct
   162  		return nil
   163  	}
   164  }
   165  
   166  // Disconnect will disjoin from aws session by clearing it
   167  func (k *PaymentCryptoData) Disconnect() {
   168  	k.pyDataClient = nil
   169  	k.sess = nil
   170  }
   171  
   172  // UpdateParentSegment updates this struct's xray parent segment, if no parent segment, set nil
   173  func (k *PaymentCryptoData) UpdateParentSegment(parentSegment *xray.XRayParentSegment) {
   174  	k._parentSegment = parentSegment
   175  }
   176  
   177  func awsNilString(input string) *string {
   178  	if input == "" {
   179  		return nil
   180  	}
   181  	return aws.String(input)
   182  }
   183  
   184  // ----------------------------------------------------------------------------------------------------------------
   185  // PaymentCryptography Data encrypt/decrypt via RSA functions
   186  // ----------------------------------------------------------------------------------------------------------------
   187  
   188  func (k *PaymentCryptoData) encrypt(plainText string, encryptionAttributes *pycryptoData.EncryptionDecryptionAttributes) (cipherText string, err error) {
   189  	var segCtx context.Context
   190  	segCtx = nil
   191  
   192  	seg := xray.NewSegmentNullable("PaymentCryptoData-encrypt", k._parentSegment)
   193  	if seg != nil {
   194  		segCtx = seg.Ctx
   195  
   196  		defer seg.Close()
   197  		defer func() {
   198  
   199  			if err != nil {
   200  				_ = seg.Seg.AddError(err)
   201  			}
   202  		}()
   203  	}
   204  
   205  	// validate
   206  	if k.pyDataClient == nil {
   207  		err = errors.New("encrypt with PaymentCryptoData key Failed: " + "PaymentCryptoData Client is Required")
   208  		return "", err
   209  	}
   210  
   211  	if len(k.KeyArn) <= 0 {
   212  		err = errors.New("encrypt with PaymentCryptoData key Failed: " + "PaymentCryptoData KeyArn is Required")
   213  		return "", err
   214  	}
   215  
   216  	// encrypt asymmetric
   217  	var encryptedOutput *pycryptoData.EncryptDataOutput
   218  	var e error
   219  
   220  	encryptDataInput := &pycryptoData.EncryptDataInput{
   221  		EncryptionAttributes: encryptionAttributes,
   222  		KeyIdentifier:        aws.String(k.KeyArn),
   223  		PlainText:            aws.String(plainText),
   224  	}
   225  
   226  	if segCtx == nil {
   227  		encryptedOutput, e = k.pyDataClient.EncryptData(encryptDataInput)
   228  	} else {
   229  		encryptedOutput, e = k.pyDataClient.EncryptDataWithContext(segCtx, encryptDataInput)
   230  	}
   231  
   232  	if e != nil {
   233  		err = errors.New("encrypt with PaymentCryptoData key Failed: " + e.Error())
   234  		return "", err
   235  	}
   236  	if encryptedOutput != nil {
   237  		cipherText = aws.StringValue(encryptedOutput.CipherText)
   238  	}
   239  	return cipherText, nil
   240  }
   241  
   242  func (k *PaymentCryptoData) decrypt(cipherText string, decryptionAttributes *pycryptoData.EncryptionDecryptionAttributes) (plainText string, err error) {
   243  
   244  	var segCtx context.Context
   245  	segCtx = nil
   246  
   247  	seg := xray.NewSegmentNullable("PaymentCryptoData-decrypt", k._parentSegment)
   248  	if seg != nil {
   249  		segCtx = seg.Ctx
   250  
   251  		defer seg.Close()
   252  		defer func() {
   253  
   254  			if err != nil {
   255  				_ = seg.Seg.AddError(err)
   256  			}
   257  		}()
   258  	}
   259  
   260  	// validate
   261  	if k.pyDataClient == nil {
   262  		err = errors.New("decrypt with PaymentCryptoData Key Failed: " + "PaymentCryptoData Client is Required")
   263  		return "", err
   264  	}
   265  
   266  	if len(k.KeyArn) <= 0 {
   267  		err = errors.New("decrypt with PaymentCryptoData Key Failed: " + "PaymentCryptoData Key Name is Required")
   268  		return "", err
   269  	}
   270  
   271  	if len(cipherText) <= 0 {
   272  		err = errors.New("decrypt with PaymentCryptoData Key Failed: " + "Cipher Text is Required")
   273  		return "", err
   274  	}
   275  
   276  	//prepare input
   277  	var decryptedOutput *pycryptoData.DecryptDataOutput
   278  	var e error
   279  
   280  	decryptInput := &pycryptoData.DecryptDataInput{
   281  		CipherText:           aws.String(cipherText),
   282  		DecryptionAttributes: decryptionAttributes,
   283  		KeyIdentifier:        aws.String(k.KeyArn),
   284  	}
   285  
   286  	if segCtx == nil {
   287  		decryptedOutput, e = k.pyDataClient.DecryptData(decryptInput)
   288  	} else {
   289  		decryptedOutput, e = k.pyDataClient.DecryptDataWithContext(segCtx, decryptInput)
   290  	}
   291  
   292  	if e != nil {
   293  		err = errors.New("decrypt with PaymentCryptoData Key Failed: " + e.Error())
   294  		return "", err
   295  	}
   296  
   297  	if decryptedOutput != nil {
   298  		plainText = aws.StringValue(decryptedOutput.PlainText)
   299  	}
   300  
   301  	return plainText, nil
   302  }
   303  
   304  func (k *PaymentCryptoData) encryptRSA(plainText string, paddingType string) (cipherText string, err error) {
   305  	encryptionAttributes := &pycryptoData.EncryptionDecryptionAttributes{
   306  		Asymmetric: &pycryptoData.AsymmetricEncryptionAttributes{
   307  			PaddingType: awsNilString(paddingType),
   308  		},
   309  	}
   310  	return k.encrypt(plainText, encryptionAttributes)
   311  }
   312  
   313  func (k *PaymentCryptoData) decryptRSA(cipherText string, paddingType string) (plainText string, err error) {
   314  	decryptionAttributes := &pycryptoData.EncryptionDecryptionAttributes{
   315  		Asymmetric: &pycryptoData.AsymmetricEncryptionAttributes{
   316  			PaddingType: awsNilString(paddingType),
   317  		},
   318  	}
   319  	return k.decrypt(cipherText, decryptionAttributes)
   320  }
   321  
   322  // EncryptViaRSAPKCS1 the padding scheme is PKCS1, the plainText & cipherText is both hex encoded string
   323  func (k *PaymentCryptoData) EncryptViaRSAPKCS1(plainText string) (cipherText string, err error) {
   324  	return k.encryptRSA(plainText, pycryptoData.PaddingTypePkcs1)
   325  }
   326  
   327  // EncryptViaRSANone the padding scheme is None, the plainText & cipherText is both hex encoded string
   328  func (k *PaymentCryptoData) EncryptViaRSANone(plainText string) (cipherText string, err error) {
   329  	return k.encryptRSA(plainText, "")
   330  }
   331  
   332  // EncryptViaRSAOEAPSHA512 the padding scheme is OEAP SHA512, the plainText & cipherText is both hex encoded string
   333  func (k *PaymentCryptoData) EncryptViaRSAOEAPSHA512(plainText string) (cipherText string, err error) {
   334  	return k.encryptRSA(plainText, pycryptoData.PaddingTypeOaepSha512)
   335  }
   336  
   337  // EncryptViaRSAOEAPSHA256 the padding scheme is OEAP SHA256, the plainText & cipherText is both hex encoded string
   338  func (k *PaymentCryptoData) EncryptViaRSAOEAPSHA256(plainText string) (cipherText string, err error) {
   339  	return k.encryptRSA(plainText, pycryptoData.PaddingTypeOaepSha256)
   340  }
   341  
   342  // EncryptViaRSAOEAPSHA128 the padding scheme is OEAP SHA1, the plainText & cipherText is both hex encoded string
   343  func (k *PaymentCryptoData) EncryptViaRSAOEAPSHA128(plainText string) (cipherText string, err error) {
   344  	return k.encryptRSA(plainText, pycryptoData.PaddingTypeOaepSha1)
   345  }
   346  
   347  // DecryptViaRSAPKCS1 the padding scheme is PKCS1, the plainText & cipherText is both hex encoded string
   348  func (k *PaymentCryptoData) DecryptViaRSAPKCS1(cipherText string) (plainText string, err error) {
   349  	return k.decryptRSA(cipherText, pycryptoData.PaddingTypePkcs1)
   350  }
   351  
   352  // DecryptViaRSANone the padding scheme is None, the plainText & cipherText is both hex encoded string
   353  func (k *PaymentCryptoData) DecryptViaRSANone(cipherText string) (plainText string, err error) {
   354  	return k.decryptRSA(cipherText, "")
   355  }
   356  
   357  // DecryptViaRSAOEAPSHA512 the padding scheme is OEAP SHA512, the plainText & cipherText is both hex encoded string
   358  func (k *PaymentCryptoData) DecryptViaRSAOEAPSHA512(cipherText string) (plainText string, err error) {
   359  	return k.decryptRSA(cipherText, pycryptoData.PaddingTypeOaepSha512)
   360  }
   361  
   362  // DecryptViaRSAOEAPSHA256 the padding scheme is OEAP SHA256, the plainText & cipherText is both hex encoded string
   363  func (k *PaymentCryptoData) DecryptViaRSAOEAPSHA256(cipherText string) (plainText string, err error) {
   364  	return k.decryptRSA(cipherText, pycryptoData.PaddingTypeOaepSha256)
   365  }
   366  
   367  // DecryptViaRSAOEAPSHA128 the padding scheme is OEAP SHA1, the plainText & cipherText is both hex encoded string
   368  func (k *PaymentCryptoData) DecryptViaRSAOEAPSHA128(cipherText string) (plainText string, err error) {
   369  	return k.decryptRSA(cipherText, pycryptoData.PaddingTypeOaepSha1)
   370  }
   371  
   372  // ----------------------------------------------------------------------------------------------------------------
   373  // PaymentCryptography Data encrypt/decrypt via AES functions
   374  // ----------------------------------------------------------------------------------------------------------------
   375  
   376  func (k *PaymentCryptoData) encryptAES(plainText string, iv, mode string) (cipherText string, err error) {
   377  	encryptionAttributes := &pycryptoData.EncryptionDecryptionAttributes{
   378  		Symmetric: &pycryptoData.SymmetricEncryptionAttributes{
   379  			InitializationVector: awsNilString(iv),
   380  			Mode:                 aws.String(mode),
   381  			PaddingType:          nil,
   382  		},
   383  	}
   384  	return k.encrypt(plainText, encryptionAttributes)
   385  }
   386  
   387  func (k *PaymentCryptoData) decryptAES(cipherText string, iv, mode string) (plainText string, err error) {
   388  	decryptionAttributes := &pycryptoData.EncryptionDecryptionAttributes{
   389  		Symmetric: &pycryptoData.SymmetricEncryptionAttributes{
   390  			InitializationVector: awsNilString(iv),
   391  			Mode:                 aws.String(mode),
   392  			PaddingType:          nil,
   393  		},
   394  	}
   395  	return k.decrypt(cipherText, decryptionAttributes)
   396  }
   397  
   398  func (k *PaymentCryptoData) EncryptViaAesECB(plainText string) (cipherText string, err error) {
   399  	return k.encryptAES(plainText, "", pycryptoData.EncryptionModeEcb)
   400  }
   401  func (k *PaymentCryptoData) EncryptViaAesCBC(plainText, iv string) (cipherText string, err error) {
   402  	return k.encryptAES(plainText, iv, pycryptoData.EncryptionModeCbc)
   403  }
   404  func (k *PaymentCryptoData) EncryptViaAesCFB(plainText, iv string) (cipherText string, err error) {
   405  	return k.encryptAES(plainText, iv, pycryptoData.EncryptionModeCfb)
   406  }
   407  func (k *PaymentCryptoData) EncryptViaAesOFB(plainText, iv string) (cipherText string, err error) {
   408  	return k.encryptAES(plainText, iv, pycryptoData.EncryptionModeOfb)
   409  }
   410  
   411  func (k *PaymentCryptoData) DecryptViaAesECB(cipherText string) (plainText string, err error) {
   412  	return k.decryptAES(cipherText, "", pycryptoData.EncryptionModeEcb)
   413  }
   414  func (k *PaymentCryptoData) DecryptViaAesCBC(cipherText, iv string) (plainText string, err error) {
   415  	return k.decryptAES(cipherText, iv, pycryptoData.EncryptionModeCbc)
   416  }
   417  func (k *PaymentCryptoData) DecryptViaAesCFB(cipherText, iv string) (plainText string, err error) {
   418  	return k.decryptAES(cipherText, iv, pycryptoData.EncryptionModeCfb)
   419  }
   420  func (k *PaymentCryptoData) DecryptViaAesOFB(cipherText, iv string) (plainText string, err error) {
   421  	return k.decryptAES(cipherText, iv, pycryptoData.EncryptionModeOfb)
   422  }
   423  
   424  func (k *PaymentCryptoData) DecryptViaDUKPT(cipherText, ksn string) (plainText string, err error) {
   425  	decryptionAttributes := &pycryptoData.EncryptionDecryptionAttributes{
   426  		Dukpt: &pycryptoData.DukptEncryptionAttributes{
   427  			KeySerialNumber: aws.String(ksn),
   428  		},
   429  	}
   430  	return k.decrypt(cipherText, decryptionAttributes)
   431  }
   432  
   433  func (k *PaymentCryptoData) EncryptViaDUKPT(plainText, ksn string) (cipherText string, err error) {
   434  	decryptionAttributes := &pycryptoData.EncryptionDecryptionAttributes{
   435  		Dukpt: &pycryptoData.DukptEncryptionAttributes{
   436  			KeySerialNumber: aws.String(ksn),
   437  		},
   438  	}
   439  	return k.encrypt(plainText, decryptionAttributes)
   440  }