-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add CMAC to JSS #259
Add CMAC to JSS #259
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
package org.mozilla.jss.crypto; | ||
|
||
import java.security.NoSuchAlgorithmException; | ||
import java.util.Hashtable; | ||
|
||
import org.mozilla.jss.asn1.OBJECT_IDENTIFIER; | ||
|
||
/** | ||
* Algorithms for performing CMACs. These can be used to create | ||
* MessageDigests. | ||
*/ | ||
public class CMACAlgorithm extends DigestAlgorithm { | ||
|
||
protected CMACAlgorithm(int oidIndex, String name, OBJECT_IDENTIFIER oid, | ||
int outputSize) { | ||
super(oidIndex, name, oid, outputSize); | ||
|
||
if (oid != null && oidMap.get(oid) == null) { | ||
oidMap.put(oid, this); | ||
} | ||
} | ||
|
||
/////////////////////////////////////////////////////////////////////// | ||
// OID mapping | ||
/////////////////////////////////////////////////////////////////////// | ||
private static Hashtable<OBJECT_IDENTIFIER, CMACAlgorithm> oidMap = new Hashtable<>(); | ||
|
||
/** | ||
* Looks up the CMAC algorithm with the given OID. | ||
* | ||
* @param oid OID. | ||
* @return CMAC algorithm. | ||
* @exception NoSuchAlgorithmException If no registered CMAC algorithm | ||
* has the given OID. | ||
*/ | ||
public static CMACAlgorithm fromOID(OBJECT_IDENTIFIER oid) | ||
throws NoSuchAlgorithmException | ||
{ | ||
CMACAlgorithm alg = oidMap.get(oid); | ||
if (alg == null) { | ||
throw new NoSuchAlgorithmException("No such algorithm for OID: " + oid); | ||
} | ||
|
||
return alg; | ||
} | ||
|
||
/** | ||
* CMAC AES-X. This is a Message Authentication Code that uses a | ||
* symmetric key together with the AES cipher to create a form of | ||
* signature. | ||
cipherboy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* | ||
* Note that we pass null for the OID here: neither NIST nor any other | ||
* standards body has defined an OID for use with CMAC. Since we use | ||
* a PKCS#11 backend and NSS doesn't otherwise define CMAC based on a | ||
* SEC OID, we don't strictly need one. | ||
* | ||
* We've left the fromOID code (and oid parameter in the constructor) as | ||
* other projects use them for HMACAlgorith. At such time as an OID is | ||
* defined, it can be added here. | ||
*/ | ||
public static final CMACAlgorithm AES = new CMACAlgorithm(CKM_AES_CMAC, "AES-CMAC", null, 16); | ||
public static final CMACAlgorithm AES128 = AES; | ||
public static final CMACAlgorithm AES192 = AES; | ||
public static final CMACAlgorithm AES256 = AES; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,7 +11,9 @@ | |
import java.security.NoSuchAlgorithmException; | ||
import java.security.spec.AlgorithmParameterSpec; | ||
|
||
import org.mozilla.jss.crypto.CMACAlgorithm; | ||
import org.mozilla.jss.crypto.CryptoToken; | ||
import org.mozilla.jss.crypto.DigestAlgorithm; | ||
import org.mozilla.jss.crypto.HMACAlgorithm; | ||
import org.mozilla.jss.crypto.JSSMessageDigest; | ||
import org.mozilla.jss.crypto.SecretKeyFacade; | ||
|
@@ -21,9 +23,9 @@ | |
class JSSMacSpi extends javax.crypto.MacSpi { | ||
|
||
private JSSMessageDigest digest=null; | ||
private HMACAlgorithm alg; | ||
private DigestAlgorithm alg; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @edewata This isn't quite right I now realize (I thought it wasn't, then though it was, now realize it isn't). DigestAlgorithm defines things like SHA-1, SHA-2, &c. HMACAlgorithm is a subclass which defines things like HMAC/SHA-1, HMAC-SHA-2-256, &c. These all require keys though, unlike digests (though, some digests are keyed, but none we define are). CMAC (the algorithm) isn't actually based off of a digest (hash algorithm), but is instead based off a (keyed) cipher. So technically, we should have a class Structure like:
But currently we have the following class structure:
Lastly, these are protected so end-users won't directly be constructing them. I see the following actions we could take:
Note that any checking that we would do here is limited to developers adding new HMAC/CMAC/*MAC implementations and making their lives easier. Thoughts? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (UMAC is the only other MAC implementation I could see someone wanting to add, but NSS doesn't yet support it). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm OK with #3 for a new JSS minor version (4.7?). If we want to keep the current minor version we can use any other options to provide the new functionality without introducing incompatible changes, but consider that as a temporary solution until we fix the structure. As long as we document the changes clearly it should be OK to require people to change their code when upgrading JSS. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about we add that as a second patchset after this merges, since it involves changing both HMAC and CMAC, and we can do additional simplifications internally? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, thanks! |
||
|
||
protected JSSMacSpi(HMACAlgorithm alg) { | ||
protected JSSMacSpi(DigestAlgorithm alg) { | ||
try { | ||
this.alg = alg; | ||
CryptoToken token = | ||
|
@@ -116,4 +118,9 @@ public HmacSHA512() { | |
} | ||
} | ||
|
||
public static class CmacAES extends JSSMacSpi { | ||
public CmacAES() { | ||
super(CMACAlgorithm.AES); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package org.mozilla.jss.tests; | ||
|
||
import java.util.Arrays; | ||
import java.util.Base64; | ||
import java.security.Key; | ||
|
||
import javax.crypto.*; | ||
import javax.crypto.spec.*; | ||
|
||
import org.mozilla.jss.*; | ||
import org.mozilla.jss.crypto.*; | ||
import org.mozilla.jss.util.*; | ||
|
||
public class TestCmac { | ||
cipherboy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
private static final byte[] NIST_128 = Base64.getDecoder().decode("K34VFiiu0qar9xWICc9PPA=="); | ||
private static final byte[] NIST_192 = Base64.getDecoder().decode("jnOw99oOZFLIEPMrgJB55WL46tJSLGt7"); | ||
private static final byte[] NIST_256 = Base64.getDecoder().decode("YD3rEBXKcb4rc67whX13gR81LAc7YQjXLZgQowkU3/Q="); | ||
|
||
public static void main(String[] args) throws Exception { | ||
CryptoManager.initialize(args[0]); | ||
CryptoManager cm = CryptoManager.getInstance(); | ||
CryptoToken tok = cm.getInternalKeyStorageToken(); | ||
PasswordCallback cb = new FilePasswordCallback(args[1]); | ||
tok.login(cb); | ||
|
||
testNISTExamples(); | ||
} | ||
|
||
/* | ||
* The following test vectors come from NIST's Examples with Intermediate | ||
* Values page: | ||
* https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values | ||
* | ||
* These are the same vectors utilized by NSS in: | ||
* gtests/freebl_gtest/cmac_unittests.cc | ||
* | ||
* These same vectors are also found in FRC 4493, Section 4. | ||
*/ | ||
public static void testNISTExamples() throws Exception { | ||
byte[] all_input = Base64.getDecoder().decode("a8G+4i5An5bpPX4Rc5MXKq4tilceA6ycnrdvrEWvjlEwyBxGo1zkEeX7wRkaClLv9p8kRd9PmxetK0F75mw3EA=="); | ||
int[] input_lengths = new int[] { 0, 16, 20, 64}; | ||
|
||
byte[][] all_expected = new byte[][] { | ||
Base64.getDecoder().decode("ux1pKelZNyh/o30Sm3VnRg=="), | ||
Base64.getDecoder().decode("BwoWtGtNQUT3m92d0EoofA=="), | ||
Base64.getDecoder().decode("fYVEnqbqGcgjp794g3363g=="), | ||
Base64.getDecoder().decode("UfC+v347nZL8SXQXeTY8/g=="), | ||
Base64.getDecoder().decode("0X3fRq2qzeUxysSD3nqTZw=="), | ||
Base64.getDecoder().decode("npmnvzHnEJAGYvZeYXxRhA=="), | ||
Base64.getDecoder().decode("PXXBlO2WBwREqfp+x0Ds+A=="), | ||
Base64.getDecoder().decode("odXfDu15D3lNd1iWWfOaEQ=="), | ||
Base64.getDecoder().decode("Aoli9ht7+J78a1UfRmfZgw=="), | ||
Base64.getDecoder().decode("KKcCP0Uuj4K9S/KNjDfDXA=="), | ||
Base64.getDecoder().decode("FWcn3Ah4lEoCPB/gO61tkw=="), | ||
Base64.getDecoder().decode("4ZkhkFSfbtVpaiwFbDFUEA==") | ||
}; | ||
|
||
for (int i = 0; i < all_expected.length; i++) { | ||
byte[] key = getKey(i); | ||
byte[] input = Arrays.copyOf(all_input, input_lengths[i % input_lengths.length]); | ||
byte[] expected = all_expected[i]; | ||
|
||
testCMAC(key, input, expected); | ||
} | ||
} | ||
|
||
public static byte[] getKey(int index) { | ||
if (index < 4) { | ||
return NIST_128; | ||
} else if (index < 8) { | ||
return NIST_192; | ||
} else if (index < 12) { | ||
return NIST_256; | ||
} | ||
|
||
return null; | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question: In TPS/TKS we start with a symmetric key such as the master key and then send that down into the routine that calculates the CMAC. The key will be on the hsm possibly as a SymmetricKey object variable. How do we use this from JSS from that starting point? The test examples start from byte array keys, which is what you expect in the HMAC stuff. Would it be similar to what I found in JSS here: // perform the digesting There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So there's two interfaces and a class in question here... and a pointer to the PKI code might be nice. But I'll assume the following is correct. :) Classes/Interfaces in question:
Everything that JSS uses has to be an instance of To go from javax.crypto.SecretKey key = new org.mozilla.jss.crypto.SecretKeyFacade(mySymmetricKey); That's really what Note that That brings us full-circle: if you have bytes, you can use the SecretKeyFactory with a correct There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See #271. I think we can support just passing SymmetricKey directly here and be better off. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is the info I wanted! Just double checking I can do what we need :) |
||
public static void testCMAC(byte[] key_bytes, byte[] input, byte[] expected) throws Exception { | ||
Mac mac = Mac.getInstance("AES_CMAC", "Mozilla-JSS"); | ||
SecretKeyFactory factory = SecretKeyFactory.getInstance("AES", "Mozilla-JSS"); | ||
Key key = factory.generateSecret(new SecretKeySpec(key_bytes, "AES")); | ||
mac.init(key); | ||
|
||
byte[] actual = mac.doFinal(input); | ||
|
||
assert(Arrays.equals(actual, expected)); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another question:
I assume by adding this mechanism , which nss now implements, is enough to make existing jni code in jss do this job for us? Just curious to take a look at what's going on down there. Is PK11MessageDigest.c where the magic is happening?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep! That's some lovely magic.
So my understanding of PKCS#11 is a bit... rough, and Bob is definitely the one to ask these questions to. :)
But there's a few basic operations in PKCS#11: Sign, Verify, Encrypt, Decrypt, etc. Each of these correspond to a top-level function call in NSS:
PK11_Sign
,PK11_Verify
, etc. So, once you've set up the context, you essentially get away with calling one of those methods, and then you'll have the result. But anyways... let's start at the top.You create a new
javax.crypto.Mac
instance of typeCmacAES
(or one of its aliases). This gets backed by aJSSMacSpi
instance of subclassCmacAES
. All this does is initialize theJSSMacSpi
instance with theCMACAlgorithm.AES
algorithm. This is an instance oforg.mozilla.jss.crypto.CMACAlgorithm
with the correct PKCS#11 constant (er, proxies). [*]Anyhow, so when you call
Mac.init
, it calls intoPK11MessageDigest.initHMAC(...)
, which creates the PKCS#11 mechanism, makes sure the key is enabled for signing, and then creates the PKCS#11 context.Each call to
Mac.update
then callsPK11_DigestOp
, and in the endPK11_DigestFinish
is called. And to finish;PK11_DigestOp
/PK11_DigestFinal
is merely a nice wrapper around all of those different PKCS#11 operations.[*]: This is where I disagree with the construction of
org.mozilla.jss.crypto.Algorithm
: rather than handling PKCS#11-backed and SecOID-backed algorithms separately, they merge it into one big table of tuples (value, type). Then, they define pretty values likeAlgorithm.CKM_AES_CMAC = 70
, which is an index and not the actual value. They could've instead made all PKCS#11-backed algorithms use, well, the underlyingPKCS11Constants
values and not made a redundant mapping (that class was added by me since its counterpart in the JDK was removed in JDK9, but that's well before our time)... But that's neither here nor there, and we haveorg.mozilla.jss.crypto.PKCS11Algorithm
to map between them for us.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent info. I will approve since it looked good to me, just wanted a couple of clarifications. I just wanted to make sure I missed some other code that calls down into your new nss cmac code. Thanks!