@@ -47,6 +47,145 @@ bool IsSupportedAuthenticatedMode(const EVP_CIPHER_CTX* ctx) {
4747bool IsValidGCMTagLength (unsigned int tag_len) {
4848 return tag_len == 4 || tag_len == 8 || (tag_len >= 12 && tag_len <= 16 );
4949}
50+
51+ // Collects and returns information on the given cipher
52+ void GetCipherInfo (const FunctionCallbackInfo<Value>& args) {
53+ Environment* env = Environment::GetCurrent (args);
54+ CHECK (args[0 ]->IsObject ());
55+ Local<Object> info = args[0 ].As <Object>();
56+
57+ CHECK (args[1 ]->IsString () || args[1 ]->IsInt32 ());
58+
59+ const EVP_CIPHER* cipher;
60+ if (args[1 ]->IsString ()) {
61+ Utf8Value name (env->isolate (), args[1 ]);
62+ cipher = EVP_get_cipherbyname (*name);
63+ } else {
64+ int nid = args[1 ].As <Int32>()->Value ();
65+ cipher = EVP_get_cipherbyname (OBJ_nid2sn (nid));
66+ }
67+
68+ if (cipher == nullptr )
69+ return ;
70+
71+ int mode = EVP_CIPHER_mode (cipher);
72+ int iv_length = EVP_CIPHER_iv_length (cipher);
73+ int key_length = EVP_CIPHER_key_length (cipher);
74+ int block_length = EVP_CIPHER_block_size (cipher);
75+ const char * mode_label = nullptr ;
76+ switch (mode) {
77+ case EVP_CIPH_CBC_MODE: mode_label = " cbc" ; break ;
78+ case EVP_CIPH_CCM_MODE: mode_label = " ccm" ; break ;
79+ case EVP_CIPH_CFB_MODE: mode_label = " cfb" ; break ;
80+ case EVP_CIPH_CTR_MODE: mode_label = " ctr" ; break ;
81+ case EVP_CIPH_ECB_MODE: mode_label = " ecb" ; break ;
82+ case EVP_CIPH_GCM_MODE: mode_label = " gcm" ; break ;
83+ case EVP_CIPH_OCB_MODE: mode_label = " ocb" ; break ;
84+ case EVP_CIPH_OFB_MODE: mode_label = " ofb" ; break ;
85+ case EVP_CIPH_WRAP_MODE: mode_label = " wrap" ; break ;
86+ case EVP_CIPH_XTS_MODE: mode_label = " xts" ; break ;
87+ case EVP_CIPH_STREAM_CIPHER: mode_label = " stream" ; break ;
88+ }
89+
90+ // If the testKeyLen and testIvLen arguments are specified,
91+ // then we will make an attempt to see if they are usable for
92+ // the cipher in question, returning undefined if they are not.
93+ // If they are, the info object will be returned with the values
94+ // given.
95+ if (args[2 ]->IsInt32 () || args[3 ]->IsInt32 ()) {
96+ // Test and input IV or key length to determine if it's acceptable.
97+ // If it is, then the getCipherInfo will succeed with the given
98+ // values.
99+ CipherCtxPointer ctx (EVP_CIPHER_CTX_new ());
100+ if (!EVP_CipherInit_ex (ctx.get (), cipher, nullptr , nullptr , nullptr , 1 ))
101+ return ;
102+
103+ if (args[2 ]->IsInt32 ()) {
104+ int check_len = args[2 ].As <Int32>()->Value ();
105+ if (!EVP_CIPHER_CTX_set_key_length (ctx.get (), check_len))
106+ return ;
107+ key_length = check_len;
108+ }
109+
110+ if (args[3 ]->IsInt32 ()) {
111+ int check_len = args[3 ].As <Int32>()->Value ();
112+ // For CCM modes, the IV may be between 7 and 13 bytes.
113+ // For GCM and OCB modes, we'll check by attempting to
114+ // set the value. For everything else, just check that
115+ // check_len == iv_length.
116+ switch (mode) {
117+ case EVP_CIPH_CCM_MODE:
118+ if (check_len < 7 || check_len > 13 )
119+ return ;
120+ break ;
121+ case EVP_CIPH_GCM_MODE:
122+ // Fall through
123+ case EVP_CIPH_OCB_MODE:
124+ if (!EVP_CIPHER_CTX_ctrl (
125+ ctx.get (),
126+ EVP_CTRL_AEAD_SET_IVLEN,
127+ check_len,
128+ nullptr )) {
129+ return ;
130+ }
131+ break ;
132+ default :
133+ if (check_len != iv_length)
134+ return ;
135+ }
136+ iv_length = check_len;
137+ }
138+ }
139+
140+ if (mode_label != nullptr &&
141+ info->Set (
142+ env->context (),
143+ FIXED_ONE_BYTE_STRING (env->isolate (), " mode" ),
144+ OneByteString (env->isolate (), mode_label)).IsNothing ()) {
145+ return ;
146+ }
147+
148+ if (info->Set (
149+ env->context (),
150+ env->name_string (),
151+ OneByteString (env->isolate (), EVP_CIPHER_name (cipher))).IsNothing ()) {
152+ return ;
153+ }
154+
155+ if (info->Set (
156+ env->context (),
157+ FIXED_ONE_BYTE_STRING (env->isolate (), " nid" ),
158+ Int32::New (env->isolate (), EVP_CIPHER_nid (cipher))).IsNothing ()) {
159+ return ;
160+ }
161+
162+ // Stream ciphers do not have a meaningful block size
163+ if (mode != EVP_CIPH_STREAM_CIPHER &&
164+ info->Set (
165+ env->context (),
166+ FIXED_ONE_BYTE_STRING (env->isolate (), " blockSize" ),
167+ Int32::New (env->isolate (), block_length)).IsNothing ()) {
168+ return ;
169+ }
170+
171+ // Ciphers that do not use an IV shouldn't report a length
172+ if (iv_length != 0 &&
173+ info->Set (
174+ env->context (),
175+ FIXED_ONE_BYTE_STRING (env->isolate (), " ivLength" ),
176+ Int32::New (env->isolate (), iv_length)).IsNothing ()) {
177+ return ;
178+ }
179+
180+ if (info->Set (
181+ env->context (),
182+ FIXED_ONE_BYTE_STRING (env->isolate (), " keyLength" ),
183+ Int32::New (env->isolate (), key_length)).IsNothing ()) {
184+ return ;
185+ }
186+
187+ args.GetReturnValue ().Set (info);
188+ }
50189} // namespace
51190
52191void CipherBase::GetSSLCiphers (const FunctionCallbackInfo<Value>& args) {
@@ -151,6 +290,8 @@ void CipherBase::Initialize(Environment* env, Local<Object> target) {
151290 EVP_PKEY_verify_recover_init,
152291 EVP_PKEY_verify_recover>);
153292
293+ env->SetMethodNoSideEffect (target, " getCipherInfo" , GetCipherInfo);
294+
154295 NODE_DEFINE_CONSTANT (target, kWebCryptoCipherEncrypt );
155296 NODE_DEFINE_CONSTANT (target, kWebCryptoCipherDecrypt );
156297}
0 commit comments