| package org.bouncycastle.jce.provider.test; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.security.InvalidAlgorithmParameterException; |
| import java.security.InvalidParameterException; |
| import java.security.Key; |
| import java.security.KeyFactory; |
| import java.security.KeyPair; |
| import java.security.KeyPairGenerator; |
| import java.security.PrivateKey; |
| import java.security.PublicKey; |
| import java.security.SecureRandom; |
| import java.security.Security; |
| import java.security.Signature; |
| import java.security.spec.AlgorithmParameterSpec; |
| import java.security.spec.ECGenParameterSpec; |
| import java.security.spec.InvalidKeySpecException; |
| import java.security.spec.PKCS8EncodedKeySpec; |
| import java.security.spec.X509EncodedKeySpec; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Set; |
| |
| import javax.crypto.KeyAgreement; |
| |
| import org.bouncycastle.asn1.ASN1ObjectIdentifier; |
| import org.bouncycastle.asn1.edec.EdECObjectIdentifiers; |
| import org.bouncycastle.asn1.x509.Certificate; |
| import org.bouncycastle.jcajce.spec.DHUParameterSpec; |
| import org.bouncycastle.jcajce.spec.EdDSAParameterSpec; |
| import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec; |
| import org.bouncycastle.jcajce.spec.XDHParameterSpec; |
| import org.bouncycastle.jce.provider.BouncyCastleProvider; |
| import org.bouncycastle.util.Strings; |
| import org.bouncycastle.util.encoders.Base64; |
| import org.bouncycastle.util.encoders.Hex; |
| import org.bouncycastle.util.test.SimpleTest; |
| |
| public class EdECTest |
| extends SimpleTest |
| { |
| private static final byte[] pubEnc = Base64.decode( |
| "MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE="); |
| |
| private static final byte[] privEnc = Base64.decode( |
| "MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC"); |
| |
| private static final byte[] privWithPubEnc = Base64.decode( |
| "MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC" + |
| "oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB" + |
| "Z9w7lshQhqowtrbLDFw4rXAxZuE="); |
| |
| public static final byte[] x25519Cert = Base64.decode( |
| "MIIBLDCB36ADAgECAghWAUdKKo3DMDAFBgMrZXAwGTEXMBUGA1UEAwwOSUVURiBUZX" + |
| "N0IERlbW8wHhcNMTYwODAxMTIxOTI0WhcNNDAxMjMxMjM1OTU5WjAZMRcwFQYDVQQD" + |
| "DA5JRVRGIFRlc3QgRGVtbzAqMAUGAytlbgMhAIUg8AmJMKdUdIt93LQ+91oNvzoNJj" + |
| "ga9OukqY6qm05qo0UwQzAPBgNVHRMBAf8EBTADAQEAMA4GA1UdDwEBAAQEAwIDCDAg" + |
| "BgNVHQ4BAQAEFgQUmx9e7e0EM4Xk97xiPFl1uQvIuzswBQYDK2VwA0EAryMB/t3J5v" + |
| "/BzKc9dNZIpDmAgs3babFOTQbs+BolzlDUwsPrdGxO3YNGhW7Ibz3OGhhlxXrCe1Cg" + |
| "w1AH9efZBw=="); |
| |
| public String getName() |
| { |
| return "EdEC"; |
| } |
| |
| public void performTest() |
| throws Exception |
| { |
| KeyFactory kFact = KeyFactory.getInstance("EdDSA", "BC"); |
| |
| PublicKey pub = kFact.generatePublic(new X509EncodedKeySpec(pubEnc)); |
| |
| isTrue("pub failed", areEqual(pubEnc, pub.getEncoded())); |
| |
| serializationTest("ref pub", pub); |
| |
| PrivateKey priv = kFact.generatePrivate(new PKCS8EncodedKeySpec(privEnc)); |
| |
| isTrue("priv failed", areEqual(privEnc, priv.getEncoded())); |
| |
| priv = kFact.generatePrivate(new PKCS8EncodedKeySpec(privWithPubEnc)); |
| |
| isTrue("priv with pub failed", areEqual(privWithPubEnc, priv.getEncoded())); |
| |
| serializationTest("ref priv", priv); |
| |
| Signature sig = Signature.getInstance("EDDSA", "BC"); |
| |
| Certificate x25519Cert = Certificate.getInstance(EdECTest.x25519Cert); |
| |
| sig.initVerify(pub); |
| |
| sig.update(x25519Cert.getTBSCertificate().getEncoded()); |
| |
| isTrue(sig.verify(x25519Cert.getSignature().getBytes())); |
| |
| x448AgreementTest(); |
| x25519AgreementTest(); |
| ed448SignatureTest(); |
| ed25519SignatureTest(); |
| x448withCKDFTest(); |
| x25519withCKDFTest(); |
| x448withKDFTest(); |
| x25519withKDFTest(); |
| x448UwithKDFTest(); |
| x25519UwithKDFTest(); |
| |
| xdhGeneratorTest(); |
| eddsaGeneratorTest(); |
| |
| keyTest("X448"); |
| keyTest("X25519"); |
| keyTest("Ed448"); |
| keyTest("Ed25519"); |
| |
| keyFactoryTest("X448", EdECObjectIdentifiers.id_X448); |
| keyFactoryTest("X25519", EdECObjectIdentifiers.id_X25519); |
| keyFactoryTest("Ed448", EdECObjectIdentifiers.id_Ed448); |
| keyFactoryTest("Ed25519", EdECObjectIdentifiers.id_Ed25519); |
| } |
| |
| private void keyFactoryTest(String algorithm, ASN1ObjectIdentifier algOid) |
| throws Exception |
| { |
| KeyPairGenerator kpGen = KeyPairGenerator.getInstance(algorithm, "BC"); |
| KeyFactory kFact = KeyFactory.getInstance((algorithm.startsWith("X") ? "XDH" : "EdDSA"), "BC"); |
| |
| KeyPair kp = kpGen.generateKeyPair(); |
| |
| Set<String> alts = new HashSet<String>(); |
| |
| alts.add("X448"); |
| alts.add("X25519"); |
| alts.add("Ed448"); |
| alts.add("Ed25519"); |
| |
| alts.remove(algorithm); |
| |
| PrivateKey k1 = kFact.generatePrivate(new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded())); |
| |
| checkEquals(algorithm, kp.getPrivate(), k1); |
| |
| PublicKey k2 = kFact.generatePublic(new X509EncodedKeySpec(kp.getPublic().getEncoded())); |
| |
| checkEquals(algorithm, kp.getPublic(), k2); |
| |
| for (Iterator<String> it = alts.iterator(); it.hasNext(); ) |
| { |
| String altAlg = (String)it.next(); |
| |
| kFact = KeyFactory.getInstance(altAlg, "BC"); |
| |
| try |
| { |
| k1 = kFact.generatePrivate(new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded())); |
| fail("no exception"); |
| } |
| catch (InvalidKeySpecException e) |
| { |
| isEquals("encoded key spec not recognized: algorithm identifier " + algOid.getId() + " in key not recognized", e.getMessage()); |
| } |
| |
| try |
| { |
| k2 = kFact.generatePublic(new X509EncodedKeySpec(kp.getPublic().getEncoded())); |
| fail("no exception"); |
| } |
| catch (InvalidKeySpecException e) |
| { |
| isEquals("encoded key spec not recognized: algorithm identifier " + algOid.getId() + " in key not recognized", e.getMessage()); |
| } |
| } |
| } |
| |
| private void keyTest(String algorithm) |
| throws Exception |
| { |
| KeyPairGenerator kpGen = KeyPairGenerator.getInstance(algorithm, "BC"); |
| |
| KeyFactory kFact = KeyFactory.getInstance(algorithm, "BC"); |
| |
| KeyPair kp = kpGen.generateKeyPair(); |
| |
| PrivateKey k1 = kFact.generatePrivate(new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded())); |
| |
| checkEquals(algorithm, kp.getPrivate(), k1); |
| |
| PublicKey k2 = kFact.generatePublic(new X509EncodedKeySpec(kp.getPublic().getEncoded())); |
| |
| checkEquals(algorithm, kp.getPublic(), k2); |
| |
| serializationTest(algorithm, kp.getPublic()); |
| serializationTest(algorithm, kp.getPrivate()); |
| |
| String pubString = kp.getPublic().toString(); |
| String privString = kp.getPrivate().toString(); |
| |
| isTrue(pubString.startsWith(algorithm + " Public Key [")); |
| isTrue(privString.startsWith(algorithm + " Private Key [")); |
| isTrue(privString.substring((algorithm + " Private Key [").length()) |
| .equals(pubString.substring((algorithm + " Public Key [").length()))); |
| } |
| |
| private void xdhGeneratorTest() |
| throws Exception |
| { |
| KeyPairGenerator kpGen = KeyPairGenerator.getInstance("XDH", "BC"); |
| |
| kpGen.initialize(new XDHParameterSpec(XDHParameterSpec.X448)); |
| |
| KeyPair kp = kpGen.generateKeyPair(); |
| |
| isTrue("X448".equals(kp.getPublic().getAlgorithm())); |
| |
| kpGen.initialize(new ECGenParameterSpec(XDHParameterSpec.X448)); |
| |
| kp = kpGen.generateKeyPair(); |
| |
| isTrue("X448".equals(kp.getPublic().getAlgorithm())); |
| |
| kpGen.initialize(448); |
| |
| kp = kpGen.generateKeyPair(); |
| |
| isTrue("X448".equals(kp.getPublic().getAlgorithm())); |
| |
| kpGen = KeyPairGenerator.getInstance("XDH", "BC"); |
| |
| kpGen.initialize(new XDHParameterSpec(XDHParameterSpec.X25519)); |
| |
| kp = kpGen.generateKeyPair(); |
| |
| isTrue("X25519".equals(kp.getPublic().getAlgorithm())); |
| |
| kpGen.initialize(new ECGenParameterSpec(XDHParameterSpec.X25519)); |
| |
| kp = kpGen.generateKeyPair(); |
| |
| isTrue("X25519".equals(kp.getPublic().getAlgorithm())); |
| |
| kpGen.initialize(256); |
| |
| kp = kpGen.generateKeyPair(); |
| |
| isTrue("X25519".equals(kp.getPublic().getAlgorithm())); |
| |
| kpGen.initialize(255); |
| |
| kp = kpGen.generateKeyPair(); |
| |
| isTrue("X25519".equals(kp.getPublic().getAlgorithm())); |
| |
| kpGen = KeyPairGenerator.getInstance("XDH", "BC"); |
| |
| try |
| { |
| kpGen.generateKeyPair(); |
| fail("no exception"); |
| } |
| catch (IllegalStateException e) |
| { |
| isEquals("generator not correctly initialized", e.getMessage()); |
| } |
| |
| try |
| { |
| kpGen.initialize(new EdDSAParameterSpec(EdDSAParameterSpec.Ed448)); |
| fail("no exception"); |
| } |
| catch (InvalidAlgorithmParameterException e) |
| { |
| isEquals("parameterSpec for wrong curve type", e.getMessage()); |
| } |
| |
| try |
| { |
| kpGen.initialize(1024); |
| fail("no exception"); |
| } |
| catch (InvalidParameterException e) |
| { |
| isEquals("unknown key size", e.getMessage()); |
| } |
| |
| try |
| { |
| kpGen.initialize(new EdDSAParameterSpec(EdDSAParameterSpec.Ed448)); |
| fail("no exception"); |
| } |
| catch (InvalidAlgorithmParameterException e) |
| { |
| isEquals("parameterSpec for wrong curve type", e.getMessage()); |
| } |
| |
| try |
| { |
| new XDHParameterSpec(EdDSAParameterSpec.Ed448); |
| } |
| catch (IllegalArgumentException e) |
| { |
| isEquals("unrecognized curve name: Ed448", e.getMessage()); |
| } |
| } |
| |
| private void eddsaGeneratorTest() |
| throws Exception |
| { |
| KeyPairGenerator kpGen = KeyPairGenerator.getInstance("EdDSA", "BC"); |
| |
| kpGen.initialize(new EdDSAParameterSpec(EdDSAParameterSpec.Ed448)); |
| |
| KeyPair kp = kpGen.generateKeyPair(); |
| |
| isTrue("Ed448".equals(kp.getPublic().getAlgorithm())); |
| |
| kpGen.initialize(new EdDSAParameterSpec(EdDSAParameterSpec.Ed448)); |
| |
| kp = kpGen.generateKeyPair(); |
| |
| isTrue("Ed448".equals(kp.getPublic().getAlgorithm())); |
| |
| kpGen.initialize(448); |
| |
| kp = kpGen.generateKeyPair(); |
| |
| isTrue("Ed448".equals(kp.getPublic().getAlgorithm())); |
| |
| kpGen = KeyPairGenerator.getInstance("EdDSA", "BC"); |
| |
| kpGen.initialize(new EdDSAParameterSpec(EdDSAParameterSpec.Ed25519)); |
| |
| kp = kpGen.generateKeyPair(); |
| |
| isTrue("Ed25519".equals(kp.getPublic().getAlgorithm())); |
| |
| kpGen.initialize(new ECGenParameterSpec(EdDSAParameterSpec.Ed25519)); |
| |
| kp = kpGen.generateKeyPair(); |
| |
| isTrue("Ed25519".equals(kp.getPublic().getAlgorithm())); |
| |
| kpGen.initialize(256); |
| |
| kp = kpGen.generateKeyPair(); |
| |
| isTrue("Ed25519".equals(kp.getPublic().getAlgorithm())); |
| |
| kpGen.initialize(255); |
| |
| kp = kpGen.generateKeyPair(); |
| |
| isTrue("Ed25519".equals(kp.getPublic().getAlgorithm())); |
| |
| kpGen = KeyPairGenerator.getInstance("EdDSA", "BC"); |
| |
| try |
| { |
| kpGen.generateKeyPair(); |
| fail("no exception"); |
| } |
| catch (IllegalStateException e) |
| { |
| isEquals("generator not correctly initialized", e.getMessage()); |
| } |
| |
| try |
| { |
| kpGen.initialize(new XDHParameterSpec(XDHParameterSpec.X448)); |
| fail("no exception"); |
| } |
| catch (InvalidAlgorithmParameterException e) |
| { |
| isEquals("parameterSpec for wrong curve type", e.getMessage()); |
| } |
| |
| try |
| { |
| kpGen.initialize(new XDHParameterSpec(XDHParameterSpec.X25519)); |
| fail("no exception"); |
| } |
| catch (InvalidAlgorithmParameterException e) |
| { |
| isEquals("parameterSpec for wrong curve type", e.getMessage()); |
| } |
| |
| try |
| { |
| kpGen.initialize(1024); |
| fail("no exception"); |
| } |
| catch (InvalidParameterException e) |
| { |
| isEquals("unknown key size", e.getMessage()); |
| } |
| |
| try |
| { |
| new EdDSAParameterSpec(XDHParameterSpec.X448); |
| } |
| catch (IllegalArgumentException e) |
| { |
| isEquals("unrecognized curve name: X448", e.getMessage()); |
| } |
| } |
| |
| private void checkEquals(String algorithm, Key ka, Key kb) |
| { |
| isEquals(algorithm + " check equals", ka, kb); |
| isEquals(algorithm + " check hashCode", ka.hashCode(), kb.hashCode()); |
| } |
| |
| private void serializationTest(String algorithm, Key key) |
| throws IOException, ClassNotFoundException |
| { |
| ByteArrayOutputStream bOut = new ByteArrayOutputStream(); |
| ObjectOutputStream oOut = new ObjectOutputStream(bOut); |
| |
| oOut.writeObject(key); |
| oOut.close(); |
| |
| ObjectInputStream oIn = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray())); |
| |
| Key rk = (Key)oIn.readObject(); |
| |
| checkEquals(algorithm, key, rk); |
| } |
| |
| private void x448AgreementTest() |
| throws Exception |
| { |
| agreementTest("X448"); |
| } |
| |
| private void x25519AgreementTest() |
| throws Exception |
| { |
| agreementTest("X25519"); |
| } |
| |
| private void x448withCKDFTest() |
| throws Exception |
| { |
| agreementTest("X448withSHA256CKDF", new UserKeyingMaterialSpec(Hex.decode("beeffeed"))); |
| agreementTest("X448withSHA384CKDF", new UserKeyingMaterialSpec(Hex.decode("beeffeed"))); |
| agreementTest("X448withSHA512CKDF", new UserKeyingMaterialSpec(Hex.decode("beeffeed"))); |
| } |
| |
| private void x25519withCKDFTest() |
| throws Exception |
| { |
| agreementTest("X25519withSHA256CKDF", new UserKeyingMaterialSpec(Hex.decode("beeffeed"))); |
| agreementTest("X25519withSHA384CKDF", new UserKeyingMaterialSpec(Hex.decode("beeffeed"))); |
| agreementTest("X25519withSHA512CKDF", new UserKeyingMaterialSpec(Hex.decode("beeffeed"))); |
| } |
| |
| private void x448withKDFTest() |
| throws Exception |
| { |
| agreementTest("X448withSHA512KDF", new UserKeyingMaterialSpec(Hex.decode("beeffeed"))); |
| } |
| |
| private void x25519withKDFTest() |
| throws Exception |
| { |
| agreementTest("X25519withSHA256KDF", new UserKeyingMaterialSpec(Hex.decode("beeffeed"))); |
| } |
| |
| private void ed448SignatureTest() |
| throws Exception |
| { |
| signatureTest("Ed448"); |
| } |
| |
| private void ed25519SignatureTest() |
| throws Exception |
| { |
| signatureTest("Ed25519"); |
| } |
| |
| private void agreementTest(String algorithm) |
| throws Exception |
| { |
| agreementTest(algorithm, null); |
| } |
| |
| private void agreementTest(String algorithm, AlgorithmParameterSpec spec) |
| throws Exception |
| { |
| KeyAgreement keyAgreement = KeyAgreement.getInstance(algorithm, "BC"); |
| |
| KeyPairGenerator kpGen = KeyPairGenerator.getInstance( |
| algorithm.startsWith("X448") ? "X448" : "X25519", "BC"); |
| |
| KeyPair kp1 = kpGen.generateKeyPair(); |
| KeyPair kp2 = kpGen.generateKeyPair(); |
| |
| keyAgreement.init(kp1.getPrivate()); |
| |
| keyAgreement.doPhase(kp2.getPublic(), true); |
| |
| byte[] sec1 = keyAgreement.generateSecret(); |
| |
| keyAgreement.init(kp2.getPrivate()); |
| |
| keyAgreement.doPhase(kp1.getPublic(), true); |
| |
| byte[] sec2 = keyAgreement.generateSecret(); |
| |
| isTrue(areEqual(sec1, sec2)); |
| |
| if (spec != null) |
| { |
| keyAgreement.init(kp1.getPrivate(), spec); |
| |
| keyAgreement.doPhase(kp2.getPublic(), true); |
| |
| byte[] sec3 = keyAgreement.generateSecret(); |
| |
| keyAgreement.init(kp2.getPrivate(), spec); |
| |
| keyAgreement.doPhase(kp1.getPublic(), true); |
| |
| byte[] sec4 = keyAgreement.generateSecret(); |
| |
| isTrue(areEqual(sec3, sec4)); |
| isTrue(!areEqual(sec1, sec4)); |
| } |
| } |
| |
| private void x448UwithKDFTest() |
| throws Exception |
| { |
| unifiedAgreementTest("X448UwithSHA512KDF"); |
| } |
| |
| private void x25519UwithKDFTest() |
| throws Exception |
| { |
| unifiedAgreementTest("X25519UwithSHA256KDF"); |
| } |
| |
| private void unifiedAgreementTest(String algorithm) |
| throws Exception |
| { |
| KeyAgreement keyAgreement = KeyAgreement.getInstance(algorithm, "BC"); |
| |
| KeyPairGenerator kpGen = KeyPairGenerator.getInstance( |
| algorithm.startsWith("X448") ? "X448" : "X25519", "BC"); |
| |
| KeyPair aKp1 = kpGen.generateKeyPair(); |
| KeyPair aKp2 = kpGen.generateKeyPair(); |
| |
| KeyPair bKp1 = kpGen.generateKeyPair(); |
| KeyPair bKp2 = kpGen.generateKeyPair(); |
| |
| keyAgreement.init(aKp1.getPrivate(), new DHUParameterSpec(aKp2, bKp2.getPublic(), Hex.decode("beeffeed"))); |
| |
| keyAgreement.doPhase(bKp1.getPublic(), true); |
| |
| byte[] sec1 = keyAgreement.generateSecret(); |
| |
| keyAgreement.init(bKp1.getPrivate(), new DHUParameterSpec(aKp2, bKp2.getPublic(), Hex.decode("beeffeed"))); |
| |
| keyAgreement.doPhase(aKp1.getPublic(), true); |
| |
| byte[] sec2 = keyAgreement.generateSecret(); |
| |
| isTrue(areEqual(sec1, sec2)); |
| |
| keyAgreement.init(bKp1.getPrivate(), new DHUParameterSpec(aKp2, bKp2.getPublic(), Hex.decode("feed"))); |
| |
| keyAgreement.doPhase(aKp1.getPublic(), true); |
| |
| byte[] sec3 = keyAgreement.generateSecret(); |
| |
| isTrue(!areEqual(sec1, sec3)); |
| } |
| |
| private void signatureTest(String algorithm) |
| throws Exception |
| { |
| byte[] msg = Strings.toByteArray("Hello, world!"); |
| Signature signature = Signature.getInstance(algorithm, "BC"); |
| |
| KeyPairGenerator kpGen = KeyPairGenerator.getInstance(algorithm, "BC"); |
| |
| KeyPair kp = kpGen.generateKeyPair(); |
| |
| signature.initSign(kp.getPrivate()); |
| |
| signature.update(msg); |
| |
| byte[] sig = signature.sign(); |
| |
| signature.initVerify(kp.getPublic()); |
| |
| signature.update(msg); |
| |
| isTrue(signature.verify(sig)); |
| |
| // try with random - should be ignored |
| |
| signature.initSign(kp.getPrivate(), new SecureRandom()); |
| |
| signature.update(msg); |
| |
| sig = signature.sign(); |
| |
| signature.initVerify(kp.getPublic()); |
| |
| signature.update(msg); |
| |
| isTrue(signature.verify(sig)); |
| } |
| |
| public static void main( |
| String[] args) |
| { |
| Security.addProvider(new BouncyCastleProvider()); |
| |
| runTest(new EdECTest()); |
| } |
| } |