Skip to content

Commit 1ad98cb

Browse files
committed
Merge pull request #30 from 420neshot/larky_AES_CCM_GCM
New larky sample AES CCM-n-GCM
2 parents bd8d0bd + 5ff5e36 commit 1ad98cb

File tree

7 files changed

+255
-10
lines changed

7 files changed

+255
-10
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# AES CCM and GCM sample of encryption in Larky
2+
3+
This sample includes:
4+
1. Python script that generates an ectrypted message of an input data;
5+
2. Larky test `.star` file that generates the same result of the same input data;
6+
3. Ready-for-use YAML (Inbound Route) that produces the same result as previous scripts.
7+
8+
## Testing part:
9+
10+
#### 1. Python:
11+
12+
Python script includes all hard-coded inside. As a result, it prints the encrypted message:
13+
```
14+
$ python python_sample.py
15+
16+
>>> Encrypted GCM: YXNkNDU2ZmdoMDEyql2O8UB53Ux94KWoIaIDarnm+6xKaXL2FNfHl5XOCtbv590W5x6ISEI3
17+
18+
>>> GCM decrypted: b'{"card_number":"4111111111111111"}'
19+
20+
>>> Encrypted CCM: YXNkNDU2ZmdoMDEysNf+zbjlTcFG/q1TPyuLIJwELXp6pSwIx1bwpV8Zw92S6G2FpI3OpWEJ
21+
22+
>>> CCM decrypted: b'{"card_number":"4111111111111111"}'
23+
24+
```
25+
26+
#### 2. Larky test:
27+
28+
To be able to run Larky locally, you'll need to setup your local environment:
29+
https://www.verygoodsecurity.com/docs/larky/test-larky-locally
30+
31+
Example of run:
32+
33+
<img width="1364" alt="image" src="https://user-images.githubusercontent.com/78090218/189484115-04cfb575-f39c-4e55-9f9e-1d0095650e63.png">
34+
35+
#### 3. YAML file:
36+
37+
Upload the YAML to your vault and run:
38+
```
39+
curl https://tntbmt67sc7.sandbox.verygoodproxy.com/post -k \
40+
-H "Content-type: application/json" \
41+
-d '{"card_number":"4111111111111111"}'
42+
```
43+
44+
Example of response:
45+
```
46+
"json": {
47+
"CCM": "YXNkNDU2ZmdoMDEysNf+zbjlTcFG/q1TPyuLIJwELXp6pSwIx1bwpV8Zw92S6G2FpI3OpWEJ",
48+
"CCM_decrypted": {
49+
"card_number": "4111111111111111"
50+
},
51+
"GCM": "YXNkNDU2ZmdoMDEyql2O8UB53Ux94KWoIaIDarnm+6xKaXL2FNfHl5XOCtbv590W5x6ISEI3",
52+
"GCM_decrypted": {
53+
"card_number": "4111111111111111"
54+
}
55+
```
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import builtins
2+
import json
3+
from requests.auth import HTTPProxyAuth
4+
import base64
5+
from Crypto.Cipher import AES
6+
7+
TAG_LENGTH = 8
8+
9+
session_key = b'vbfhg768ghvbfhg768ghvbfhg768gh12'
10+
nonce = b'asd456fgh012'
11+
12+
body = b'{"card_number":"4111111111111111"}'
13+
14+
cipher_aes = AES.new(session_key, AES.MODE_GCM, nonce=nonce, mac_len=TAG_LENGTH)
15+
ciphertext, tag = cipher_aes.encrypt_and_digest(body)
16+
ciphertext_tag = b"".join([cipher_aes.nonce, ciphertext, tag])
17+
result_GCM = base64.b64encode(ciphertext_tag).decode('utf-8')
18+
print('\n>>> Encrypted GCM: ', result_GCM)
19+
20+
#decrypt GCM:
21+
cipher = AES.new(session_key, AES.MODE_GCM, nonce=nonce)
22+
plaintext = cipher.decrypt(ciphertext)
23+
print("\n>>> GCM decrypted: " + str(plaintext))
24+
25+
cipher_aes = AES.new(session_key, AES.MODE_CCM, nonce=nonce, mac_len=TAG_LENGTH)
26+
ciphertext, tag = cipher_aes.encrypt_and_digest(body)
27+
ciphertext_tag = b"".join([cipher_aes.nonce, ciphertext, tag])
28+
result_CCM = base64.b64encode(ciphertext_tag).decode('utf-8')
29+
print('\n>>> Encrypted CCM: ', result_CCM)
30+
31+
#decrypt CCM:
32+
cipher = AES.new(session_key, AES.MODE_CCM, nonce=nonce)
33+
plaintext = cipher.decrypt(ciphertext)
34+
print("\n>>> CCM decrypted: " + str(plaintext) + '\n')
35+
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
load("@stdlib//unittest", "unittest")
2+
load("@vendor//asserts", "asserts")
3+
load("@vgs//http/request", "VGSHttpRequest")
4+
load("@stdlib//json", json="json")
5+
load("@stdlib//builtins", builtins="builtins")
6+
load("@vendor//Crypto/Cipher/AES", AES="AES")
7+
load("@stdlib//base64", base64="base64")
8+
9+
10+
def process(input, ctx):
11+
TAG_LENGTH = 8
12+
session_key = b'vbfhg768ghvbfhg768ghvbfhg768gh12'
13+
nonce = b'asd456fgh012'
14+
15+
body_str = str(input.body)
16+
body = json.loads(body_str)
17+
18+
# GCM:
19+
cipher_aes = AES.new(session_key, AES.MODE_GCM, nonce=nonce, mac_len=TAG_LENGTH)
20+
ciphertext, tag = cipher_aes.encrypt_and_digest(builtins.bytes(body_str, 'utf-8'))
21+
ciphertext_tag = b"".join([cipher_aes.nonce, ciphertext, tag])
22+
ciphertext_b64_GCM = base64.b64encode(ciphertext_tag).decode('utf-8')
23+
# decrypt:
24+
cipher = AES.new(session_key, AES.MODE_GCM, nonce=nonce)
25+
plaintext = cipher.decrypt(ciphertext)
26+
body['GCM'] = ciphertext_b64_GCM
27+
body['GCM_decrypted'] = json.loads(str(plaintext))
28+
29+
# CCM:
30+
cipher_aes = AES.new(session_key, AES.MODE_CCM, nonce=nonce, mac_len=TAG_LENGTH)
31+
ciphertext, tag = cipher_aes.encrypt_and_digest(builtins.bytes(body_str, 'utf-8'))
32+
ciphertext_tag = b"".join([cipher_aes.nonce, ciphertext, tag])
33+
ciphertext_b64_CCM = base64.b64encode(ciphertext_tag).decode('utf-8')
34+
# decrypt:
35+
cipher = AES.new(session_key, AES.MODE_CCM, nonce=nonce)
36+
plaintext = cipher.decrypt(ciphertext)
37+
body['CCM'] = ciphertext_b64_CCM
38+
body['CCM_decrypted'] = json.loads(str(plaintext))
39+
40+
body.pop('card_number')
41+
input.body = builtins.bytes(json.dumps(body))
42+
return input
43+
44+
45+
def test_process():
46+
headers = {}
47+
body = b'{"card_number":"4111111111111111"}'
48+
input = VGSHttpRequest("https://test.com", data=body, headers=headers, method='POST')
49+
response = process(input, None)
50+
expected_body = b'{"CCM":"YXNkNDU2ZmdoMDEysNf+zbjlTcFG/q1TPyuLIJwELXp6pSwIx1bwpV8Zw92S6G2FpI3OpWEJ","CCM_decrypted":{"card_number":"4111111111111111"},"GCM":"YXNkNDU2ZmdoMDEyql2O8UB53Ux94KWoIaIDarnm+6xKaXL2FNfHl5XOCtbv590W5x6ISEI3","GCM_decrypted":{"card_number":"4111111111111111"}}'
51+
print(response.body)
52+
print(expected_body)
53+
asserts.assert_that(response.body).is_equal_to(expected_body)
54+
55+
56+
def _testsuite():
57+
_suite = unittest.TestSuite()
58+
_suite.addTest(unittest.FunctionTestCase(test_process))
59+
return _suite
60+
61+
_runner = unittest.TextTestRunner()
62+
_runner.run(_testsuite())
63+
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
data:
2+
- attributes:
3+
created_at: '2021-04-26T09:55:37'
4+
destination_override_endpoint: 'https://echo.apps.verygood.systems'
5+
entries:
6+
- classifiers: {}
7+
config:
8+
condition: AND
9+
rules:
10+
- condition: null
11+
expression:
12+
field: PathInfo
13+
operator: matches
14+
type: string
15+
values:
16+
- /post
17+
- condition: null
18+
expression:
19+
field: ContentType
20+
operator: equals
21+
type: string
22+
values:
23+
- application/json
24+
id: 3e4ca047-23e2-4397-9a67-d673cedf4cc8
25+
id_selector: null
26+
operation: REDACT
27+
operations:
28+
- name: github.com/verygoodsecurity/common/compute/larky/http/Process
29+
parameters:
30+
script: |-
31+
load("@stdlib//base64", base64="base64")
32+
load("@stdlib//builtins", builtins="builtins")
33+
load("@stdlib//json", json="json")
34+
load("@stdlib//larky", larky="larky")
35+
load("@vendor//Crypto/Cipher/AES", AES="AES")
36+
load("@vgs//vault", "vault")
37+
38+
def process(input, ctx):
39+
TAG_LENGTH = 8
40+
session_key = b'vbfhg768ghvbfhg768ghvbfhg768gh12'
41+
nonce = b'asd456fgh012'
42+
43+
body_str = str(input.body)
44+
body = json.loads(body_str)
45+
46+
# GCM:
47+
cipher_aes = AES.new(session_key, AES.MODE_GCM, nonce=nonce, mac_len=TAG_LENGTH)
48+
ciphertext, tag = cipher_aes.encrypt_and_digest(builtins.bytes(body_str, 'utf-8'))
49+
ciphertext_tag = b"".join([cipher_aes.nonce, ciphertext, tag])
50+
ciphertext_b64_GCM = base64.b64encode(ciphertext_tag).decode('utf-8')
51+
# decrypt:
52+
cipher = AES.new(session_key, AES.MODE_GCM, nonce=nonce)
53+
plaintext = cipher.decrypt(ciphertext)
54+
body['GCM'] = ciphertext_b64_GCM
55+
body['GCM_decrypted'] = json.loads(str(plaintext))
56+
57+
# CCM:
58+
cipher_aes = AES.new(session_key, AES.MODE_CCM, nonce=nonce, mac_len=TAG_LENGTH)
59+
ciphertext, tag = cipher_aes.encrypt_and_digest(builtins.bytes(body_str, 'utf-8'))
60+
ciphertext_tag = b"".join([cipher_aes.nonce, ciphertext, tag])
61+
ciphertext_b64_CCM = base64.b64encode(ciphertext_tag).decode('utf-8')
62+
# decrypt:
63+
cipher = AES.new(session_key, AES.MODE_CCM, nonce=nonce)
64+
plaintext = cipher.decrypt(ciphertext)
65+
body['CCM'] = ciphertext_b64_CCM
66+
body['CCM_decrypted'] = json.loads(str(plaintext))
67+
68+
body.pop('card_number')
69+
input.body = builtins.bytes(json.dumps(body))
70+
return input
71+
phase: REQUEST
72+
public_token_generator: UUID
73+
targets:
74+
- body
75+
token_manager: PERSISTENT
76+
transformer: JSON_PATH
77+
transformer_config:
78+
- $.email
79+
transformer_config_map: null
80+
host_endpoint: (.*)\.verygoodproxy\.com
81+
id: c891d346-0e1b-49b8-99ac-0754d34f14f2
82+
ordinal: null
83+
port: 80
84+
protocol: http
85+
source_endpoint: '*'
86+
tags:
87+
name: echo.apps.verygood.systems-steel-blue-parallelogram
88+
source: RouteContainer
89+
updated_at: '2021-05-07T11:45:32'
90+
id: c891d346-0e1b-49b8-99ac-0754d34f14f2
91+
type: rule_chain
92+
version: 1

integrations/larky/crypto/AES_CTR/inbound-AES-CTR.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ data:
4343
load("@vendor//Crypto/Hash/SHA256", SHA256="SHA256")
4444
4545
def process(input, ctx):
46-
API_SECRET = '5243A220F218587596BC3A9E3C47A4E7B4FF98F7136856F174940D22071A35DD'
47-
secondKey = 'MDIzM1OCNkMzRDTJGNDIc0MM1MkEz0NTA0E3Rjk5NDc='
46+
API_SECRET = '...'
47+
secondKey = '...'
4848
4949
body_str = str(input.body)
5050
body = json.loads(body_str)
@@ -58,7 +58,7 @@ data:
5858
5959
# Aes CTR encryption part
6060
to_encrypt = query + '&alg=SHA256&hashed_query='+hashValue.hexdigest()
61-
ivv = 'seBheNNYzEURuyQR' # made it static to compare with Python
61+
ivv = '...' # made it static to compare with Python
6262
secondKey = base64.b64decode(secondKey)
6363
h = binascii.hexlify(bytes(ivv,encoding='utf-8'))
6464
ctr = Counter.new(128, initial_value=int(str(h), 16))
@@ -67,7 +67,7 @@ data:
6767
encString = base64.urlsafe_b64encode(chipertext).decode()
6868
payload = '&encryptedData=' + encString + '&iv=' + ivv
6969
70-
# final result to be send to dev.finexusgroup.com
70+
# final result to be sent to upstream
7171
enc = bytes(payload, encoding='utf-8')
7272
final = base64.b64encode(enc).decode()
7373

integrations/larky/crypto/AES_CTR/python_sample.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
import binascii
88
from base64 import b64encode
99
import string
10-
API_SECRET = '5243A220F218587596BC3A9E3C47A4E7B4FF98F7136856F174940D22071A35DD'
10+
API_SECRET = '...'
1111
query = 'MID=4111111111111111&exp_year=2030&exp_month=09&cvv=123&name=CoreyTaylor&amount=100'
12-
secondKey = 'MDIzM1OCNkMzRDTJGNDIc0MM1MkEz0NTA0E3Rjk5NDc='
12+
secondKey = '...='
1313

1414
"""hashpart"""
1515
secret = API_SECRET.encode()
@@ -19,7 +19,7 @@
1919
"""Aes CTR encryption part"""
2020
to_encrypt = query + '&alg=SHA256&hashed_query=' + hashValue.hexdigest()
2121
secondKey = base64.b64decode(secondKey)
22-
ivv = 'seBheNNYzEURuyQR'
22+
ivv = '...'
2323
h = binascii.hexlify(bytes(ivv,encoding='utf-8'))
2424
ctr = Counter.new(128, initial_value=int(h, 16))
2525
chiper = AES.new(secondKey, AES.MODE_CTR, counter=ctr)

integrations/larky/crypto/AES_CTR/test_aes_ctr.star

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ load("@vendor//Crypto/Hash/SHA256", SHA256="SHA256")
1616

1717

1818
def process(input, ctx):
19-
API_SECRET = '5243A220F218587596BC3A9E3C47A4E7B4FF98F7136856F174940D22071A35DD'
20-
secondKey = 'MDIzM1OCNkMzRDTJGNDIc0MM1MkEz0NTA0E3Rjk5NDc='
19+
API_SECRET = '...'
20+
secondKey = '...'
2121

2222
body_str = str(input.body)
2323
body = json.loads(body_str)
@@ -31,7 +31,7 @@ def process(input, ctx):
3131

3232
"""Aes CTR encryption part"""
3333
to_encrypt = query + '&alg=SHA256&hashed_query='+hashValue.hexdigest()
34-
ivv = 'seBheNNYzEURuyQR' # made it static to compare with Python
34+
ivv = '...' # made it static to compare with Python
3535
secondKey = base64.b64decode(secondKey)
3636
h = binascii.hexlify(bytes(ivv,encoding='utf-8'))
3737
ctr = Counter.new(128, initial_value=int(str(h), 16))

0 commit comments

Comments
 (0)