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 }