This is an automated email from the ASF dual-hosted git repository. lgoldstein pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mina-sshd.git
The following commit(s) were added to refs/heads/master by this push: new 37ffa46 [SSHD-1004] Using a more constant time MAC validation to minimize timing side channel information leak 37ffa46 is described below commit 37ffa46e5bcf6ded8be3dddfd66fc7a815c1c40e Author: Lyor Goldstein <lgoldst...@apache.org> AuthorDate: Mon Aug 10 18:09:04 2020 +0300 [SSHD-1004] Using a more constant time MAC validation to minimize timing side channel information leak --- CHANGES.md | 1 + .../main/java/org/apache/sshd/common/mac/Mac.java | 28 ++++++++++++++++++++++ .../sshd/common/util/buffer/BufferUtils.java | 20 ++++++++++++++++ .../common/session/helpers/AbstractSession.java | 2 +- 4 files changed, 50 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 0dbba59..bd4bae8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -20,6 +20,7 @@ or `-key-file` command line option. ## Minor code helpers +* [SSHD-1004](https://issues.apache.org/jira/browse/SSHD-1004) Using a more constant time MAC validation to minimize timing side channel information leak. * [SSHD-1030](https://issues.apache.org/jira/browse/SSHD-1030) Added a NoneFileSystemFactory implementation * [SSHD-1042](https://issues.apache.org/jira/browse/SSHD-1042) Added more callbacks to SftpEventListener * [SSHD-1040](https://issues.apache.org/jira/browse/SSHD-1040) Make server key available after KEX completed. diff --git a/sshd-common/src/main/java/org/apache/sshd/common/mac/Mac.java b/sshd-common/src/main/java/org/apache/sshd/common/mac/Mac.java index 3af44a7..f5ff7c4 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/mac/Mac.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/mac/Mac.java @@ -48,4 +48,32 @@ public interface Mac extends MacInformation { } void doFinal(byte[] buf, int offset) throws Exception; + + /* + * Executes a more-or-less constant time verification in order + * to avoid timing side-channel information leak + */ + static boolean equals(byte[] a1, int a1Offset, byte[] a2, int a2Offset, int length) { + int len1 = NumberUtils.length(a1); + int len2 = NumberUtils.length(a2); + int result = 0; + + if (len1 < (a1Offset + length)) { + length = Math.min(length, len1 - a1Offset); + length = Math.max(length, 0); + result |= 0x00FF; + } + + if (len2 < (a2Offset + length)) { + length = Math.min(length, len2 - a2Offset); + length = Math.max(length, 0); + result |= 0xFF00; + } + + for (int cmpLen = length; cmpLen > 0; a1Offset++, a2Offset++, cmpLen--) { + result |= a1[a1Offset] ^ a2[a2Offset]; + } + + return result == 0; + } } diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java index a7e3a59..be67be0 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java @@ -522,6 +522,14 @@ public final class BufferUtils { return Long.BYTES; } + /** + * Compares the contents of 2 arrays of bytes - <B>Note:</B> do not use it to execute security related comparisons + * (e.g. MACs) since the method leaks timing information. Use {@code Mac#equals} method instead. + * + * @param a1 1st array + * @param a2 2nd array + * @return {@code true} if all bytes in the compared arrays are equal + */ public static boolean equals(byte[] a1, byte[] a2) { int len1 = NumberUtils.length(a1); int len2 = NumberUtils.length(a2); @@ -532,6 +540,18 @@ public final class BufferUtils { } } + /** + * Compares the contents of 2 arrays of bytes - <B>Note:</B> do not use it to execute security related comparisons + * (e.g. MACs) since the method leaks timing information. Use {@code Mac#equals} method instead. + * + * @param a1 1st array + * @param a1Offset Offset to start comparing in 1st array + * @param a2 2nd array + * @param a2Offset Offset to start comparing in 2nd array + * @param length Number of bytes to compare + * @return {@code true} if all bytes in the compared arrays are equal when compared from the specified + * offsets and up to specified length + */ @SuppressWarnings("PMD.AssignmentInOperand") public static boolean equals(byte[] a1, int a1Offset, byte[] a2, int a2Offset, int length) { int len1 = NumberUtils.length(a1); diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java index dcc9f40..9509d09 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java @@ -1456,7 +1456,7 @@ public abstract class AbstractSession extends SessionHelper { inMac.doFinal(inMacResult, 0); // Check the computed result with the received mac (just after the packet data) - if (!BufferUtils.equals(inMacResult, 0, data, offset + len, inMacSize)) { + if (!Mac.equals(inMacResult, 0, data, offset + len, inMacSize)) { throw new SshException(SshConstants.SSH2_DISCONNECT_MAC_ERROR, "MAC Error"); } }