Skip to content

Commit 30e22d9

Browse files
committed
cipher: various docs improvements
The EVP_CIPHER_CTX = OpenSSL::Cipher interface for AEAD ciphers is notoriously complicated and full of pitfalls. I tried to clarify docs so that users can hopefully connect the Ruby methods with the corresponding OpenSSL man pages more easily. - Call out the common mistakes with Cipher#iv= and Cipher#auth_tag= with AES-GCM. - Update outdated notes about the method calling order requirements with AEAD ciphers. - Add references to the man page where the behavior varies according to the specific cipher algorithm and we cannot document every detail. - Various style/wording updates.
1 parent 82e80ee commit 30e22d9

File tree

1 file changed

+123
-80
lines changed

1 file changed

+123
-80
lines changed

ext/openssl/ossl_cipher.c

Lines changed: 123 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,8 @@ ossl_cipher_init(VALUE self, int enc)
219219
*
220220
* Initializes the Cipher for encryption.
221221
*
222-
* Make sure to call Cipher#encrypt or Cipher#decrypt before using any of the
223-
* following methods:
224-
* * [#key=, #iv=, #random_key, #random_iv, #pkcs5_keyivgen]
222+
* Make sure to call either #encrypt or #decrypt before using the Cipher for
223+
* any operation or setting any parameters.
225224
*
226225
* Internally calls EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, 1).
227226
*/
@@ -237,9 +236,8 @@ ossl_cipher_encrypt(VALUE self)
237236
*
238237
* Initializes the Cipher for decryption.
239238
*
240-
* Make sure to call Cipher#encrypt or Cipher#decrypt before using any of the
241-
* following methods:
242-
* * [#key=, #iv=, #random_key, #random_iv, #pkcs5_keyivgen]
239+
* Make sure to call either #encrypt or #decrypt before using the Cipher for
240+
* any operation or setting any parameters.
243241
*
244242
* Internally calls EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, 0).
245243
*/
@@ -255,19 +253,15 @@ ossl_cipher_decrypt(VALUE self)
255253
*
256254
* Generates and sets the key/IV based on a password.
257255
*
258-
* *WARNING*: This method is only PKCS5 v1.5 compliant when using RC2, RC4-40,
259-
* or DES with MD5 or SHA1. Using anything else (like AES) will generate the
260-
* key/iv using an OpenSSL specific method. This method is deprecated and
261-
* should no longer be used. Use a PKCS5 v2 key generation method from
262-
* OpenSSL::PKCS5 instead.
256+
* *WARNING*: This method is deprecated and should not be used. This method
257+
* corresponds to EVP_BytesToKey(), a non-standard OpenSSL extension of the
258+
* legacy PKCS #5 v1.5 key derivation function. See OpenSSL::KDF for other
259+
* options to derive keys from passwords.
263260
*
264261
* === Parameters
265262
* * _salt_ must be an 8 byte string if provided.
266263
* * _iterations_ is an integer with a default of 2048.
267264
* * _digest_ is a Digest object that defaults to 'MD5'
268-
*
269-
* A minimum of 1000 iterations is recommended.
270-
*
271265
*/
272266
static VALUE
273267
ossl_cipher_pkcs5_keyivgen(int argc, VALUE *argv, VALUE self)
@@ -339,6 +333,9 @@ ossl_cipher_update_long(EVP_CIPHER_CTX *ctx, unsigned char *out, long *out_len_p
339333
*
340334
* If _buffer_ is given, the encryption/decryption result will be written to
341335
* it. _buffer_ will be resized automatically.
336+
*
337+
* *NOTE*: When decrypting using an AEAD cipher, the integrity of the output
338+
* is not verified until #final has been called.
342339
*/
343340
static VALUE
344341
ossl_cipher_update(int argc, VALUE *argv, VALUE self)
@@ -398,14 +395,17 @@ ossl_cipher_update(int argc, VALUE *argv, VALUE self)
398395
* cipher.final -> string
399396
*
400397
* Returns the remaining data held in the cipher object. Further calls to
401-
* Cipher#update or Cipher#final will return garbage. This call should always
398+
* Cipher#update or Cipher#final are invalid. This call should always
402399
* be made as the last call of an encryption or decryption operation, after
403400
* having fed the entire plaintext or ciphertext to the Cipher instance.
404401
*
405-
* If an authenticated cipher was used, a CipherError is raised if the tag
406-
* could not be authenticated successfully. Only call this method after
407-
* setting the authentication tag and passing the entire contents of the
408-
* ciphertext into the cipher.
402+
* When encrypting using an AEAD cipher, the authentication tag can be
403+
* retrieved by #auth_tag after #final has been called.
404+
*
405+
* When decrypting using an AEAD cipher, this method will verify the integrity
406+
* of the ciphertext and the associated data with the authentication tag,
407+
* which must be set by #auth_tag= prior to calling this method.
408+
* If the verification fails, CipherError will be raised.
409409
*/
410410
static VALUE
411411
ossl_cipher_final(VALUE self)
@@ -452,14 +452,16 @@ ossl_cipher_name(VALUE self)
452452

453453
/*
454454
* call-seq:
455-
* cipher.key = string -> string
455+
* cipher.key = string
456456
*
457457
* Sets the cipher key. To generate a key, you should either use a secure
458458
* random byte string or, if the key is to be derived from a password, you
459459
* should rely on PBKDF2 functionality provided by OpenSSL::PKCS5. To
460460
* generate a secure random-based key, Cipher#random_key may be used.
461461
*
462462
* Only call this method after calling Cipher#encrypt or Cipher#decrypt.
463+
*
464+
* See also the man page EVP_CipherInit_ex(3).
463465
*/
464466
static VALUE
465467
ossl_cipher_set_key(VALUE self, VALUE key)
@@ -484,15 +486,21 @@ ossl_cipher_set_key(VALUE self, VALUE key)
484486

485487
/*
486488
* call-seq:
487-
* cipher.iv = string -> string
489+
* cipher.iv = string
488490
*
489491
* Sets the cipher IV. Please note that since you should never be using ECB
490492
* mode, an IV is always explicitly required and should be set prior to
491-
* encryption. The IV itself can be safely transmitted in public, but it
492-
* should be unpredictable to prevent certain kinds of attacks. You may use
493-
* Cipher#random_iv to create a secure random IV.
493+
* encryption. The IV itself can be safely transmitted in public.
494494
*
495-
* Only call this method after calling Cipher#encrypt or Cipher#decrypt.
495+
* This method expects the String to have the length equal to #iv_len. To use
496+
* a different IV length with an AEAD cipher, #iv_len= must be set prior to
497+
* calling this method.
498+
*
499+
* *NOTE*: In OpenSSL API conventions, the IV value may correspond to the
500+
* "nonce" instead in some cipher modes. Refer to the OpenSSL man pages for
501+
* details.
502+
*
503+
* See also the man page EVP_CipherInit_ex(3).
496504
*/
497505
static VALUE
498506
ossl_cipher_set_iv(VALUE self, VALUE iv)
@@ -520,8 +528,7 @@ ossl_cipher_set_iv(VALUE self, VALUE iv)
520528
* call-seq:
521529
* cipher.authenticated? -> true | false
522530
*
523-
* Indicated whether this Cipher instance uses an Authenticated Encryption
524-
* mode.
531+
* Indicates whether this Cipher instance uses an AEAD mode.
525532
*/
526533
static VALUE
527534
ossl_cipher_is_authenticated(VALUE self)
@@ -535,21 +542,23 @@ ossl_cipher_is_authenticated(VALUE self)
535542

536543
/*
537544
* call-seq:
538-
* cipher.auth_data = string -> string
545+
* cipher.auth_data = string
546+
*
547+
* Sets additional authenticated data (AAD), also called associated data, for
548+
* this Cipher. This method is available for AEAD ciphers.
539549
*
540-
* Sets the cipher's additional authenticated data. This field must be
541-
* set when using AEAD cipher modes such as GCM or CCM. If no associated
542-
* data shall be used, this method must *still* be called with a value of "".
543550
* The contents of this field should be non-sensitive data which will be
544551
* added to the ciphertext to generate the authentication tag which validates
545552
* the contents of the ciphertext.
546553
*
547-
* The AAD must be set prior to encryption or decryption. In encryption mode,
548-
* it must be set after calling Cipher#encrypt and setting Cipher#key= and
549-
* Cipher#iv=. When decrypting, the authenticated data must be set after key,
550-
* iv and especially *after* the authentication tag has been set. I.e. set it
551-
* only after calling Cipher#decrypt, Cipher#key=, Cipher#iv= and
552-
* Cipher#auth_tag= first.
554+
* This method must be called after #key= and #iv= have been set, but before
555+
* starting actual encryption or decryption with #update. In some cipher modes,
556+
* #auth_tag_len= and #ccm_data_len= may also need to be called before this
557+
* method.
558+
*
559+
* See also the "AEAD Interface" section of the man page EVP_EncryptInit(3).
560+
* This method internally calls EVP_CipherUpdate() with the output buffer
561+
* set to NULL.
553562
*/
554563
static VALUE
555564
ossl_cipher_set_auth_data(VALUE self, VALUE data)
@@ -577,16 +586,17 @@ ossl_cipher_set_auth_data(VALUE self, VALUE data)
577586
* call-seq:
578587
* cipher.auth_tag(tag_len = 16) -> String
579588
*
580-
* Gets the authentication tag generated by Authenticated Encryption Cipher
581-
* modes (GCM for example). This tag may be stored along with the ciphertext,
582-
* then set on the decryption cipher to authenticate the contents of the
583-
* ciphertext against changes. If the optional integer parameter _tag_len_ is
584-
* given, the returned tag will be _tag_len_ bytes long. If the parameter is
585-
* omitted, the default length of 16 bytes or the length previously set by
586-
* #auth_tag_len= will be used. For maximum security, the longest possible
587-
* should be chosen.
589+
* Gets the generated authentication tag. This method is available for AEAD
590+
* ciphers, and should be called after encryption has been finalized by calling
591+
* #final.
592+
*
593+
* The returned tag will be _tag_len_ bytes long. Some cipher modes require
594+
* the desired length in advance using a separate call to #auth_tag_len=,
595+
* before starting encryption.
588596
*
589-
* The tag may only be retrieved after calling Cipher#final.
597+
* See also the "AEAD Interface" section of the man page EVP_EncryptInit(3).
598+
* This method internally calls EVP_CIPHER_CTX_ctrl() with
599+
* EVP_CTRL_AEAD_GET_TAG.
590600
*/
591601
static VALUE
592602
ossl_cipher_get_auth_tag(int argc, VALUE *argv, VALUE self)
@@ -615,16 +625,24 @@ ossl_cipher_get_auth_tag(int argc, VALUE *argv, VALUE self)
615625

616626
/*
617627
* call-seq:
618-
* cipher.auth_tag = string -> string
628+
* cipher.auth_tag = string
619629
*
620630
* Sets the authentication tag to verify the integrity of the ciphertext.
621-
* This can be called only when the cipher supports AE. The tag must be set
622-
* after calling Cipher#decrypt, Cipher#key= and Cipher#iv=, but before
623-
* calling Cipher#final. After all decryption is performed, the tag is
624-
* verified automatically in the call to Cipher#final.
625631
*
626-
* For OCB mode, the tag length must be supplied with #auth_tag_len=
627-
* beforehand.
632+
* The authentication tag must be set before #final is called. The tag is
633+
* verified during the #final call.
634+
*
635+
* Note that, for CCM mode and OCB mode, the expected length of the tag must
636+
* be set before starting decryption by a separate call to #auth_tag_len=.
637+
* The content of the tag can be provided at any time before #final is called.
638+
*
639+
* *NOTE*: The caller must ensure that the String passed to this method has
640+
* the desired length. Some cipher modes support variable tag lengths, and
641+
* this method may accept a truncated tag without raising an exception.
642+
*
643+
* See also the "AEAD Interface" section of the man page EVP_EncryptInit(3).
644+
* This method internally calls EVP_CIPHER_CTX_ctrl() with
645+
* EVP_CTRL_AEAD_SET_TAG.
628646
*/
629647
static VALUE
630648
ossl_cipher_set_auth_tag(VALUE self, VALUE vtag)
@@ -649,14 +667,17 @@ ossl_cipher_set_auth_tag(VALUE self, VALUE vtag)
649667

650668
/*
651669
* call-seq:
652-
* cipher.auth_tag_len = Integer -> Integer
670+
* cipher.auth_tag_len = integer
653671
*
654-
* Sets the length of the authentication tag to be generated or to be given for
655-
* AEAD ciphers that requires it as in input parameter. Note that not all AEAD
656-
* ciphers support this method.
672+
* Sets the length of the expected authentication tag for this Cipher. This
673+
* method is available for some of AEAD ciphers that require the length to be
674+
* set before starting encryption or decryption, such as CCM mode or OCB mode.
657675
*
658-
* In OCB mode, the length must be supplied both when encrypting and when
659-
* decrypting, and must be before specifying an IV.
676+
* For CCM mode and OCB mode, the tag length must be set before #iv= is set.
677+
*
678+
* See also the "AEAD Interface" section of the man page EVP_EncryptInit(3).
679+
* This method internally calls EVP_CIPHER_CTX_ctrl() with
680+
* EVP_CTRL_AEAD_SET_TAG and a NULL buffer.
660681
*/
661682
static VALUE
662683
ossl_cipher_set_auth_tag_len(VALUE self, VALUE vlen)
@@ -679,11 +700,16 @@ ossl_cipher_set_auth_tag_len(VALUE self, VALUE vlen)
679700

680701
/*
681702
* call-seq:
682-
* cipher.iv_len = integer -> integer
703+
* cipher.iv_len = integer
704+
*
705+
* Sets the IV/nonce length for this Cipher. This method is available for AEAD
706+
* ciphers that support variable IV lengths. This method can be called if a
707+
* different IV length than OpenSSL's default is desired, prior to calling
708+
* #iv=.
683709
*
684-
* Sets the IV/nonce length of the Cipher. Normally block ciphers don't allow
685-
* changing the IV length, but some make use of IV for 'nonce'. You may need
686-
* this for interoperability with other applications.
710+
* See also the "AEAD Interface" section of the man page EVP_EncryptInit(3).
711+
* This method internally calls EVP_CIPHER_CTX_ctrl() with
712+
* EVP_CTRL_AEAD_SET_IVLEN.
687713
*/
688714
static VALUE
689715
ossl_cipher_set_iv_length(VALUE self, VALUE iv_length)
@@ -709,13 +735,14 @@ ossl_cipher_set_iv_length(VALUE self, VALUE iv_length)
709735

710736
/*
711737
* call-seq:
712-
* cipher.key_len = integer -> integer
738+
* cipher.key_len = integer
713739
*
714740
* Sets the key length of the cipher. If the cipher is a fixed length cipher
715741
* then attempting to set the key length to any value other than the fixed
716742
* value is an error.
717743
*
718-
* Under normal circumstances you do not need to call this method (and probably shouldn't).
744+
* Under normal circumstances you do not need to call this method (and
745+
* probably shouldn't).
719746
*
720747
* See EVP_CIPHER_CTX_set_key_length for further information.
721748
*/
@@ -732,13 +759,16 @@ ossl_cipher_set_key_length(VALUE self, VALUE key_length)
732759
return key_length;
733760
}
734761

762+
// TODO: Should #padding= take a boolean value instead?
735763
/*
736764
* call-seq:
737-
* cipher.padding = integer -> integer
765+
* cipher.padding = 1 or 0
738766
*
739-
* Enables or disables padding. By default encryption operations are padded using standard block padding and the
740-
* padding is checked and removed when decrypting. If the pad parameter is zero then no padding is performed, the
741-
* total amount of data encrypted or decrypted must then be a multiple of the block size or an error will occur.
767+
* Enables or disables padding. By default encryption operations are padded
768+
* using standard block padding and the padding is checked and removed when
769+
* decrypting. If the pad parameter is zero then no padding is performed, the
770+
* total amount of data encrypted or decrypted must then be a multiple of the
771+
* block size or an error will occur.
742772
*
743773
* See EVP_CIPHER_CTX_set_padding for further information.
744774
*/
@@ -809,13 +839,17 @@ ossl_cipher_block_size(VALUE self)
809839

810840
/*
811841
* call-seq:
812-
* cipher.ccm_data_len = integer -> integer
842+
* cipher.ccm_data_len = integer
813843
*
814-
* Sets the length of the plaintext / ciphertext message that will be
815-
* processed in CCM mode. Make sure to call this method after #key= and
816-
* #iv= have been set, and before #auth_data=.
844+
* Sets the total length of the plaintext / ciphertext message that will be
845+
* processed by #update in CCM mode.
817846
*
818-
* Only call this method after calling Cipher#encrypt or Cipher#decrypt.
847+
* Make sure to call this method after #key= and #iv= have been set, and
848+
* before #auth_data= or #update are called.
849+
*
850+
* This method is only available for CCM mode ciphers.
851+
*
852+
* See also the "AEAD Interface" section of the man page EVP_EncryptInit(3).
819853
*/
820854
static VALUE
821855
ossl_cipher_set_ccm_data_len(VALUE self, VALUE data_len)
@@ -998,24 +1032,28 @@ Init_ossl_cipher(void)
9981032
* could otherwise be exploited to modify ciphertexts in ways beneficial to
9991033
* potential attackers.
10001034
*
1001-
* An associated data is used where there is additional information, such as
1035+
* Associated data, also called additional authenticated data (AAD), is
1036+
* optionally used where there is additional information, such as
10021037
* headers or some metadata, that must be also authenticated but not
1003-
* necessarily need to be encrypted. If no associated data is needed for
1004-
* encryption and later decryption, the OpenSSL library still requires a
1005-
* value to be set - "" may be used in case none is available.
1038+
* necessarily need to be encrypted.
10061039
*
10071040
* An example using the GCM (Galois/Counter Mode). You have 16 bytes _key_,
10081041
* 12 bytes (96 bits) _nonce_ and the associated data _auth_data_. Be sure
10091042
* not to reuse the _key_ and _nonce_ pair. Reusing an nonce ruins the
10101043
* security guarantees of GCM mode.
10111044
*
1045+
* key = OpenSSL::Random.random_bytes(16)
1046+
* nonce = OpenSSL::Random.random_bytes(12)
1047+
* auth_data = "authenticated but unencrypted data"
1048+
* data = "encrypted data"
1049+
*
10121050
* cipher = OpenSSL::Cipher.new('aes-128-gcm').encrypt
10131051
* cipher.key = key
10141052
* cipher.iv = nonce
10151053
* cipher.auth_data = auth_data
10161054
*
10171055
* encrypted = cipher.update(data) + cipher.final
1018-
* tag = cipher.auth_tag # produces 16 bytes tag by default
1056+
* tag = cipher.auth_tag(16)
10191057
*
10201058
* Now you are the receiver. You know the _key_ and have received _nonce_,
10211059
* _auth_data_, _encrypted_ and _tag_ through an untrusted network. Note
@@ -1028,12 +1066,17 @@ Init_ossl_cipher(void)
10281066
* decipher = OpenSSL::Cipher.new('aes-128-gcm').decrypt
10291067
* decipher.key = key
10301068
* decipher.iv = nonce
1031-
* decipher.auth_tag = tag
1069+
* decipher.auth_tag = tag # could be called at any time before #final
10321070
* decipher.auth_data = auth_data
10331071
*
10341072
* decrypted = decipher.update(encrypted) + decipher.final
10351073
*
10361074
* puts data == decrypted #=> true
1075+
*
1076+
* Note that other AEAD ciphers may require additional steps, such as
1077+
* setting the expected tag length (#auth_tag_len=) or the total data
1078+
* length (#ccm_data_len=) in advance. Make sure to read the relevant man
1079+
* page for details.
10371080
*/
10381081
cCipher = rb_define_class_under(mOSSL, "Cipher", rb_cObject);
10391082
eCipherError = rb_define_class_under(cCipher, "CipherError", eOSSLError);

0 commit comments

Comments
 (0)