Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
crypto: add support for OCB mode for AEAD
  • Loading branch information
tniessen committed Jul 17, 2018
commit bbc8da2366e23cd6e9852f0c05b9aa6a85b52c9f
48 changes: 29 additions & 19 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2683,6 +2683,9 @@ void CipherBase::Init(const FunctionCallbackInfo<Value>& args) {
cipher->Init(*cipher_type, key_buf, key_buf_len, auth_tag_len);
}

#define IS_SUPPORTED_AUTHENTICATED_MODE(mode) ((mode) == EVP_CIPH_CCM_MODE || \
(mode) == EVP_CIPH_GCM_MODE || \
(mode) == EVP_CIPH_OCB_MODE)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not make this a function?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mhhh I think in my head it still looks like "less overhead". Would you like me to change it?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, please.


void CipherBase::InitIv(const char* cipher_type,
const char* key,
Expand All @@ -2700,8 +2703,7 @@ void CipherBase::InitIv(const char* cipher_type,

const int expected_iv_len = EVP_CIPHER_iv_length(cipher);
const int mode = EVP_CIPHER_mode(cipher);
const bool is_gcm_mode = (EVP_CIPH_GCM_MODE == mode);
const bool is_ccm_mode = (EVP_CIPH_CCM_MODE == mode);
const bool is_authenticated_mode = IS_SUPPORTED_AUTHENTICATED_MODE(mode);
const bool has_iv = iv_len >= 0;

// Throw if no IV was passed and the cipher requires an IV
Expand All @@ -2712,7 +2714,7 @@ void CipherBase::InitIv(const char* cipher_type,
}

// Throw if an IV was passed which does not match the cipher's fixed IV length
if (!is_gcm_mode && !is_ccm_mode && has_iv && iv_len != expected_iv_len) {
if (!is_authenticated_mode && has_iv && iv_len != expected_iv_len) {
return env()->ThrowError("Invalid IV length");
}

Expand All @@ -2728,7 +2730,7 @@ void CipherBase::InitIv(const char* cipher_type,
"Failed to initialize cipher");
}

if (IsAuthenticatedMode()) {
if (is_authenticated_mode) {
CHECK(has_iv);
if (!InitAuthenticated(cipher_type, iv_len, auth_tag_len))
return;
Expand Down Expand Up @@ -2803,7 +2805,7 @@ bool CipherBase::InitAuthenticated(const char* cipher_type, int iv_len,
}

const int mode = EVP_CIPHER_CTX_mode(ctx_.get());
if (mode == EVP_CIPH_CCM_MODE) {
if (mode == EVP_CIPH_CCM_MODE || mode == EVP_CIPH_OCB_MODE) {
if (auth_tag_len == kNoAuthTagLength) {
char msg[128];
snprintf(msg, sizeof(msg), "authTagLength required for %s", cipher_type);
Expand All @@ -2813,25 +2815,29 @@ bool CipherBase::InitAuthenticated(const char* cipher_type, int iv_len,

#ifdef NODE_FIPS_MODE
// TODO(tniessen) Support CCM decryption in FIPS mode
if (kind_ == kDecipher && FIPS_mode()) {
if (mode == EVP_CIPH_CCM_MODE && kind_ == kDecipher && FIPS_mode()) {
env()->ThrowError("CCM decryption not supported in FIPS mode");
return false;
}
#endif

if (!EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_CCM_SET_TAG, auth_tag_len,
// Tell OpenSSL about the desired length.
if (!EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_AEAD_SET_TAG, auth_tag_len,
nullptr)) {
env()->ThrowError("Invalid authentication tag length");
return false;
}

// Remember the given authentication tag length for later.
auth_tag_len_ = auth_tag_len;

// Restrict the message length to min(INT_MAX, 2^(8*(15-iv_len))-1) bytes.
CHECK(iv_len >= 7 && iv_len <= 13);
max_message_size_ = INT_MAX;
if (iv_len == 12) max_message_size_ = 16777215;
if (iv_len == 13) max_message_size_ = 65535;
if (mode == EVP_CIPH_CCM_MODE) {
// Restrict the message length to min(INT_MAX, 2^(8*(15-iv_len))-1) bytes.
CHECK(iv_len >= 7 && iv_len <= 13);
max_message_size_ = INT_MAX;
if (iv_len == 12) max_message_size_ = 16777215;
if (iv_len == 13) max_message_size_ = 65535;
}
} else {
CHECK_EQ(mode, EVP_CIPH_GCM_MODE);

Expand Down Expand Up @@ -2870,7 +2876,7 @@ bool CipherBase::IsAuthenticatedMode() const {
// Check if this cipher operates in an AEAD mode that we support.
CHECK(ctx_);
const int mode = EVP_CIPHER_CTX_mode(ctx_.get());
return mode == EVP_CIPH_GCM_MODE || mode == EVP_CIPH_CCM_MODE;
return IS_SUPPORTED_AUTHENTICATED_MODE(mode);
}


Expand Down Expand Up @@ -2903,16 +2909,18 @@ void CipherBase::SetAuthTag(const FunctionCallbackInfo<Value>& args) {
return args.GetReturnValue().Set(false);
}

// Restrict GCM tag lengths according to NIST 800-38d, page 9.
unsigned int tag_len = Buffer::Length(args[0]);
const int mode = EVP_CIPHER_CTX_mode(cipher->ctx_.get());
bool is_valid;
if (mode == EVP_CIPH_GCM_MODE) {
// Restrict GCM tag lengths according to NIST 800-38d, page 9.
is_valid = (cipher->auth_tag_len_ == kNoAuthTagLength ||
cipher->auth_tag_len_ == tag_len) &&
IsValidGCMTagLength(tag_len);
} else {
CHECK_EQ(mode, EVP_CIPH_CCM_MODE);
// At this point, the tag length is already known and must match the
// length of the given authentication tag.
CHECK(mode == EVP_CIPH_CCM_MODE || mode == EVP_CIPH_OCB_MODE);
CHECK_NE(cipher->auth_tag_len_, kNoAuthTagLength);
is_valid = cipher->auth_tag_len_ == tag_len;
}
Expand Down Expand Up @@ -3008,7 +3016,7 @@ CipherBase::UpdateResult CipherBase::Update(const char* data,
if (kind_ == kDecipher && IsAuthenticatedMode() && auth_tag_len_ > 0 &&
auth_tag_len_ != kNoAuthTagLength && !auth_tag_set_) {
CHECK(EVP_CIPHER_CTX_ctrl(ctx_.get(),
EVP_CTRL_GCM_SET_TAG,
EVP_CTRL_AEAD_SET_TAG,
auth_tag_len_,
reinterpret_cast<unsigned char*>(auth_tag_)));
auth_tag_set_ = true;
Expand Down Expand Up @@ -3121,10 +3129,12 @@ bool CipherBase::Final(unsigned char** out, int* out_len) {

if (ok && kind_ == kCipher && IsAuthenticatedMode()) {
// In GCM mode, the authentication tag length can be specified in advance,
// but defaults to 16 bytes when encrypting. In CCM mode, it must always
// be given by the user.
if (mode == EVP_CIPH_GCM_MODE && auth_tag_len_ == kNoAuthTagLength)
// but defaults to 16 bytes when encrypting. In CCM and OCB mode, it must
// always be given by the user.
if (auth_tag_len_ == kNoAuthTagLength) {
CHECK(mode == EVP_CIPH_GCM_MODE);
auth_tag_len_ = sizeof(auth_tag_);
}
CHECK_EQ(1, EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_AEAD_GET_TAG,
auth_tag_len_,
reinterpret_cast<unsigned char*>(auth_tag_)));
Expand Down
Loading