RESOLVED FIXED Bug 207176
[OpenSSL] Implement WebCrypto APIs for AES-CBC, AES-CFB and AES-GCM
https://bugs.webkit.org/show_bug.cgi?id=207176
Summary [OpenSSL] Implement WebCrypto APIs for AES-CBC, AES-CFB and AES-GCM
Tomoki Imai
Reported 2020-02-03 18:59:54 PST
As the first step of bug 206439, add AES-CBC, AES-CFB, AES-CTR, and AES-GCM support to WebCrypto with OpenSSL backend.
Attachments
patch (45.20 KB, patch)
2020-02-03 19:07 PST, Tomoki Imai
no flags
patch (45.25 KB, patch)
2020-02-03 20:49 PST, Tomoki Imai
no flags
patch (47.56 KB, patch)
2020-02-04 03:29 PST, Tomoki Imai
no flags
patch (42.33 KB, patch)
2020-02-05 02:41 PST, Tomoki Imai
no flags
patch (42.32 KB, patch)
2020-02-06 21:24 PST, Tomoki Imai
no flags
Tomoki Imai
Comment 1 2020-02-03 19:07:26 PST
Created attachment 389617 [details] patch Patch to add AES-CBC, AES-CFB, AES-CTR, AES-GCM support
Tomoki Imai
Comment 2 2020-02-03 19:15:17 PST
It would be nice to land bug 207174 before this bug, because bug 207174 adds more LayoutTests which checks AES-CBC implementations we're going to add in this bug.
Yoshiaki Jitsukawa
Comment 3 2020-02-03 19:51:02 PST
Comment on attachment 389617 [details] patch View in context: https://bugs.webkit.org/attachment.cgi?id=389617&action=review > Source/WebCore/crypto/openssl/CryptoAlgorithmAES_CBCOpenSSL.cpp:59 > + return WTF::nullopt; In this case we can return early before creating a cipher context. > Source/WebCore/crypto/openssl/CryptoAlgorithmAES_CBCOpenSSL.cpp:64 > + Ensure that the iv size is equal to the block size. > Source/WebCore/crypto/openssl/CryptoAlgorithmAES_CBCOpenSSL.cpp:67 > + return WTF::nullopt; Shouldn't we clean up "ctx" before leaving the function? We may want to introduce a wrapper class like CairoUniquePtr or something like that. > Source/WebCore/crypto/openssl/CryptoAlgorithmAES_CBCOpenSSL.cpp:100 > + Please check the IV size. It'd be nice to check if cipherSize is a multiple of the block size.
Yoshiaki Jitsukawa
Comment 4 2020-02-03 20:30:34 PST
Comment on attachment 389617 [details] patch View in context: https://bugs.webkit.org/attachment.cgi?id=389617&action=review >> Source/WebCore/crypto/openssl/CryptoAlgorithmAES_CBCOpenSSL.cpp:64 >> + > > Ensure that the iv size is equal to the block size. Never mind. IV size is checked in CryptoAlgorithmAES_CBC::decrypt(). >> Source/WebCore/crypto/openssl/CryptoAlgorithmAES_CBCOpenSSL.cpp:100 >> + > > Please check the IV size. > > It'd be nice to check if cipherSize is a multiple of the block size. Never mind. IV size is checked in CryptoAlgorithmAES_CBC::encrypt(). > Source/WebCore/crypto/openssl/CryptoAlgorithmAES_CFBOpenSSL.cpp:53 > + Vector<uint8_t> cipher(plainText.size()); "cipher" should be renamed to "cipherText" for better consistency? > Source/WebCore/crypto/openssl/CryptoAlgorithmAES_GCMOpenSSL.cpp:50 > + if (iv.size() != EVP_MAX_IV_LENGTH) Is this intended to be "if (iv.size() > EVP_MAX_IV_LENGTH)" ? > Source/WebCore/crypto/openssl/CryptoAlgorithmAES_GCMOpenSSL.cpp:111 > + if (iv.size() != EVP_MAX_IV_LENGTH) Is this intended to be "if (iv.size() > EVP_MAX_IV_LENGTH)" ? > Source/WebCore/crypto/openssl/CryptoAlgorithmAES_GCMOpenSSL.cpp:114 > + if (cipherText.size() <= tagLength) This is already checked in CryptoAlgorithmAES_GCM::decrypt().
Tomoki Imai
Comment 5 2020-02-03 20:49:48 PST
Created attachment 389622 [details] patch Just addressed style-checker errors from attachment 389617 [details].
Tomoki Imai
Comment 6 2020-02-03 20:51:07 PST
(In reply to Yoshiaki Jitsukawa from comment #4) > Comment on attachment 389617 [details] > patch > Thanks for your review! We're now addressing your comments.
Yoshiaki Jitsukawa
Comment 7 2020-02-03 22:47:26 PST
Comment on attachment 389622 [details] patch View in context: https://bugs.webkit.org/attachment.cgi?id=389622&action=review > Source/WebCore/crypto/openssl/CryptoAlgorithmAES_CFBOpenSSL.cpp:33 > +#include <openssl/aes.h> Probably we shouldn't include low level header "aes.h". > Source/WebCore/crypto/openssl/CryptoAlgorithmAES_CTROpenSSL.cpp:33 > +#include <openssl/aes.h> Probably we shouldn't include low level header "aes.h". > Source/WebCore/crypto/openssl/CryptoAlgorithmAES_CTROpenSSL.cpp:75 > + size_t blocks = inputText.size() / EVP_MAX_IV_LENGTH + 1; It's unclear to me why EVP_MAX_IV_LENGTH should be used. Shouldn't this be replaced with the block size from EVP_CIPHER_block_size()? Also It seems that blocks is the number of padded cipher blocks thus it will differ depends on the operation mode. > Source/WebCore/crypto/openssl/CryptoAlgorithmAES_CTROpenSSL.cpp:78 > + if (counterLength < EVP_MAX_KEY_LENGTH && blocks > (int)(1 << counterLength)) counterLength may be more than 64 so the bit shift may result in an overflow. EVP_MAX_KEY_LENGTH is 64 bytes so it doesn't make sense to compare counterLength (in bits) with EVP_MAX_KEY_LENGTH (in bytes). > Source/WebCore/crypto/openssl/CryptoAlgorithmAES_CTROpenSSL.cpp:83 > + size_t counts = bigIntegerToSizeT(counter); counts may be more than 2^64. > Source/WebCore/crypto/openssl/CryptoAlgorithmAES_CTROpenSSL.cpp:157 > + auto output = crypt(AES_ENCRYPT, key.key(), parameters.counterVector(), parameters.length, plainText); According to the document, EVP_CipherInit_ex() takes 1 for encryption and 0 for decryption for the last parameter so AES_ENCRYPT/AES_DECRYPT shouldn't appear hear. Instead, how about passing crypt() to a boolean parameter and give 1 or 0 to EVP_CipherInit_ex().
Tomoki Imai
Comment 8 2020-02-04 03:29:27 PST
Created attachment 389647 [details] patch Fixed the issues pointed in comment 3, comment 4, and comment 7. Some of issues in CryptoAlgorithmAES_CTROpenSSL is not addressed yet. I'll describe about the remaining issues later.
Tomoki Imai
Comment 9 2020-02-04 03:43:38 PST
Comment on attachment 389617 [details] patch View in context: https://bugs.webkit.org/attachment.cgi?id=389617&action=review >> Source/WebCore/crypto/openssl/CryptoAlgorithmAES_CBCOpenSSL.cpp:59 >> + return WTF::nullopt; > > In this case we can return early before creating a cipher context. Thanks, we moved the aesAlgorithm and make an early return if we cannot obtain algorithm. >> Source/WebCore/crypto/openssl/CryptoAlgorithmAES_CBCOpenSSL.cpp:67 >> + return WTF::nullopt; > > Shouldn't we clean up "ctx" before leaving the function? We may want to introduce a wrapper class like CairoUniquePtr or something like that. We definitely should clean up. We introduced OpenSSLCryptoUniquePtr to make sure that we call EVP_CIPHER_CTX_free every branch. >> Source/WebCore/crypto/openssl/CryptoAlgorithmAES_CFBOpenSSL.cpp:53 >> + Vector<uint8_t> cipher(plainText.size()); > > "cipher" should be renamed to "cipherText" for better consistency? Thanks, we renamed all "cipher" to "cipherText", and "plain" to "plainText". >> Source/WebCore/crypto/openssl/CryptoAlgorithmAES_GCMOpenSSL.cpp:50 >> + if (iv.size() != EVP_MAX_IV_LENGTH) > > Is this intended to be "if (iv.size() > EVP_MAX_IV_LENGTH)" ? Thanks, good point. Actually I believe we don't need this check because AES-GCM accepts the initialization vectors of arbitrary length, and EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, ivlen, NULL) doesn't have ivlen limitation for GCM mode. - https://www.w3.org/TR/WebCryptoAPI/#aes-gcm-params - https://www.openssl.org/docs/manmaster/man3/EVP_CIPHER_block_size.html We may want to have LayoutTests to make sure that it works with the arbitrary length IV. >> Source/WebCore/crypto/openssl/CryptoAlgorithmAES_GCMOpenSSL.cpp:111 >> + if (iv.size() != EVP_MAX_IV_LENGTH) > > Is this intended to be "if (iv.size() > EVP_MAX_IV_LENGTH)" ? As described in cryptEncrypt, we believe we don't need this check. >> Source/WebCore/crypto/openssl/CryptoAlgorithmAES_GCMOpenSSL.cpp:114 >> + if (cipherText.size() <= tagLength) > > This is already checked in CryptoAlgorithmAES_GCM::decrypt(). Thanks, removed.
Tomoki Imai
Comment 10 2020-02-04 04:25:23 PST
Comment on attachment 389622 [details] patch View in context: https://bugs.webkit.org/attachment.cgi?id=389622&action=review >> Source/WebCore/crypto/openssl/CryptoAlgorithmAES_CFBOpenSSL.cpp:33 >> +#include <openssl/aes.h> > > Probably we shouldn't include low level header "aes.h". Yes, removed. >> Source/WebCore/crypto/openssl/CryptoAlgorithmAES_CTROpenSSL.cpp:33 >> +#include <openssl/aes.h> > > Probably we shouldn't include low level header "aes.h". Removed. >> Source/WebCore/crypto/openssl/CryptoAlgorithmAES_CTROpenSSL.cpp:75 >> + size_t blocks = inputText.size() / EVP_MAX_IV_LENGTH + 1; > > It's unclear to me why EVP_MAX_IV_LENGTH should be used. Shouldn't this be replaced with the block size from EVP_CIPHER_block_size()? > > Also It seems that blocks is the number of padded cipher blocks thus it will differ depends on the operation mode. Yes, we should use EVP_CIPHER_block_size here. (not addressed yet) >> Source/WebCore/crypto/openssl/CryptoAlgorithmAES_CTROpenSSL.cpp:78 >> + if (counterLength < EVP_MAX_KEY_LENGTH && blocks > (int)(1 << counterLength)) > > counterLength may be more than 64 so the bit shift may result in an overflow. EVP_MAX_KEY_LENGTH is 64 bytes so it doesn't make sense to compare counterLength (in bits) with EVP_MAX_KEY_LENGTH (in bytes). Thanks for pointing this out, you're right, using EVP_MAX_KEY_LENGTH is incorrect. (not addressed yet) What we wanted to here is checking counterLength is large enough to decrypt/encrypt data of arbitrary size fit in the address space, or the number of blocks is smaller than value range of counter. To test the former, Mac implementation compares counterLength and __WORDSIZE, which is expanded to 32 or 64. https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/crypto/mac/CryptoAlgorithmAES_CTRMac.cpp?rev=255668#L58 Unfortunately, on Windows __WORDSIZE is not available. Is there any common way to get __WORDSIZE on every ports? We also noticed that platform-independent CryptoAlgorithmAES_GCM uses __WORDSIZE. It is used in #if directive, and expanded as 0 as it's not defined. As a result it currently uses 32 bit path, even when 64bit Windows. https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/crypto/algorithms/CryptoAlgorithmAES_GCM.cpp?rev=255668#L42 >> Source/WebCore/crypto/openssl/CryptoAlgorithmAES_CTROpenSSL.cpp:83 >> + size_t counts = bigIntegerToSizeT(counter); > > counts may be more than 2^64. bigIntegerToSizeT should return a value up to 2^64-1 on 64bit machine, and up to 2^32-1 on 32bit machine. (not addressed yet) From my understanding, there is no case where we touch 64 ~ 127-th (0-origin) bit from right on 64bit machine, because the largest block number we may have is bound by 2^64. https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/crypto/mac/CryptoAlgorithmAES_CTRMac.cpp?rev=255668#L37 Also here we want to use __WORDSIZE equivalent for Windows. >> Source/WebCore/crypto/openssl/CryptoAlgorithmAES_CTROpenSSL.cpp:157 >> + auto output = crypt(AES_ENCRYPT, key.key(), parameters.counterVector(), parameters.length, plainText); > > According to the document, EVP_CipherInit_ex() takes 1 for encryption and 0 for decryption for the last parameter so AES_ENCRYPT/AES_DECRYPT shouldn't appear hear. Instead, how about passing crypt() to a boolean parameter and give 1 or 0 to EVP_CipherInit_ex(). Right, we shouldn't use AES_ENCRYPT/AES_DECRYPT here. We now passes 1 or 0 to crypt, instead of boolean as you suggested. It is because the other ports also passes the platform-dependent constants here. - https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/crypto/gcrypt/CryptoAlgorithmAES_CTRGCrypt.cpp?rev=255668#L200 - https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/crypto/mac/CryptoAlgorithmAES_CTRMac.cpp?rev=255668#L144
Tomoki Imai
Comment 11 2020-02-04 19:04:11 PST
Comment on attachment 389647 [details] patch I'm making r? as we need more reviews on the patch, but not cq? because we still have non-addressed review comments.
Tomoki Imai
Comment 12 2020-02-05 01:07:14 PST
We're going to drop AES-CTR support for now because we cannot be confident of our CTR mode implementation. After landing the AES-CBC, AES-CFB, and AES-GCM, we will make another patch to support AES-CTR.
Tomoki Imai
Comment 13 2020-02-05 02:41:54 PST
Created attachment 389788 [details] patch - Drop AES-CTR support. - Replace "Finalise" with "Finalize" - Fix some capitalization in comments. By this patch, we addressed all the reviewed points in comment 3 - comment 7.
Jiewen Tan
Comment 14 2020-02-06 12:40:14 PST
Comment on attachment 389788 [details] patch Sorry that I'm not familiar with OpenSSL. Overall, the implementation seems solid. I will let someone from Sony to put the trigger to r+ it.
Yoshiaki Jitsukawa
Comment 15 2020-02-06 17:15:58 PST
Comment on attachment 389788 [details] patch View in context: https://bugs.webkit.org/attachment.cgi?id=389788&action=review > Source/WebCore/crypto/openssl/CryptoAlgorithmAES_GCMOpenSSL.cpp:61 > + Vector<uint8_t> cipherText(plainText.size()); We can reserve this cipherText buffer including the tag area like: Vector<uint8_t> cipherText(plainText.size() + tagLength) so that we can avoid potential memory reallocation afterwards.
Tomoki Imai
Comment 16 2020-02-06 21:24:32 PST
Created attachment 390054 [details] patch Reserve tag memory area along with cipherText in CryptoAlgorithmAES_GCMOpenSSL(suggested in comment 15)
WebKit Commit Bot
Comment 17 2020-02-07 02:01:57 PST
Comment on attachment 390054 [details] patch Clearing flags on attachment: 390054 Committed r256014: <https://trac.webkit.org/changeset/256014>
WebKit Commit Bot
Comment 18 2020-02-07 02:01:59 PST
All reviewed patches have been landed. Closing bug.
Radar WebKit Bug Importer
Comment 19 2020-02-07 02:02:14 PST
Note You need to log in before you can comment on or make changes to this bug.