mirror of
https://github.com/github/codeql.git
synced 2025-12-21 19:26:31 +01:00
Narrow NonConstantTimeCryptoComparison.ql to timing attack on signatures and MACs only
This commit is contained in:
committed by
Fosstars
parent
c359852608
commit
8b557765b3
@@ -3,16 +3,16 @@
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
When comparing results of cryptographic operations, such as MAC or digital signature,
|
||||
a constant-time algorithm should be used. In other words, the comparison time should not depend on
|
||||
the content of the input. Otherwise, attackers may be able to implement a timing attack
|
||||
if they can control input. A successful timing attack may result in leaking secrets or authentication bypass.
|
||||
A constant-time algorithm should be used for checking a MAC or a digital signature.
|
||||
In other words, the comparison time should not depend on the content of the input.
|
||||
Otherwise, attackers may be able to implement a timing attack if they control inputs.
|
||||
A successful attack may uncover a valid MAC or signature that in turn can result in authentication bypass.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Use <code>MessageDigest.isEqual()</code> method to compare results of cryptographic operations.
|
||||
Use <code>MessageDigest.isEqual()</code> method to check MACs and signatures.
|
||||
If this method is used, then the calculation time depends only on the length of input byte arrays,
|
||||
and does not depend on the contents of the arrays.
|
||||
</p>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/**
|
||||
* @name Using a non-constant-time algorithm for comparing results of a cryptographic operation
|
||||
* @description When comparing results of a cryptographic operation, a constant-time algorithm should be used.
|
||||
* Otherwise, attackers may be able to implement a timing attack if they can control input.
|
||||
* A successful attack may result in leaking secrets or authentication bypass.
|
||||
* @name Using a non-constant-time algorithm for comparing MAC or signature
|
||||
* @description When checking MAC or signature, a constant-time algorithm should be used.
|
||||
* Otherwise, attackers may be able to implement a timing attack if they control inputs.
|
||||
* A successful attack may uncover a valid MAC or signature that in turn can result in authentication bypass.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @id java/non-constant-time-crypto-comparison
|
||||
* @id java/non-constant-time-in-signature-check
|
||||
* @tags security
|
||||
* external/cwe/cwe-208
|
||||
*/
|
||||
@@ -25,6 +25,9 @@ abstract private class ProduceCryptoCall extends MethodAccess {
|
||||
|
||||
/** Return the result of cryptographic operation. */
|
||||
Expr output() { result = output }
|
||||
|
||||
/** Return a type of the result of cryptographic operation such as MAC, signature or ciphertext. */
|
||||
abstract string getResultType();
|
||||
}
|
||||
|
||||
/** A method call that produces a MAC. */
|
||||
@@ -37,6 +40,8 @@ private class ProduceMacCall extends ProduceCryptoCall {
|
||||
getMethod().hasStringSignature("doFinal(byte[], int)") and getArgument(0) = output
|
||||
)
|
||||
}
|
||||
|
||||
override string getResultType() { result = "MAC" }
|
||||
}
|
||||
|
||||
/** A method call that produces a signature. */
|
||||
@@ -49,6 +54,8 @@ private class ProduceSignatureCall extends ProduceCryptoCall {
|
||||
getMethod().hasStringSignature("sign(byte[], int, int)") and getArgument(0) = output
|
||||
)
|
||||
}
|
||||
|
||||
override string getResultType() { result = "signature" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,6 +105,8 @@ private class ProduceCiphertextCall extends ProduceCryptoCall {
|
||||
config.hasFlowTo(DataFlow3::exprNode(this.getQualifier()))
|
||||
)
|
||||
}
|
||||
|
||||
override string getResultType() { result = "ciphertext" }
|
||||
}
|
||||
|
||||
/** Holds if `fromNode` to `toNode` is a dataflow step that updates a cryptographic operation. */
|
||||
@@ -173,13 +182,9 @@ private class UserInputInCryptoOperationConfig extends TaintTracking2::Configura
|
||||
|
||||
/** A source that produces result of cryptographic operation. */
|
||||
private class CryptoOperationSource extends DataFlow::Node {
|
||||
Expr cryptoOperation;
|
||||
ProduceCryptoCall call;
|
||||
|
||||
CryptoOperationSource() {
|
||||
exists(ProduceCryptoCall call | call.output() = this.asExpr() |
|
||||
cryptoOperation = call.getQualifier()
|
||||
)
|
||||
}
|
||||
CryptoOperationSource() { call.output() = this.asExpr() }
|
||||
|
||||
/** Holds if remote user input was used in the cryptographic operation. */
|
||||
predicate includesUserInput() {
|
||||
@@ -188,9 +193,11 @@ private class CryptoOperationSource extends DataFlow::Node {
|
||||
|
|
||||
config.hasFlowPath(source, sink)
|
||||
|
|
||||
sink.getNode().asExpr() = cryptoOperation
|
||||
sink.getNode().asExpr() = call.getQualifier()
|
||||
)
|
||||
}
|
||||
|
||||
ProduceCryptoCall getCall() { result = call }
|
||||
}
|
||||
|
||||
/** Methods that use a non-constant-time algorithm for comparing inputs. */
|
||||
@@ -329,8 +336,8 @@ from DataFlow::PathNode source, DataFlow::PathNode sink, NonConstantTimeCryptoCo
|
||||
where
|
||||
conf.hasFlowPath(source, sink) and
|
||||
(
|
||||
source.getNode().(CryptoOperationSource).includesUserInput() or
|
||||
source.getNode().(CryptoOperationSource).includesUserInput() and
|
||||
sink.getNode().(NonConstantTimeComparisonSink).includesUserInput()
|
||||
)
|
||||
select sink.getNode(), source, sink,
|
||||
"Using a non-constant-time algorithm for comparing results of a cryptographic operation."
|
||||
select sink.getNode(), source, sink, "Using a non-constant-time method for cheching a $@.", source,
|
||||
source.getNode().(CryptoOperationSource).getCall().getResultType()
|
||||
|
||||
@@ -1,50 +1,50 @@
|
||||
edges
|
||||
| NonConstantTimeCryptoComparison.java:20:28:20:44 | doFinal(...) : byte[] | NonConstantTimeCryptoComparison.java:21:43:21:51 | actualMac |
|
||||
| NonConstantTimeCryptoComparison.java:29:28:29:40 | doFinal(...) : byte[] | NonConstantTimeCryptoComparison.java:30:84:30:92 | actualMac : byte[] |
|
||||
| NonConstantTimeCryptoComparison.java:30:84:30:92 | actualMac : byte[] | NonConstantTimeCryptoComparison.java:30:66:30:93 | castToObjectArray(...) |
|
||||
| NonConstantTimeCryptoComparison.java:37:21:37:29 | actualMac : byte[] | NonConstantTimeCryptoComparison.java:39:43:39:51 | actualMac |
|
||||
| NonConstantTimeCryptoComparison.java:57:28:57:40 | sign(...) : byte[] | NonConstantTimeCryptoComparison.java:58:40:58:48 | signature |
|
||||
| NonConstantTimeCryptoComparison.java:68:21:68:29 | signature : byte[] | NonConstantTimeCryptoComparison.java:69:40:69:48 | signature |
|
||||
| NonConstantTimeCryptoComparison.java:87:22:87:41 | doFinal(...) : byte[] | NonConstantTimeCryptoComparison.java:89:45:89:47 | tag |
|
||||
| NonConstantTimeCryptoComparison.java:102:24:102:26 | tag : byte[] | NonConstantTimeCryptoComparison.java:103:40:103:42 | tag |
|
||||
| NonConstantTimeCryptoComparison.java:116:52:116:54 | tag : ByteBuffer | NonConstantTimeCryptoComparison.java:118:40:118:42 | tag : ByteBuffer |
|
||||
| NonConstantTimeCryptoComparison.java:118:40:118:42 | tag : ByteBuffer | NonConstantTimeCryptoComparison.java:118:40:118:50 | array(...) |
|
||||
| NonConstantTimeCryptoComparison.java:128:52:128:54 | tag : ByteBuffer | NonConstantTimeCryptoComparison.java:129:49:129:51 | tag |
|
||||
| NonConstantTimeCryptoComparison.java:146:22:146:46 | doFinal(...) : byte[] | NonConstantTimeCryptoComparison.java:147:40:147:42 | tag |
|
||||
| NonConstantTimeCryptoComparison.java:177:34:177:50 | doFinal(...) : byte[] | NonConstantTimeCryptoComparison.java:180:26:180:36 | computedTag |
|
||||
| NonConstantTimeCryptoComparison.java:21:32:21:48 | doFinal(...) : byte[] | NonConstantTimeCryptoComparison.java:23:47:23:55 | actualMac |
|
||||
| NonConstantTimeCryptoComparison.java:33:32:33:44 | doFinal(...) : byte[] | NonConstantTimeCryptoComparison.java:35:88:35:96 | actualMac : byte[] |
|
||||
| NonConstantTimeCryptoComparison.java:35:88:35:96 | actualMac : byte[] | NonConstantTimeCryptoComparison.java:35:70:35:97 | castToObjectArray(...) |
|
||||
| NonConstantTimeCryptoComparison.java:46:25:46:33 | actualMac : byte[] | NonConstantTimeCryptoComparison.java:48:47:48:55 | actualMac |
|
||||
| NonConstantTimeCryptoComparison.java:71:32:71:44 | sign(...) : byte[] | NonConstantTimeCryptoComparison.java:73:44:73:52 | signature |
|
||||
| NonConstantTimeCryptoComparison.java:85:25:85:33 | signature : byte[] | NonConstantTimeCryptoComparison.java:87:44:87:52 | signature |
|
||||
| NonConstantTimeCryptoComparison.java:111:26:111:45 | doFinal(...) : byte[] | NonConstantTimeCryptoComparison.java:113:49:113:51 | tag |
|
||||
| NonConstantTimeCryptoComparison.java:128:28:128:30 | tag : byte[] | NonConstantTimeCryptoComparison.java:130:44:130:46 | tag |
|
||||
| NonConstantTimeCryptoComparison.java:146:56:146:58 | tag : ByteBuffer | NonConstantTimeCryptoComparison.java:148:44:148:46 | tag : ByteBuffer |
|
||||
| NonConstantTimeCryptoComparison.java:148:44:148:46 | tag : ByteBuffer | NonConstantTimeCryptoComparison.java:148:44:148:54 | array(...) |
|
||||
| NonConstantTimeCryptoComparison.java:160:56:160:58 | tag : ByteBuffer | NonConstantTimeCryptoComparison.java:162:53:162:55 | tag |
|
||||
| NonConstantTimeCryptoComparison.java:185:26:185:50 | doFinal(...) : byte[] | NonConstantTimeCryptoComparison.java:187:44:187:46 | tag |
|
||||
| NonConstantTimeCryptoComparison.java:220:34:220:50 | doFinal(...) : byte[] | NonConstantTimeCryptoComparison.java:223:26:223:36 | computedTag |
|
||||
nodes
|
||||
| NonConstantTimeCryptoComparison.java:20:28:20:44 | doFinal(...) : byte[] | semmle.label | doFinal(...) : byte[] |
|
||||
| NonConstantTimeCryptoComparison.java:21:43:21:51 | actualMac | semmle.label | actualMac |
|
||||
| NonConstantTimeCryptoComparison.java:29:28:29:40 | doFinal(...) : byte[] | semmle.label | doFinal(...) : byte[] |
|
||||
| NonConstantTimeCryptoComparison.java:30:66:30:93 | castToObjectArray(...) | semmle.label | castToObjectArray(...) |
|
||||
| NonConstantTimeCryptoComparison.java:30:84:30:92 | actualMac : byte[] | semmle.label | actualMac : byte[] |
|
||||
| NonConstantTimeCryptoComparison.java:37:21:37:29 | actualMac : byte[] | semmle.label | actualMac : byte[] |
|
||||
| NonConstantTimeCryptoComparison.java:39:43:39:51 | actualMac | semmle.label | actualMac |
|
||||
| NonConstantTimeCryptoComparison.java:57:28:57:40 | sign(...) : byte[] | semmle.label | sign(...) : byte[] |
|
||||
| NonConstantTimeCryptoComparison.java:58:40:58:48 | signature | semmle.label | signature |
|
||||
| NonConstantTimeCryptoComparison.java:68:21:68:29 | signature : byte[] | semmle.label | signature : byte[] |
|
||||
| NonConstantTimeCryptoComparison.java:69:40:69:48 | signature | semmle.label | signature |
|
||||
| NonConstantTimeCryptoComparison.java:87:22:87:41 | doFinal(...) : byte[] | semmle.label | doFinal(...) : byte[] |
|
||||
| NonConstantTimeCryptoComparison.java:89:45:89:47 | tag | semmle.label | tag |
|
||||
| NonConstantTimeCryptoComparison.java:102:24:102:26 | tag : byte[] | semmle.label | tag : byte[] |
|
||||
| NonConstantTimeCryptoComparison.java:103:40:103:42 | tag | semmle.label | tag |
|
||||
| NonConstantTimeCryptoComparison.java:116:52:116:54 | tag : ByteBuffer | semmle.label | tag : ByteBuffer |
|
||||
| NonConstantTimeCryptoComparison.java:118:40:118:42 | tag : ByteBuffer | semmle.label | tag : ByteBuffer |
|
||||
| NonConstantTimeCryptoComparison.java:118:40:118:50 | array(...) | semmle.label | array(...) |
|
||||
| NonConstantTimeCryptoComparison.java:128:52:128:54 | tag : ByteBuffer | semmle.label | tag : ByteBuffer |
|
||||
| NonConstantTimeCryptoComparison.java:129:49:129:51 | tag | semmle.label | tag |
|
||||
| NonConstantTimeCryptoComparison.java:146:22:146:46 | doFinal(...) : byte[] | semmle.label | doFinal(...) : byte[] |
|
||||
| NonConstantTimeCryptoComparison.java:147:40:147:42 | tag | semmle.label | tag |
|
||||
| NonConstantTimeCryptoComparison.java:177:34:177:50 | doFinal(...) : byte[] | semmle.label | doFinal(...) : byte[] |
|
||||
| NonConstantTimeCryptoComparison.java:180:26:180:36 | computedTag | semmle.label | computedTag |
|
||||
| NonConstantTimeCryptoComparison.java:21:32:21:48 | doFinal(...) : byte[] | semmle.label | doFinal(...) : byte[] |
|
||||
| NonConstantTimeCryptoComparison.java:23:47:23:55 | actualMac | semmle.label | actualMac |
|
||||
| NonConstantTimeCryptoComparison.java:33:32:33:44 | doFinal(...) : byte[] | semmle.label | doFinal(...) : byte[] |
|
||||
| NonConstantTimeCryptoComparison.java:35:70:35:97 | castToObjectArray(...) | semmle.label | castToObjectArray(...) |
|
||||
| NonConstantTimeCryptoComparison.java:35:88:35:96 | actualMac : byte[] | semmle.label | actualMac : byte[] |
|
||||
| NonConstantTimeCryptoComparison.java:46:25:46:33 | actualMac : byte[] | semmle.label | actualMac : byte[] |
|
||||
| NonConstantTimeCryptoComparison.java:48:47:48:55 | actualMac | semmle.label | actualMac |
|
||||
| NonConstantTimeCryptoComparison.java:71:32:71:44 | sign(...) : byte[] | semmle.label | sign(...) : byte[] |
|
||||
| NonConstantTimeCryptoComparison.java:73:44:73:52 | signature | semmle.label | signature |
|
||||
| NonConstantTimeCryptoComparison.java:85:25:85:33 | signature : byte[] | semmle.label | signature : byte[] |
|
||||
| NonConstantTimeCryptoComparison.java:87:44:87:52 | signature | semmle.label | signature |
|
||||
| NonConstantTimeCryptoComparison.java:111:26:111:45 | doFinal(...) : byte[] | semmle.label | doFinal(...) : byte[] |
|
||||
| NonConstantTimeCryptoComparison.java:113:49:113:51 | tag | semmle.label | tag |
|
||||
| NonConstantTimeCryptoComparison.java:128:28:128:30 | tag : byte[] | semmle.label | tag : byte[] |
|
||||
| NonConstantTimeCryptoComparison.java:130:44:130:46 | tag | semmle.label | tag |
|
||||
| NonConstantTimeCryptoComparison.java:146:56:146:58 | tag : ByteBuffer | semmle.label | tag : ByteBuffer |
|
||||
| NonConstantTimeCryptoComparison.java:148:44:148:46 | tag : ByteBuffer | semmle.label | tag : ByteBuffer |
|
||||
| NonConstantTimeCryptoComparison.java:148:44:148:54 | array(...) | semmle.label | array(...) |
|
||||
| NonConstantTimeCryptoComparison.java:160:56:160:58 | tag : ByteBuffer | semmle.label | tag : ByteBuffer |
|
||||
| NonConstantTimeCryptoComparison.java:162:53:162:55 | tag | semmle.label | tag |
|
||||
| NonConstantTimeCryptoComparison.java:185:26:185:50 | doFinal(...) : byte[] | semmle.label | doFinal(...) : byte[] |
|
||||
| NonConstantTimeCryptoComparison.java:187:44:187:46 | tag | semmle.label | tag |
|
||||
| NonConstantTimeCryptoComparison.java:220:34:220:50 | doFinal(...) : byte[] | semmle.label | doFinal(...) : byte[] |
|
||||
| NonConstantTimeCryptoComparison.java:223:26:223:36 | computedTag | semmle.label | computedTag |
|
||||
#select
|
||||
| NonConstantTimeCryptoComparison.java:21:43:21:51 | actualMac | NonConstantTimeCryptoComparison.java:20:28:20:44 | doFinal(...) : byte[] | NonConstantTimeCryptoComparison.java:21:43:21:51 | actualMac | Using a non-constant-time algorithm for comparing results of a cryptographic operation. |
|
||||
| NonConstantTimeCryptoComparison.java:30:66:30:93 | castToObjectArray(...) | NonConstantTimeCryptoComparison.java:29:28:29:40 | doFinal(...) : byte[] | NonConstantTimeCryptoComparison.java:30:66:30:93 | castToObjectArray(...) | Using a non-constant-time algorithm for comparing results of a cryptographic operation. |
|
||||
| NonConstantTimeCryptoComparison.java:39:43:39:51 | actualMac | NonConstantTimeCryptoComparison.java:37:21:37:29 | actualMac : byte[] | NonConstantTimeCryptoComparison.java:39:43:39:51 | actualMac | Using a non-constant-time algorithm for comparing results of a cryptographic operation. |
|
||||
| NonConstantTimeCryptoComparison.java:58:40:58:48 | signature | NonConstantTimeCryptoComparison.java:57:28:57:40 | sign(...) : byte[] | NonConstantTimeCryptoComparison.java:58:40:58:48 | signature | Using a non-constant-time algorithm for comparing results of a cryptographic operation. |
|
||||
| NonConstantTimeCryptoComparison.java:69:40:69:48 | signature | NonConstantTimeCryptoComparison.java:68:21:68:29 | signature : byte[] | NonConstantTimeCryptoComparison.java:69:40:69:48 | signature | Using a non-constant-time algorithm for comparing results of a cryptographic operation. |
|
||||
| NonConstantTimeCryptoComparison.java:89:45:89:47 | tag | NonConstantTimeCryptoComparison.java:87:22:87:41 | doFinal(...) : byte[] | NonConstantTimeCryptoComparison.java:89:45:89:47 | tag | Using a non-constant-time algorithm for comparing results of a cryptographic operation. |
|
||||
| NonConstantTimeCryptoComparison.java:103:40:103:42 | tag | NonConstantTimeCryptoComparison.java:102:24:102:26 | tag : byte[] | NonConstantTimeCryptoComparison.java:103:40:103:42 | tag | Using a non-constant-time algorithm for comparing results of a cryptographic operation. |
|
||||
| NonConstantTimeCryptoComparison.java:118:40:118:50 | array(...) | NonConstantTimeCryptoComparison.java:116:52:116:54 | tag : ByteBuffer | NonConstantTimeCryptoComparison.java:118:40:118:50 | array(...) | Using a non-constant-time algorithm for comparing results of a cryptographic operation. |
|
||||
| NonConstantTimeCryptoComparison.java:129:49:129:51 | tag | NonConstantTimeCryptoComparison.java:128:52:128:54 | tag : ByteBuffer | NonConstantTimeCryptoComparison.java:129:49:129:51 | tag | Using a non-constant-time algorithm for comparing results of a cryptographic operation. |
|
||||
| NonConstantTimeCryptoComparison.java:180:26:180:36 | computedTag | NonConstantTimeCryptoComparison.java:177:34:177:50 | doFinal(...) : byte[] | NonConstantTimeCryptoComparison.java:180:26:180:36 | computedTag | Using a non-constant-time algorithm for comparing results of a cryptographic operation. |
|
||||
| NonConstantTimeCryptoComparison.java:23:47:23:55 | actualMac | NonConstantTimeCryptoComparison.java:21:32:21:48 | doFinal(...) : byte[] | NonConstantTimeCryptoComparison.java:23:47:23:55 | actualMac | Using a non-constant-time method for cheching a $@. | NonConstantTimeCryptoComparison.java:21:32:21:48 | doFinal(...) : byte[] | MAC |
|
||||
| NonConstantTimeCryptoComparison.java:35:70:35:97 | castToObjectArray(...) | NonConstantTimeCryptoComparison.java:33:32:33:44 | doFinal(...) : byte[] | NonConstantTimeCryptoComparison.java:35:70:35:97 | castToObjectArray(...) | Using a non-constant-time method for cheching a $@. | NonConstantTimeCryptoComparison.java:33:32:33:44 | doFinal(...) : byte[] | MAC |
|
||||
| NonConstantTimeCryptoComparison.java:48:47:48:55 | actualMac | NonConstantTimeCryptoComparison.java:46:25:46:33 | actualMac : byte[] | NonConstantTimeCryptoComparison.java:48:47:48:55 | actualMac | Using a non-constant-time method for cheching a $@. | NonConstantTimeCryptoComparison.java:46:25:46:33 | actualMac : byte[] | MAC |
|
||||
| NonConstantTimeCryptoComparison.java:73:44:73:52 | signature | NonConstantTimeCryptoComparison.java:71:32:71:44 | sign(...) : byte[] | NonConstantTimeCryptoComparison.java:73:44:73:52 | signature | Using a non-constant-time method for cheching a $@. | NonConstantTimeCryptoComparison.java:71:32:71:44 | sign(...) : byte[] | signature |
|
||||
| NonConstantTimeCryptoComparison.java:87:44:87:52 | signature | NonConstantTimeCryptoComparison.java:85:25:85:33 | signature : byte[] | NonConstantTimeCryptoComparison.java:87:44:87:52 | signature | Using a non-constant-time method for cheching a $@. | NonConstantTimeCryptoComparison.java:85:25:85:33 | signature : byte[] | signature |
|
||||
| NonConstantTimeCryptoComparison.java:113:49:113:51 | tag | NonConstantTimeCryptoComparison.java:111:26:111:45 | doFinal(...) : byte[] | NonConstantTimeCryptoComparison.java:113:49:113:51 | tag | Using a non-constant-time method for cheching a $@. | NonConstantTimeCryptoComparison.java:111:26:111:45 | doFinal(...) : byte[] | ciphertext |
|
||||
| NonConstantTimeCryptoComparison.java:130:44:130:46 | tag | NonConstantTimeCryptoComparison.java:128:28:128:30 | tag : byte[] | NonConstantTimeCryptoComparison.java:130:44:130:46 | tag | Using a non-constant-time method for cheching a $@. | NonConstantTimeCryptoComparison.java:128:28:128:30 | tag : byte[] | ciphertext |
|
||||
| NonConstantTimeCryptoComparison.java:148:44:148:54 | array(...) | NonConstantTimeCryptoComparison.java:146:56:146:58 | tag : ByteBuffer | NonConstantTimeCryptoComparison.java:148:44:148:54 | array(...) | Using a non-constant-time method for cheching a $@. | NonConstantTimeCryptoComparison.java:146:56:146:58 | tag : ByteBuffer | ciphertext |
|
||||
| NonConstantTimeCryptoComparison.java:162:53:162:55 | tag | NonConstantTimeCryptoComparison.java:160:56:160:58 | tag : ByteBuffer | NonConstantTimeCryptoComparison.java:162:53:162:55 | tag | Using a non-constant-time method for cheching a $@. | NonConstantTimeCryptoComparison.java:160:56:160:58 | tag : ByteBuffer | ciphertext |
|
||||
| NonConstantTimeCryptoComparison.java:187:44:187:46 | tag | NonConstantTimeCryptoComparison.java:185:26:185:50 | doFinal(...) : byte[] | NonConstantTimeCryptoComparison.java:187:44:187:46 | tag | Using a non-constant-time method for cheching a $@. | NonConstantTimeCryptoComparison.java:185:26:185:50 | doFinal(...) : byte[] | ciphertext |
|
||||
|
||||
@@ -13,74 +13,98 @@ import javax.crypto.Mac;
|
||||
public class NonConstantTimeCryptoComparison {
|
||||
|
||||
// BAD: compare MACs using a non-constant-time method
|
||||
public boolean unsafeMacCheckWithArrayEquals(byte[] expectedMac, Socket socket) throws Exception {
|
||||
public boolean unsafeMacCheckWithArrayEquals(Socket socket) throws Exception {
|
||||
try (InputStream is = socket.getInputStream()) {
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
byte[] data = new byte[1024];
|
||||
socket.getInputStream().read(data);
|
||||
is.read(data);
|
||||
byte[] actualMac = mac.doFinal(data);
|
||||
byte[] expectedMac = is.readNBytes(32);
|
||||
return Arrays.equals(expectedMac, actualMac);
|
||||
}
|
||||
}
|
||||
|
||||
// BAD: compare MACs using a non-constant-time method
|
||||
public boolean unsafeMacCheckWithArraysDeepEquals(byte[] expectedMac, Socket socket) throws Exception {
|
||||
public boolean unsafeMacCheckWithArraysDeepEquals(Socket socket) throws Exception {
|
||||
try (InputStream is = socket.getInputStream()) {
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
byte[] data = socket.getInputStream().readAllBytes();
|
||||
mac.update(data);
|
||||
byte[] actualMac = mac.doFinal();
|
||||
byte[] expectedMac = is.readNBytes(32);
|
||||
return Arrays.deepEquals(castToObjectArray(expectedMac), castToObjectArray(actualMac));
|
||||
}
|
||||
}
|
||||
|
||||
// BAD: compare MACs using a non-constant-time method
|
||||
public boolean unsafeMacCheckWithDoFinalWithOutputArray(byte[] data, Socket socket) throws Exception {
|
||||
public boolean unsafeMacCheckWithDoFinalWithOutputArray(Socket socket) throws Exception {
|
||||
try (InputStream is = socket.getInputStream()) {
|
||||
byte[] data = is.readNBytes(100);
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
byte[] actualMac = new byte[256];
|
||||
mac.update(data);
|
||||
mac.doFinal(actualMac, 0);
|
||||
byte[] expectedMac = socket.getInputStream().readNBytes(256);
|
||||
return Arrays.equals(expectedMac, actualMac);
|
||||
}
|
||||
}
|
||||
|
||||
// GOOD: compare MACs using a constant-time method
|
||||
public boolean saferMacCheck(byte[] expectedMac, Socket socket) throws Exception {
|
||||
public boolean saferMacCheck(Socket socket) throws Exception {
|
||||
try (InputStream is = socket.getInputStream()) {
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
byte[] data = new byte[1024];
|
||||
socket.getInputStream().read(data);
|
||||
is.read(data);
|
||||
byte[] actualMac = mac.doFinal(data);
|
||||
byte[] expectedMac = is.readNBytes(32);
|
||||
return MessageDigest.isEqual(expectedMac, actualMac);
|
||||
}
|
||||
}
|
||||
|
||||
// BAD: compare signatures using a non-constant-time method
|
||||
public boolean unsafeCheckSignatures(byte[] expected, Socket socket, PrivateKey key) throws Exception {
|
||||
public boolean unsafeCheckSignatures(Socket socket, PrivateKey key) throws Exception {
|
||||
try (InputStream is = socket.getInputStream()) {
|
||||
Signature engine = Signature.getInstance("SHA256withRSA");
|
||||
engine.initSign(key);
|
||||
byte[] data = socket.getInputStream().readAllBytes();
|
||||
engine.update(data);
|
||||
byte[] signature = engine.sign();
|
||||
byte[] expected = is.readNBytes(256);
|
||||
return Arrays.equals(expected, signature);
|
||||
}
|
||||
}
|
||||
|
||||
// BAD: compare signatures using a non-constant-time method
|
||||
public boolean unsafeCheckSignaturesWithOutputArray(byte[] expected, Socket socket, PrivateKey key) throws Exception {
|
||||
public boolean unsafeCheckSignaturesWithOutputArray(Socket socket, PrivateKey key) throws Exception {
|
||||
try (InputStream is = socket.getInputStream()) {
|
||||
Signature engine = Signature.getInstance("SHA256withRSA");
|
||||
engine.initSign(key);
|
||||
byte[] data = socket.getInputStream().readAllBytes();
|
||||
engine.update(data);
|
||||
byte[] signature = new byte[1024];
|
||||
engine.sign(signature, 0, 1024);
|
||||
byte[] expected = is.readNBytes(256);
|
||||
return Arrays.equals(expected, signature);
|
||||
}
|
||||
}
|
||||
|
||||
// GOOD: compare signatures using a constant-time method
|
||||
public boolean saferCheckSignatures(byte[] expected, Socket socket, PrivateKey key) throws Exception {
|
||||
public boolean saferCheckSignatures(Socket socket, PrivateKey key) throws Exception {
|
||||
try (InputStream is = socket.getInputStream()) {
|
||||
Signature engine = Signature.getInstance("SHA256withRSA");
|
||||
engine.initSign(key);
|
||||
byte[] data = socket.getInputStream().readAllBytes();
|
||||
engine.update(data);
|
||||
byte[] signature = engine.sign();
|
||||
byte[] expected = is.readNBytes(256);
|
||||
return MessageDigest.isEqual(expected, signature);
|
||||
}
|
||||
}
|
||||
|
||||
// BAD: compare ciphertexts (custom MAC) using a non-constant-time method
|
||||
public boolean unsafeCheckCiphertext(Socket socket, byte[] plaintext, Key key) throws Exception {
|
||||
public boolean unsafeCheckCiphertext(Socket socket, Key key) throws Exception {
|
||||
try (InputStream is = socket.getInputStream()) {
|
||||
byte[] plaintext = is.readNBytes(100);
|
||||
byte[] hash = MessageDigest.getInstance("SHA-256").digest(plaintext);
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key);
|
||||
@@ -88,9 +112,11 @@ public class NonConstantTimeCryptoComparison {
|
||||
byte[] expected = socket.getInputStream().readAllBytes();
|
||||
return Objects.deepEquals(expected, tag);
|
||||
}
|
||||
}
|
||||
|
||||
// BAD: compare ciphertexts (custom MAC) using a non-constant-time method
|
||||
public boolean unsafeCheckCiphertextWithOutputArray(byte[] expected, Socket socket, Key key) throws Exception {
|
||||
public boolean unsafeCheckCiphertextWithOutputArray(Socket socket, Key key) throws Exception {
|
||||
try (InputStream is = socket.getInputStream()) {
|
||||
byte[] plaintext = socket.getInputStream().readAllBytes();
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-512");
|
||||
md.update(plaintext);
|
||||
@@ -100,11 +126,15 @@ public class NonConstantTimeCryptoComparison {
|
||||
cipher.update(hash);
|
||||
byte[] tag = new byte[1024];
|
||||
cipher.doFinal(tag, 0);
|
||||
byte[] expected = is.readNBytes(32);
|
||||
return Arrays.equals(expected, tag);
|
||||
}
|
||||
}
|
||||
|
||||
// BAD: compare ciphertexts (custom MAC) using a non-constant-time method
|
||||
public boolean unsafeCheckCiphertextWithByteBuffer(Socket socket, byte[] plaintext, Key key) throws Exception {
|
||||
public boolean unsafeCheckCiphertextWithByteBuffer(Socket socket, Key key) throws Exception {
|
||||
try (InputStream is = socket.getInputStream()) {
|
||||
byte[] plaintext = is.readNBytes(300);
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-512");
|
||||
md.update(plaintext);
|
||||
byte[] hash = new byte[1024];
|
||||
@@ -117,20 +147,26 @@ public class NonConstantTimeCryptoComparison {
|
||||
byte[] expected = socket.getInputStream().readNBytes(1024);
|
||||
return Arrays.equals(expected, tag.array());
|
||||
}
|
||||
}
|
||||
|
||||
// BAD: compare ciphertexts (custom MAC) using a non-constant-time method
|
||||
public boolean unsafeCheckCiphertextWithByteBufferEquals(byte[] expected, Socket socket, Key key) throws Exception {
|
||||
public boolean unsafeCheckCiphertextWithByteBufferEquals(Socket socket, Key key) throws Exception {
|
||||
try (InputStream is = socket.getInputStream()) {
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key);
|
||||
byte[] plaintext = socket.getInputStream().readAllBytes();
|
||||
cipher.update(plaintext);
|
||||
ByteBuffer tag = ByteBuffer.wrap(new byte[1024]);
|
||||
cipher.doFinal(ByteBuffer.wrap(plaintext), tag);
|
||||
byte[] expected = is.readNBytes(32);
|
||||
return ByteBuffer.wrap(expected).equals(tag);
|
||||
}
|
||||
}
|
||||
|
||||
// GOOD: compare ciphertexts (custom MAC) using a constant-time method
|
||||
public boolean saferCheckCiphertext(Socket socket, byte[] plaintext, Key key) throws Exception {
|
||||
public boolean saferCheckCiphertext(Socket socket, Key key) throws Exception {
|
||||
try (InputStream is = socket.getInputStream()) {
|
||||
byte[] plaintext = is.readNBytes(200);
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key);
|
||||
byte[] hash = MessageDigest.getInstance("SHA-256").digest(plaintext);
|
||||
@@ -138,23 +174,30 @@ public class NonConstantTimeCryptoComparison {
|
||||
byte[] expected = socket.getInputStream().readAllBytes();
|
||||
return MessageDigest.isEqual(expected, tag);
|
||||
}
|
||||
}
|
||||
|
||||
// GOOD: compare ciphertexts using a constant-time method, but no user input
|
||||
public boolean noUserInputWhenCheckingCiphertext(byte[] expected, byte[] plaintext, Key key) throws Exception {
|
||||
public boolean noUserInputWhenCheckingCiphertext(Socket socket, Key key) throws Exception {
|
||||
try (InputStream is = socket.getInputStream()) {
|
||||
byte[] plaintext = is.readNBytes(100);
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key);
|
||||
byte[] tag = cipher.doFinal(plaintext);
|
||||
byte[] expected = is.readNBytes(32);
|
||||
return Arrays.equals(expected, tag);
|
||||
}
|
||||
}
|
||||
|
||||
// GOOD: compare MAC with constant using a constant-time method
|
||||
public boolean compareMacWithConstant(Socket socket) throws Exception {
|
||||
try (InputStream is = socket.getInputStream()) {
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
byte[] data = new byte[1024];
|
||||
socket.getInputStream().read(data);
|
||||
byte[] actualMac = mac.doFinal(data);
|
||||
return "constant".equals(new String(actualMac));
|
||||
}
|
||||
}
|
||||
|
||||
private static Object[] castToObjectArray(byte[] array) {
|
||||
Object[] result = new Object[array.length];
|
||||
|
||||
Reference in New Issue
Block a user