AES CTR Set IV(counter value)
회사 과제를 하다가 AES-128 CTR 모드를 복호화 할 일이 있었다.
다음은 Swift
와 Objective-C
로 작성된 iOS Signal의 encryptDeviceName()
함수다.
- encryptDeviceName()
// cipher_key = HmacSHA256(key=HmacSHA256(key=master_secret, “cipher”), input=synthetic_iv)
let cipherKey = try computeCipherKey(masterSecret: masterSecret, syntheticIV: syntheticIV)
// cipher_text = AES-CTR(key=cipher_key, input=plaintext, counter=0)
//
// An all-zeros IV corresponds to an AES CTR counter of zero.
let ciphertextIV = Data(count: Int(kAES256CTR_IVLength))
guard let ciphertextKey = OWSAES256Key(data: cipherKey) else {
owsFailDebug("Invalid cipher key.")
throw DeviceNameError.assertionFailure
}
guard let ciphertext: AES256CTREncryptionResult = Cryptography.encryptAESCTR(
plaintextData: plaintextData,
initializationVector: ciphertextIV,
key: ciphertextKey) else {
owsFailDebug("Could not encrypt cipher text.")
throw DeviceNameError.assertionFailure
}
IV(counter)를 랜덤하게 생성해서 AES CTR 모드를 이용해 암복호화하는 예제는 인터넷에 많았다.
그런데 IV를 특정한 값으로 지정하는 예제가 없었다.
만약 int형 counter를 인자로 전달하려 하면 에러가 발생한다.
ex) counter=1
AES256CTR = AES.new(cipher_key, AES.MODE_CTR, counter=1)
- error
File "C:\Users\pc003\AppData\Local\Programs\Python\Python39\lib\site-packages\Cryptodome\Cipher\AES.py", line 232, in new
return _create_cipher(sys.modules[__name__], key, mode, *args, **kwargs)
File "C:\Users\pc003\AppData\Local\Programs\Python\Python39\lib\site-packages\Cryptodome\Cipher\__init__.py", line 79, in _create_cipher
return modes[mode](factory, **kwargs)
File "C:\Users\pc003\AppData\Local\Programs\Python\Python39\lib\site-packages\Cryptodome\Cipher\_mode_ctr.py", line 366, in _create_ctr_cipher
_counter = dict(counter)
TypeError: 'int' object is not iterable
int가 아닌 object
로 할당해야한다.
Crypto.Util.Counter
인자를 Crypto.Util.Counter
로 전달해주면 된다.
아래는 AES-128 CTR에서 counter를 특정 정수로 지정한 테스트 코드다. specific_IV = 0
- test code
#!/usr/bin/env python3
try:
from pip import get_installed_distributions as pip_list
except:
from pip._internal.utils.misc import get_installed_distributions as pip_list
packages=[]
[packages.append(pkg.key) for pkg in pip_list()]
if 'pycryptodome' in packages:
from Crypto.Cipher import AES
from Crypto.Util import Counter
elif 'pycryptodomex' in packages:
from Cryptodome.Cipher import AES
from Cryptodome.Util import Counter
else:
print("install pycryptodome or pycryptodomex with:")
print("`pip install pycryptodome` or `pip install pycryptodomex`")
exit()
def AES_CTR_Test():
# Set cipher key and iv
cipher_key = b'00112233445566778899aabbccddeeff'
specific_IV = 0
counter_value = Counter.new(128, initial_value=specific_IV)
plaintext = b'THIS_IS_A_PLAIN_TEXT_LONGER_THAN_16BYTES'
print("[plaintext]\n", plaintext)
# Encrypt
# ciphertext = AES-128-CTR-Encrypt(key=cipher_key, input=plaintext, counter=0)
AES256CTR = AES.new(cipher_key, AES.MODE_CTR, counter=counter_value)
ciphertext = AES256CTR.encrypt(plaintext)
assert(len(ciphertext) == len(plaintext))
print("[ciphertext]\n", ciphertext.hex())
# Decrypt
# plaintext = AES-128-CTR-Encrypt(key=cipher_key, input=ciphertext, counter=0)
AES256CTR = AES.new(cipher_key, AES.MODE_CTR, counter=counter_value)
recovered = AES256CTR.decrypt(ciphertext)
assert(len(ciphertext) == len(plaintext))
print("[recovered]\n", recovered)
def main():
AES_CTR_Test()
if __name__ == '__main__':
main()
# EOF
- output
[plaintext]
b'THIS_IS_A_PLAIN_TEXT_LONGER_THAN_16BYTES'
[ciphertext]
e3e3805032ec378a18216992cfbfbe33d728bb6aac2c4e0c3730cbd8dd3cf68722feb5a518f4dcdf
[recovered]
b'THIS_IS_A_PLAIN_TEXT_LONGER_THAN_16BYTES'
Swift와 위 코드로 크로스 체크했다.
참고
Signal-iOS/SignalServiceKit/src/Util/DeviceNames.swift:
https://github.com/signalapp/Signal-iOS/blob/master/SignalServiceKit/src/Util/DeviceNames.swift#L47
Uploaded by Notion2Tistory v1.1.0