AES CTR mode encryption with HMACAES encryption in PHPAES encryption wrapperAES CTR mode using pycryptoAES...
Is it wise to focus on putting odd beats on left when playing double bass drums?
A poker game description that does not feel gimmicky
What is the meaning of "of trouble" in the following sentence?
Can I legally use front facing blue light in the UK?
Is this food a bread or a loaf?
How to move the player while also allowing forces to affect it
Is there a name of the flying bionic bird?
How could a lack of term limits lead to a "dictatorship?"
"My colleague's body is amazing"
Unbreakable Formation vs. Cry of the Carnarium
Copycat chess is back
Is "plugging out" electronic devices an American expression?
Manga about a female worker who got dragged into another world together with this high school girl and she was just told she's not needed anymore
How to make payment on the internet without leaving a money trail?
Are white and non-white police officers equally likely to kill black suspects?
Could a US political party gain complete control over the government by removing checks & balances?
COUNT(*) or MAX(id) - which is faster?
Information to fellow intern about hiring?
Why was the "bread communication" in the arena of Catching Fire left out in the movie?
What does it exactly mean if a random variable follows a distribution
What does 'script /dev/null' do?
Does it makes sense to buy a new cycle to learn riding?
extract characters between two commas?
Email Account under attack (really) - anything I can do?
AES CTR mode encryption with HMAC
AES encryption in PHPAES encryption wrapperAES CTR mode using pycryptoAES encryption classRecreating binary counter for arbitrary length arraysEncrypting a binary stream with RSA + AES in counter modeEncryption algorithm using CTR modeEncrypt and decrypt a message using AES-256 with GCM mode using Bouncy Castle C# libraryPyCrypto AES-CFB with SCrypt and HMACJava AES file encryption/decryption
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ margin-bottom:0;
}
$begingroup$
I am trying to implement AES CTR encryption mode with HMAC authentication for messages.
It's encrypting and decrypting fine as long as the key length is 64 bytes, since AES key and HMAC key are being derived from this key.
Questions
- Is it safe to append IV or nonce to the encrypted messages?
- Is it safe to append HMAC digest to append to the messages?
- Can you review it for best security coding practices?
I am using Pycryptodome
Code
import os
import zlib
from Crypto.Hash import HMAC
from Crypto.Hash import SHA256
from Crypto.Cipher import AES
from Crypto.Util import Counter
import zlib
def encrypt(full_key, plaintext):
if len(full_key) != 64:
raise Exception("FULL key length shall be equal to 64")
key = full_key[:len(full_key) //2]
# Use the last half as the HMAC key
hmac_key = full_key[len(full_key) // 2:]
if isinstance(plaintext, str):
plaintext = plaintext.encode()
compressed = zlib.compress(plaintext, 5)
print (f"compressed plaintext {compressed}")
# Choose a random, 16-byte IV.
iv = os.urandom(16)
# Convert the IV to a Python integer.
iv_int = int(binascii.hexlify(iv), 16)
# Create a new Counter object with IV = iv_int.
ctr = Counter.new(128, initial_value=iv_int)
# Create AES-CTR cipher.
aes = AES.new(key, AES.MODE_CTR, counter=ctr)
# Encrypt and return IV and ciphertext.
ciphertext = aes.encrypt(compressed)
hmac_obj = HMAC.new(hmac_key, compressed, SHA256)
mac = hmac_obj.digest()
return iv+ciphertext+mac
def decrypt(key, ciphertext):
# Initialize counter for decryption. iv should be the same as the output of
# encrypt().
if len(full_key) != 64:
raise Exception("FULL key length shall be equal to 64")
key = full_key[:len(full_key) //2]
# Use the last half as the HMAC key
hmac_key = full_key[len(full_key) // 2:]
mac_length = 32
iv_length = 16
iv = ciphertext[:16]
mac = ciphertext[-mac_length:]
_ciphertext = ciphertext[iv_length:-mac_length]
iv_int = int(iv.hex(), 16)
ctr = Counter.new(128, initial_value=iv_int)
# Create AES-CTR cipher.
aes = AES.new(key, AES.MODE_CTR, counter=ctr)
ciphertext = aes.decrypt(_ciphertext)
# Extract the MAC from the end of the file
hmac_obj = HMAC.new(hmac_key, ciphertext, SHA256)
computed_mac = hmac_obj.digest()
if computed_mac != mac:
raise Exception("Messege integrity violated")
plaintext= zlib.decompress(ciphertext)
# Decrypt and return the plaintext.
return plaintext
python python-3.x security authentication aes
New contributor
$endgroup$
add a comment |
$begingroup$
I am trying to implement AES CTR encryption mode with HMAC authentication for messages.
It's encrypting and decrypting fine as long as the key length is 64 bytes, since AES key and HMAC key are being derived from this key.
Questions
- Is it safe to append IV or nonce to the encrypted messages?
- Is it safe to append HMAC digest to append to the messages?
- Can you review it for best security coding practices?
I am using Pycryptodome
Code
import os
import zlib
from Crypto.Hash import HMAC
from Crypto.Hash import SHA256
from Crypto.Cipher import AES
from Crypto.Util import Counter
import zlib
def encrypt(full_key, plaintext):
if len(full_key) != 64:
raise Exception("FULL key length shall be equal to 64")
key = full_key[:len(full_key) //2]
# Use the last half as the HMAC key
hmac_key = full_key[len(full_key) // 2:]
if isinstance(plaintext, str):
plaintext = plaintext.encode()
compressed = zlib.compress(plaintext, 5)
print (f"compressed plaintext {compressed}")
# Choose a random, 16-byte IV.
iv = os.urandom(16)
# Convert the IV to a Python integer.
iv_int = int(binascii.hexlify(iv), 16)
# Create a new Counter object with IV = iv_int.
ctr = Counter.new(128, initial_value=iv_int)
# Create AES-CTR cipher.
aes = AES.new(key, AES.MODE_CTR, counter=ctr)
# Encrypt and return IV and ciphertext.
ciphertext = aes.encrypt(compressed)
hmac_obj = HMAC.new(hmac_key, compressed, SHA256)
mac = hmac_obj.digest()
return iv+ciphertext+mac
def decrypt(key, ciphertext):
# Initialize counter for decryption. iv should be the same as the output of
# encrypt().
if len(full_key) != 64:
raise Exception("FULL key length shall be equal to 64")
key = full_key[:len(full_key) //2]
# Use the last half as the HMAC key
hmac_key = full_key[len(full_key) // 2:]
mac_length = 32
iv_length = 16
iv = ciphertext[:16]
mac = ciphertext[-mac_length:]
_ciphertext = ciphertext[iv_length:-mac_length]
iv_int = int(iv.hex(), 16)
ctr = Counter.new(128, initial_value=iv_int)
# Create AES-CTR cipher.
aes = AES.new(key, AES.MODE_CTR, counter=ctr)
ciphertext = aes.decrypt(_ciphertext)
# Extract the MAC from the end of the file
hmac_obj = HMAC.new(hmac_key, ciphertext, SHA256)
computed_mac = hmac_obj.digest()
if computed_mac != mac:
raise Exception("Messege integrity violated")
plaintext= zlib.decompress(ciphertext)
# Decrypt and return the plaintext.
return plaintext
python python-3.x security authentication aes
New contributor
$endgroup$
add a comment |
$begingroup$
I am trying to implement AES CTR encryption mode with HMAC authentication for messages.
It's encrypting and decrypting fine as long as the key length is 64 bytes, since AES key and HMAC key are being derived from this key.
Questions
- Is it safe to append IV or nonce to the encrypted messages?
- Is it safe to append HMAC digest to append to the messages?
- Can you review it for best security coding practices?
I am using Pycryptodome
Code
import os
import zlib
from Crypto.Hash import HMAC
from Crypto.Hash import SHA256
from Crypto.Cipher import AES
from Crypto.Util import Counter
import zlib
def encrypt(full_key, plaintext):
if len(full_key) != 64:
raise Exception("FULL key length shall be equal to 64")
key = full_key[:len(full_key) //2]
# Use the last half as the HMAC key
hmac_key = full_key[len(full_key) // 2:]
if isinstance(plaintext, str):
plaintext = plaintext.encode()
compressed = zlib.compress(plaintext, 5)
print (f"compressed plaintext {compressed}")
# Choose a random, 16-byte IV.
iv = os.urandom(16)
# Convert the IV to a Python integer.
iv_int = int(binascii.hexlify(iv), 16)
# Create a new Counter object with IV = iv_int.
ctr = Counter.new(128, initial_value=iv_int)
# Create AES-CTR cipher.
aes = AES.new(key, AES.MODE_CTR, counter=ctr)
# Encrypt and return IV and ciphertext.
ciphertext = aes.encrypt(compressed)
hmac_obj = HMAC.new(hmac_key, compressed, SHA256)
mac = hmac_obj.digest()
return iv+ciphertext+mac
def decrypt(key, ciphertext):
# Initialize counter for decryption. iv should be the same as the output of
# encrypt().
if len(full_key) != 64:
raise Exception("FULL key length shall be equal to 64")
key = full_key[:len(full_key) //2]
# Use the last half as the HMAC key
hmac_key = full_key[len(full_key) // 2:]
mac_length = 32
iv_length = 16
iv = ciphertext[:16]
mac = ciphertext[-mac_length:]
_ciphertext = ciphertext[iv_length:-mac_length]
iv_int = int(iv.hex(), 16)
ctr = Counter.new(128, initial_value=iv_int)
# Create AES-CTR cipher.
aes = AES.new(key, AES.MODE_CTR, counter=ctr)
ciphertext = aes.decrypt(_ciphertext)
# Extract the MAC from the end of the file
hmac_obj = HMAC.new(hmac_key, ciphertext, SHA256)
computed_mac = hmac_obj.digest()
if computed_mac != mac:
raise Exception("Messege integrity violated")
plaintext= zlib.decompress(ciphertext)
# Decrypt and return the plaintext.
return plaintext
python python-3.x security authentication aes
New contributor
$endgroup$
I am trying to implement AES CTR encryption mode with HMAC authentication for messages.
It's encrypting and decrypting fine as long as the key length is 64 bytes, since AES key and HMAC key are being derived from this key.
Questions
- Is it safe to append IV or nonce to the encrypted messages?
- Is it safe to append HMAC digest to append to the messages?
- Can you review it for best security coding practices?
I am using Pycryptodome
Code
import os
import zlib
from Crypto.Hash import HMAC
from Crypto.Hash import SHA256
from Crypto.Cipher import AES
from Crypto.Util import Counter
import zlib
def encrypt(full_key, plaintext):
if len(full_key) != 64:
raise Exception("FULL key length shall be equal to 64")
key = full_key[:len(full_key) //2]
# Use the last half as the HMAC key
hmac_key = full_key[len(full_key) // 2:]
if isinstance(plaintext, str):
plaintext = plaintext.encode()
compressed = zlib.compress(plaintext, 5)
print (f"compressed plaintext {compressed}")
# Choose a random, 16-byte IV.
iv = os.urandom(16)
# Convert the IV to a Python integer.
iv_int = int(binascii.hexlify(iv), 16)
# Create a new Counter object with IV = iv_int.
ctr = Counter.new(128, initial_value=iv_int)
# Create AES-CTR cipher.
aes = AES.new(key, AES.MODE_CTR, counter=ctr)
# Encrypt and return IV and ciphertext.
ciphertext = aes.encrypt(compressed)
hmac_obj = HMAC.new(hmac_key, compressed, SHA256)
mac = hmac_obj.digest()
return iv+ciphertext+mac
def decrypt(key, ciphertext):
# Initialize counter for decryption. iv should be the same as the output of
# encrypt().
if len(full_key) != 64:
raise Exception("FULL key length shall be equal to 64")
key = full_key[:len(full_key) //2]
# Use the last half as the HMAC key
hmac_key = full_key[len(full_key) // 2:]
mac_length = 32
iv_length = 16
iv = ciphertext[:16]
mac = ciphertext[-mac_length:]
_ciphertext = ciphertext[iv_length:-mac_length]
iv_int = int(iv.hex(), 16)
ctr = Counter.new(128, initial_value=iv_int)
# Create AES-CTR cipher.
aes = AES.new(key, AES.MODE_CTR, counter=ctr)
ciphertext = aes.decrypt(_ciphertext)
# Extract the MAC from the end of the file
hmac_obj = HMAC.new(hmac_key, ciphertext, SHA256)
computed_mac = hmac_obj.digest()
if computed_mac != mac:
raise Exception("Messege integrity violated")
plaintext= zlib.decompress(ciphertext)
# Decrypt and return the plaintext.
return plaintext
python python-3.x security authentication aes
python python-3.x security authentication aes
New contributor
New contributor
edited 7 hours ago
saurav verma
New contributor
asked yesterday
saurav vermasaurav verma
1115
1115
New contributor
New contributor
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
$begingroup$
I'm not a security expert, so my review will focus on general best practices first.
Code structure and style
1. Imports
You can group import that belong together, e.g
from Crypto.Hash import HMAC
from Crypto.Hash import SHA256
can be written as
from Crypto.Hash import HMAC, SHA256
The Official Style Guide for Python also recommends to place import from the standard library before any third party libraries. Apart from the double import of zlib
you're already following this.
2. Documentation
You started to add some kind of documentation on decrypt
, so encrypt
should be treated the same. The officially recommended way is to provide function documentation surrounded by """..."""
like so
def decrypt(key, ciphertext):
"""Initialize counter for decryption.
iv should be the same as the output of encrypt().
"""
#^--- you probably also mean ciphertext here
This will also allow Python's built-in help(...)
function to pick up your documentation.
3. Whitespace
You should tidy up the newlines in your code. There are often two (sometimes more) newlines within a function's body, while there are none between the imports and functions. This might be an issue from posting the code here on Code Review, but you should definitely check that. A reasonable convention many people agree on is to use two newlines to seperate functions (or imports from the following functions) and only use a single newline in function bodys to visually create logical groups of code.
The code itself
1. Exceptions
"You're not doing the right thing!" What thing you might ask? Well, ... "Read it! Find it out yourself!"
Sounds frustrating, doesn't it? Python offers a wide variety of built-in exceptions so you can easily convey the type of error that has happened, without the need to parse/read the actual error message. In your case ValueError
seems to be appropriate. From the documentation:
exception
ValueError
Raised when an operation or function receives an argument that has the right type but an inappropriate value, [...]
In case of an error on checking the HMAC, you might add your own exception derived from Exception
(maybe HMACValidationError
?). I will leave this as an exercise to you.
2. encrypt/decrypt
These functions look quite reasonable from what I can jugde and work the way I would expect them to work. A minor "issue" I found, is that they are not handling string encoding/decoding symmetrically. While encrypt
does handle unicode strings by converting them to bytes representation before encryption, decrypt
has no way of knowing that this step has taken place and will always return the raw bytes.
At the moment, I would favor an approach where the application using those functions has to take care that the input is already encoded into bytes and also decode the bytes back into a unicode string if desired. This would also be in line with not handling string values for the key, which strongly hints that there is an earlier step where those are created and possibly encoded.
3. Security
From what I've seen on my journey through cryptoland, it is a quite common practice to simply prepend the ciphertext with the initialiation vector (IV). Password hashing functions do something similar and tend to store the salt prepended to the actual hash to have it available for validation later. Remember: the IV does not protect your ciphertext in the same way the key does, and therefore must not be handled with the same care. It instead prevents an attacker to see the same plaintext mapped the same ciphertext repeatedly, which is maybe more than you can afford to reveal.
Something similar applys to the HMAC. The security of the HMAC authentication should only rely on the secrecy of the key, and nothing else. So as long as you use a suitable HMAC implementation and a reasonably secure key, you should be good to go. Just bear in mind that an HMAC alone does not and cannot prevent all kind of attacks, e.g. HMAC alone cannot prevent replay.
$endgroup$
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
saurav verma is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f217014%2faes-ctr-mode-encryption-with-hmac%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
I'm not a security expert, so my review will focus on general best practices first.
Code structure and style
1. Imports
You can group import that belong together, e.g
from Crypto.Hash import HMAC
from Crypto.Hash import SHA256
can be written as
from Crypto.Hash import HMAC, SHA256
The Official Style Guide for Python also recommends to place import from the standard library before any third party libraries. Apart from the double import of zlib
you're already following this.
2. Documentation
You started to add some kind of documentation on decrypt
, so encrypt
should be treated the same. The officially recommended way is to provide function documentation surrounded by """..."""
like so
def decrypt(key, ciphertext):
"""Initialize counter for decryption.
iv should be the same as the output of encrypt().
"""
#^--- you probably also mean ciphertext here
This will also allow Python's built-in help(...)
function to pick up your documentation.
3. Whitespace
You should tidy up the newlines in your code. There are often two (sometimes more) newlines within a function's body, while there are none between the imports and functions. This might be an issue from posting the code here on Code Review, but you should definitely check that. A reasonable convention many people agree on is to use two newlines to seperate functions (or imports from the following functions) and only use a single newline in function bodys to visually create logical groups of code.
The code itself
1. Exceptions
"You're not doing the right thing!" What thing you might ask? Well, ... "Read it! Find it out yourself!"
Sounds frustrating, doesn't it? Python offers a wide variety of built-in exceptions so you can easily convey the type of error that has happened, without the need to parse/read the actual error message. In your case ValueError
seems to be appropriate. From the documentation:
exception
ValueError
Raised when an operation or function receives an argument that has the right type but an inappropriate value, [...]
In case of an error on checking the HMAC, you might add your own exception derived from Exception
(maybe HMACValidationError
?). I will leave this as an exercise to you.
2. encrypt/decrypt
These functions look quite reasonable from what I can jugde and work the way I would expect them to work. A minor "issue" I found, is that they are not handling string encoding/decoding symmetrically. While encrypt
does handle unicode strings by converting them to bytes representation before encryption, decrypt
has no way of knowing that this step has taken place and will always return the raw bytes.
At the moment, I would favor an approach where the application using those functions has to take care that the input is already encoded into bytes and also decode the bytes back into a unicode string if desired. This would also be in line with not handling string values for the key, which strongly hints that there is an earlier step where those are created and possibly encoded.
3. Security
From what I've seen on my journey through cryptoland, it is a quite common practice to simply prepend the ciphertext with the initialiation vector (IV). Password hashing functions do something similar and tend to store the salt prepended to the actual hash to have it available for validation later. Remember: the IV does not protect your ciphertext in the same way the key does, and therefore must not be handled with the same care. It instead prevents an attacker to see the same plaintext mapped the same ciphertext repeatedly, which is maybe more than you can afford to reveal.
Something similar applys to the HMAC. The security of the HMAC authentication should only rely on the secrecy of the key, and nothing else. So as long as you use a suitable HMAC implementation and a reasonably secure key, you should be good to go. Just bear in mind that an HMAC alone does not and cannot prevent all kind of attacks, e.g. HMAC alone cannot prevent replay.
$endgroup$
add a comment |
$begingroup$
I'm not a security expert, so my review will focus on general best practices first.
Code structure and style
1. Imports
You can group import that belong together, e.g
from Crypto.Hash import HMAC
from Crypto.Hash import SHA256
can be written as
from Crypto.Hash import HMAC, SHA256
The Official Style Guide for Python also recommends to place import from the standard library before any third party libraries. Apart from the double import of zlib
you're already following this.
2. Documentation
You started to add some kind of documentation on decrypt
, so encrypt
should be treated the same. The officially recommended way is to provide function documentation surrounded by """..."""
like so
def decrypt(key, ciphertext):
"""Initialize counter for decryption.
iv should be the same as the output of encrypt().
"""
#^--- you probably also mean ciphertext here
This will also allow Python's built-in help(...)
function to pick up your documentation.
3. Whitespace
You should tidy up the newlines in your code. There are often two (sometimes more) newlines within a function's body, while there are none between the imports and functions. This might be an issue from posting the code here on Code Review, but you should definitely check that. A reasonable convention many people agree on is to use two newlines to seperate functions (or imports from the following functions) and only use a single newline in function bodys to visually create logical groups of code.
The code itself
1. Exceptions
"You're not doing the right thing!" What thing you might ask? Well, ... "Read it! Find it out yourself!"
Sounds frustrating, doesn't it? Python offers a wide variety of built-in exceptions so you can easily convey the type of error that has happened, without the need to parse/read the actual error message. In your case ValueError
seems to be appropriate. From the documentation:
exception
ValueError
Raised when an operation or function receives an argument that has the right type but an inappropriate value, [...]
In case of an error on checking the HMAC, you might add your own exception derived from Exception
(maybe HMACValidationError
?). I will leave this as an exercise to you.
2. encrypt/decrypt
These functions look quite reasonable from what I can jugde and work the way I would expect them to work. A minor "issue" I found, is that they are not handling string encoding/decoding symmetrically. While encrypt
does handle unicode strings by converting them to bytes representation before encryption, decrypt
has no way of knowing that this step has taken place and will always return the raw bytes.
At the moment, I would favor an approach where the application using those functions has to take care that the input is already encoded into bytes and also decode the bytes back into a unicode string if desired. This would also be in line with not handling string values for the key, which strongly hints that there is an earlier step where those are created and possibly encoded.
3. Security
From what I've seen on my journey through cryptoland, it is a quite common practice to simply prepend the ciphertext with the initialiation vector (IV). Password hashing functions do something similar and tend to store the salt prepended to the actual hash to have it available for validation later. Remember: the IV does not protect your ciphertext in the same way the key does, and therefore must not be handled with the same care. It instead prevents an attacker to see the same plaintext mapped the same ciphertext repeatedly, which is maybe more than you can afford to reveal.
Something similar applys to the HMAC. The security of the HMAC authentication should only rely on the secrecy of the key, and nothing else. So as long as you use a suitable HMAC implementation and a reasonably secure key, you should be good to go. Just bear in mind that an HMAC alone does not and cannot prevent all kind of attacks, e.g. HMAC alone cannot prevent replay.
$endgroup$
add a comment |
$begingroup$
I'm not a security expert, so my review will focus on general best practices first.
Code structure and style
1. Imports
You can group import that belong together, e.g
from Crypto.Hash import HMAC
from Crypto.Hash import SHA256
can be written as
from Crypto.Hash import HMAC, SHA256
The Official Style Guide for Python also recommends to place import from the standard library before any third party libraries. Apart from the double import of zlib
you're already following this.
2. Documentation
You started to add some kind of documentation on decrypt
, so encrypt
should be treated the same. The officially recommended way is to provide function documentation surrounded by """..."""
like so
def decrypt(key, ciphertext):
"""Initialize counter for decryption.
iv should be the same as the output of encrypt().
"""
#^--- you probably also mean ciphertext here
This will also allow Python's built-in help(...)
function to pick up your documentation.
3. Whitespace
You should tidy up the newlines in your code. There are often two (sometimes more) newlines within a function's body, while there are none between the imports and functions. This might be an issue from posting the code here on Code Review, but you should definitely check that. A reasonable convention many people agree on is to use two newlines to seperate functions (or imports from the following functions) and only use a single newline in function bodys to visually create logical groups of code.
The code itself
1. Exceptions
"You're not doing the right thing!" What thing you might ask? Well, ... "Read it! Find it out yourself!"
Sounds frustrating, doesn't it? Python offers a wide variety of built-in exceptions so you can easily convey the type of error that has happened, without the need to parse/read the actual error message. In your case ValueError
seems to be appropriate. From the documentation:
exception
ValueError
Raised when an operation or function receives an argument that has the right type but an inappropriate value, [...]
In case of an error on checking the HMAC, you might add your own exception derived from Exception
(maybe HMACValidationError
?). I will leave this as an exercise to you.
2. encrypt/decrypt
These functions look quite reasonable from what I can jugde and work the way I would expect them to work. A minor "issue" I found, is that they are not handling string encoding/decoding symmetrically. While encrypt
does handle unicode strings by converting them to bytes representation before encryption, decrypt
has no way of knowing that this step has taken place and will always return the raw bytes.
At the moment, I would favor an approach where the application using those functions has to take care that the input is already encoded into bytes and also decode the bytes back into a unicode string if desired. This would also be in line with not handling string values for the key, which strongly hints that there is an earlier step where those are created and possibly encoded.
3. Security
From what I've seen on my journey through cryptoland, it is a quite common practice to simply prepend the ciphertext with the initialiation vector (IV). Password hashing functions do something similar and tend to store the salt prepended to the actual hash to have it available for validation later. Remember: the IV does not protect your ciphertext in the same way the key does, and therefore must not be handled with the same care. It instead prevents an attacker to see the same plaintext mapped the same ciphertext repeatedly, which is maybe more than you can afford to reveal.
Something similar applys to the HMAC. The security of the HMAC authentication should only rely on the secrecy of the key, and nothing else. So as long as you use a suitable HMAC implementation and a reasonably secure key, you should be good to go. Just bear in mind that an HMAC alone does not and cannot prevent all kind of attacks, e.g. HMAC alone cannot prevent replay.
$endgroup$
I'm not a security expert, so my review will focus on general best practices first.
Code structure and style
1. Imports
You can group import that belong together, e.g
from Crypto.Hash import HMAC
from Crypto.Hash import SHA256
can be written as
from Crypto.Hash import HMAC, SHA256
The Official Style Guide for Python also recommends to place import from the standard library before any third party libraries. Apart from the double import of zlib
you're already following this.
2. Documentation
You started to add some kind of documentation on decrypt
, so encrypt
should be treated the same. The officially recommended way is to provide function documentation surrounded by """..."""
like so
def decrypt(key, ciphertext):
"""Initialize counter for decryption.
iv should be the same as the output of encrypt().
"""
#^--- you probably also mean ciphertext here
This will also allow Python's built-in help(...)
function to pick up your documentation.
3. Whitespace
You should tidy up the newlines in your code. There are often two (sometimes more) newlines within a function's body, while there are none between the imports and functions. This might be an issue from posting the code here on Code Review, but you should definitely check that. A reasonable convention many people agree on is to use two newlines to seperate functions (or imports from the following functions) and only use a single newline in function bodys to visually create logical groups of code.
The code itself
1. Exceptions
"You're not doing the right thing!" What thing you might ask? Well, ... "Read it! Find it out yourself!"
Sounds frustrating, doesn't it? Python offers a wide variety of built-in exceptions so you can easily convey the type of error that has happened, without the need to parse/read the actual error message. In your case ValueError
seems to be appropriate. From the documentation:
exception
ValueError
Raised when an operation or function receives an argument that has the right type but an inappropriate value, [...]
In case of an error on checking the HMAC, you might add your own exception derived from Exception
(maybe HMACValidationError
?). I will leave this as an exercise to you.
2. encrypt/decrypt
These functions look quite reasonable from what I can jugde and work the way I would expect them to work. A minor "issue" I found, is that they are not handling string encoding/decoding symmetrically. While encrypt
does handle unicode strings by converting them to bytes representation before encryption, decrypt
has no way of knowing that this step has taken place and will always return the raw bytes.
At the moment, I would favor an approach where the application using those functions has to take care that the input is already encoded into bytes and also decode the bytes back into a unicode string if desired. This would also be in line with not handling string values for the key, which strongly hints that there is an earlier step where those are created and possibly encoded.
3. Security
From what I've seen on my journey through cryptoland, it is a quite common practice to simply prepend the ciphertext with the initialiation vector (IV). Password hashing functions do something similar and tend to store the salt prepended to the actual hash to have it available for validation later. Remember: the IV does not protect your ciphertext in the same way the key does, and therefore must not be handled with the same care. It instead prevents an attacker to see the same plaintext mapped the same ciphertext repeatedly, which is maybe more than you can afford to reveal.
Something similar applys to the HMAC. The security of the HMAC authentication should only rely on the secrecy of the key, and nothing else. So as long as you use a suitable HMAC implementation and a reasonably secure key, you should be good to go. Just bear in mind that an HMAC alone does not and cannot prevent all kind of attacks, e.g. HMAC alone cannot prevent replay.
answered 3 hours ago
AlexAlex
1,333419
1,333419
add a comment |
add a comment |
saurav verma is a new contributor. Be nice, and check out our Code of Conduct.
saurav verma is a new contributor. Be nice, and check out our Code of Conduct.
saurav verma is a new contributor. Be nice, and check out our Code of Conduct.
saurav verma is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f217014%2faes-ctr-mode-encryption-with-hmac%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown