diff options
Diffstat (limited to 'prov/src/main/java/org/spongycastle/pqc/jcajce/provider')
28 files changed, 6083 insertions, 0 deletions
diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java new file mode 100644 index 00000000..b237aee4 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java @@ -0,0 +1,157 @@ +package org.spongycastle.pqc.jcajce.provider; + +import java.io.IOException; +import java.security.AccessController; +import java.security.PrivateKey; +import java.security.PrivilegedAction; +import java.security.Provider; +import java.security.PublicKey; +import java.util.HashMap; +import java.util.Map; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.jcajce.provider.config.ConfigurableProvider; +import org.spongycastle.jcajce.provider.config.ProviderConfiguration; +import org.spongycastle.jcajce.provider.util.AlgorithmProvider; +import org.spongycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; + +public class BouncyCastlePQCProvider + extends Provider + implements ConfigurableProvider +{ + private static String info = "BouncyCastle Post-Quantum Security Provider v1.50"; + + public static String PROVIDER_NAME = "BCPQC"; + + public static final ProviderConfiguration CONFIGURATION = null; + + + private static final Map keyInfoConverters = new HashMap(); + + /* + * Configurable symmetric ciphers + */ + private static final String ALGORITHM_PACKAGE = "org.spongycastle.pqc.jcajce.provider."; + private static final String[] ALGORITHMS = + { + "Rainbow", "McEliece" + }; + + /** + * Construct a new provider. This should only be required when + * using runtime registration of the provider using the + * <code>Security.addProvider()</code> mechanism. + */ + public BouncyCastlePQCProvider() + { + super(PROVIDER_NAME, 1.50, info); + + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + setup(); + return null; + } + }); + } + + private void setup() + { + loadAlgorithms(ALGORITHM_PACKAGE, ALGORITHMS); + } + + private void loadAlgorithms(String packageName, String[] names) + { + for (int i = 0; i != names.length; i++) + { + Class clazz = null; + try + { + ClassLoader loader = this.getClass().getClassLoader(); + + if (loader != null) + { + clazz = loader.loadClass(packageName + names[i] + "$Mappings"); + } + else + { + clazz = Class.forName(packageName + names[i] + "$Mappings"); + } + } + catch (ClassNotFoundException e) + { + // ignore + } + + if (clazz != null) + { + try + { + ((AlgorithmProvider)clazz.newInstance()).configure(this); + } + catch (Exception e) + { // this should never ever happen!! + throw new InternalError("cannot create instance of " + + packageName + names[i] + "$Mappings : " + e); + } + } + } + } + + public void setParameter(String parameterName, Object parameter) + { + synchronized (CONFIGURATION) + { + //((BouncyCastleProviderConfiguration)CONFIGURATION).setParameter(parameterName, parameter); + } + } + + public boolean hasAlgorithm(String type, String name) + { + return containsKey(type + "." + name) || containsKey("Alg.Alias." + type + "." + name); + } + + public void addAlgorithm(String key, String value) + { + if (containsKey(key)) + { + throw new IllegalStateException("duplicate provider key (" + key + ") found"); + } + + put(key, value); + } + + public void addKeyInfoConverter(ASN1ObjectIdentifier oid, AsymmetricKeyInfoConverter keyInfoConverter) + { + keyInfoConverters.put(oid, keyInfoConverter); + } + + public static PublicKey getPublicKey(SubjectPublicKeyInfo publicKeyInfo) + throws IOException + { + AsymmetricKeyInfoConverter converter = (AsymmetricKeyInfoConverter)keyInfoConverters.get(publicKeyInfo.getAlgorithm().getAlgorithm()); + + if (converter == null) + { + return null; + } + + return converter.generatePublic(publicKeyInfo); + } + + public static PrivateKey getPrivateKey(PrivateKeyInfo privateKeyInfo) + throws IOException + { + AsymmetricKeyInfoConverter converter = (AsymmetricKeyInfoConverter)keyInfoConverters.get(privateKeyInfo.getPrivateKeyAlgorithm().getAlgorithm()); + + if (converter == null) + { + return null; + } + + return converter.generatePrivate(privateKeyInfo); + } +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/McEliece.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/McEliece.java new file mode 100644 index 00000000..06f16f85 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/McEliece.java @@ -0,0 +1,62 @@ +package org.spongycastle.pqc.jcajce.provider; + +import org.spongycastle.jcajce.provider.config.ConfigurableProvider; +import org.spongycastle.jcajce.provider.util.AsymmetricAlgorithmProvider; +import org.spongycastle.pqc.asn1.PQCObjectIdentifiers; + +public class McEliece +{ + private static final String PREFIX = "org.spongycastle.pqc.jcajce.provider" + ".mceliece."; + + public static class Mappings + extends AsymmetricAlgorithmProvider + { + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + // McElieceKobaraImai + provider.addAlgorithm("KeyPairGenerator.McElieceKobaraImai", PREFIX + "McElieceKeyPairGeneratorSpi$McElieceCCA2"); + // McEliecePointcheval + provider.addAlgorithm("KeyPairGenerator.McEliecePointcheval", PREFIX + "McElieceKeyPairGeneratorSpi$McElieceCCA2"); + // McElieceFujisaki + provider.addAlgorithm("KeyPairGenerator.McElieceFujisaki", PREFIX + "McElieceKeyPairGeneratorSpi$McElieceCCA2"); + // McEliecePKCS + provider.addAlgorithm("KeyPairGenerator.McEliecePKCS", PREFIX + "McElieceKeyPairGeneratorSpi$McEliece"); + + provider.addAlgorithm("KeyPairGenerator." + PQCObjectIdentifiers.mcEliece, PREFIX + "McElieceKeyPairGeneratorSpi$McEliece"); + provider.addAlgorithm("KeyPairGenerator." + PQCObjectIdentifiers.mcElieceCca2, PREFIX + "McElieceKeyPairGeneratorSpi$McElieceCCA2"); + + provider.addAlgorithm("Cipher.McEliecePointcheval", PREFIX + "McEliecePointchevalCipherSpi$McEliecePointcheval"); + provider.addAlgorithm("Cipher.McEliecePointchevalWithSHA1", PREFIX + "McEliecePointchevalCipherSpi$McEliecePointcheval"); + provider.addAlgorithm("Cipher.McEliecePointchevalWithSHA224", PREFIX + "McEliecePointchevalCipherSpi$McEliecePointcheval224"); + provider.addAlgorithm("Cipher.McEliecePointchevalWithSHA256", PREFIX + "McEliecePointchevalCipherSpi$McEliecePointcheval256"); + provider.addAlgorithm("Cipher.McEliecePointchevalWithSHA384", PREFIX + "McEliecePointchevalCipherSpi$McEliecePointcheval384"); + provider.addAlgorithm("Cipher.McEliecePointchevalWithSHA512", PREFIX + "McEliecePointchevalCipherSpi$McEliecePointcheval512"); + + provider.addAlgorithm("Cipher.McEliecePKCS", PREFIX + "McEliecePKCSCipherSpi$McEliecePKCS"); + provider.addAlgorithm("Cipher.McEliecePKCSWithSHA1", PREFIX + "McEliecePKCSCipherSpi$McEliecePKCS"); + provider.addAlgorithm("Cipher.McEliecePKCSWithSHA224", PREFIX + "McEliecePKCSCipherSpi$McEliecePKCS224"); + provider.addAlgorithm("Cipher.McEliecePKCSWithSHA256", PREFIX + "McEliecePKCSCipherSpi$McEliecePKCS256"); + provider.addAlgorithm("Cipher.McEliecePKCSWithSHA384", PREFIX + "McEliecePKCSCipherSpi$McEliecePKCS384"); + provider.addAlgorithm("Cipher.McEliecePKCSWithSHA512", PREFIX + "McEliecePKCSCipherSpi$McEliecePKCS512"); + + provider.addAlgorithm("Cipher.McElieceKobaraImai", PREFIX + "McElieceKobaraImaiCipherSpi$McElieceKobaraImai"); + provider.addAlgorithm("Cipher.McElieceKobaraImaiWithSHA1", PREFIX + "McElieceKobaraImaiCipherSpi$McElieceKobaraImai"); + provider.addAlgorithm("Cipher.McElieceKobaraImaiWithSHA224", PREFIX + "McElieceKobaraImaiCipherSpi$McElieceKobaraImai224"); + provider.addAlgorithm("Cipher.McElieceKobaraImaiWithSHA256", PREFIX + "McElieceKobaraImaiCipherSpi$McElieceKobaraImai256"); + provider.addAlgorithm("Cipher.McElieceKobaraImaiWithSHA384", PREFIX + "McElieceKobaraImaiCipherSpi$McElieceKobaraImai384"); + provider.addAlgorithm("Cipher.McElieceKobaraImaiWithSHA512", PREFIX + "McElieceKobaraImaiCipherSpi$McElieceKobaraImai512"); + + provider.addAlgorithm("Cipher.McElieceFujisaki", PREFIX + "McElieceFujisakiCipherSpi$McElieceFujisaki"); + provider.addAlgorithm("Cipher.McElieceFujisakiWithSHA1", PREFIX + "McElieceFujisakiCipherSpi$McElieceFujisaki"); + provider.addAlgorithm("Cipher.McElieceFujisakiWithSHA224", PREFIX + "McElieceFujisakiCipherSpi$McElieceFujisaki224"); + provider.addAlgorithm("Cipher.McElieceFujisakiWithSHA256", PREFIX + "McElieceFujisakiCipherSpi$McElieceFujisaki256"); + provider.addAlgorithm("Cipher.McElieceFujisakiWithSHA384", PREFIX + "McElieceFujisakiCipherSpi$McElieceFujisaki384"); + provider.addAlgorithm("Cipher.McElieceFujisakiWithSHA512", PREFIX + "McElieceFujisakiCipherSpi$McElieceFujisaki512"); + + } + } +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/Rainbow.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/Rainbow.java new file mode 100644 index 00000000..7752c4bf --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/Rainbow.java @@ -0,0 +1,36 @@ +package org.spongycastle.pqc.jcajce.provider; + +import org.spongycastle.jcajce.provider.config.ConfigurableProvider; +import org.spongycastle.jcajce.provider.util.AsymmetricAlgorithmProvider; +import org.spongycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; +import org.spongycastle.pqc.asn1.PQCObjectIdentifiers; +import org.spongycastle.pqc.jcajce.provider.rainbow.RainbowKeyFactorySpi; + +public class Rainbow +{ + private static final String PREFIX = "org.spongycastle.pqc.jcajce.provider" + ".rainbow."; + + public static class Mappings + extends AsymmetricAlgorithmProvider + { + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + provider.addAlgorithm("KeyFactory.Rainbow", PREFIX + "RainbowKeyFactorySpi"); + provider.addAlgorithm("KeyPairGenerator.Rainbow", PREFIX + "RainbowKeyPairGeneratorSpi"); + + addSignatureAlgorithm(provider, "SHA224", "Rainbow", PREFIX + "SignatureSpi$withSha224", PQCObjectIdentifiers.rainbowWithSha224); + addSignatureAlgorithm(provider, "SHA256", "Rainbow", PREFIX + "SignatureSpi$withSha256", PQCObjectIdentifiers.rainbowWithSha256); + addSignatureAlgorithm(provider, "SHA384", "Rainbow", PREFIX + "SignatureSpi$withSha384", PQCObjectIdentifiers.rainbowWithSha384); + addSignatureAlgorithm(provider, "SHA512", "Rainbow", PREFIX + "SignatureSpi$withSha512", PQCObjectIdentifiers.rainbowWithSha512); + + AsymmetricKeyInfoConverter keyFact = new RainbowKeyFactorySpi(); + + registerOid(provider, PQCObjectIdentifiers.rainbow, "Rainbow", keyFact); + registerOidAlgorithmParameters(provider, PQCObjectIdentifiers.rainbow, "Rainbow"); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/gmss/BCGMSSPublicKey.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/gmss/BCGMSSPublicKey.java new file mode 100644 index 00000000..b3253160 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/gmss/BCGMSSPublicKey.java @@ -0,0 +1,131 @@ +package org.spongycastle.pqc.jcajce.provider.gmss; + +import java.security.PublicKey; + +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.pqc.asn1.GMSSPublicKey; +import org.spongycastle.pqc.asn1.PQCObjectIdentifiers; +import org.spongycastle.pqc.asn1.ParSet; +import org.spongycastle.pqc.crypto.gmss.GMSSParameters; +import org.spongycastle.pqc.crypto.gmss.GMSSPublicKeyParameters; +import org.spongycastle.pqc.jcajce.provider.util.KeyUtil; +import org.spongycastle.pqc.jcajce.spec.GMSSPublicKeySpec; +import org.spongycastle.util.encoders.Hex; + +/** + * This class implements the GMSS public key and is usually initiated by the <a + * href="GMSSKeyPairGenerator">GMSSKeyPairGenerator</a>. + * + * @see org.spongycastle.pqc.crypto.gmss.GMSSKeyPairGenerator + * @see org.spongycastle.pqc.jcajce.spec.GMSSPublicKeySpec + */ +public class BCGMSSPublicKey + implements CipherParameters, PublicKey +{ + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * The GMSS public key + */ + private byte[] publicKeyBytes; + + /** + * The GMSSParameterSet + */ + private GMSSParameters gmssParameterSet; + + + private GMSSParameters gmssParams; + + /** + * The constructor + * + * @param pub a raw GMSS public key + * @param gmssParameterSet an instance of GMSS Parameterset + * @see org.spongycastle.pqc.crypto.gmss.GMSSKeyPairGenerator + */ + public BCGMSSPublicKey(byte[] pub, GMSSParameters gmssParameterSet) + { + this.gmssParameterSet = gmssParameterSet; + this.publicKeyBytes = pub; + } + + /** + * The constructor + * + * @param keySpec a GMSS key specification + */ + protected BCGMSSPublicKey(GMSSPublicKeySpec keySpec) + { + this(keySpec.getPublicKey(), keySpec.getParameters()); + } + + public BCGMSSPublicKey( + GMSSPublicKeyParameters params) + { + this(params.getPublicKey(), params.getParameters()); + } + + /** + * Returns the name of the algorithm + * + * @return "GMSS" + */ + public String getAlgorithm() + { + return "GMSS"; + } + + /** + * @return The GMSS public key byte array + */ + public byte[] getPublicKeyBytes() + { + return publicKeyBytes; + } + + /** + * @return The GMSS Parameterset + */ + public GMSSParameters getParameterSet() + { + return gmssParameterSet; + } + + /** + * Returns a human readable form of the GMSS public key + * + * @return A human readable form of the GMSS public key + */ + public String toString() + { + String out = "GMSS public key : " + + new String(Hex.encode(publicKeyBytes)) + "\n" + + "Height of Trees: \n"; + + for (int i = 0; i < gmssParameterSet.getHeightOfTrees().length; i++) + { + out = out + "Layer " + i + " : " + + gmssParameterSet.getHeightOfTrees()[i] + + " WinternitzParameter: " + + gmssParameterSet.getWinternitzParameter()[i] + " K: " + + gmssParameterSet.getK()[i] + "\n"; + } + return out; + } + + public byte[] getEncoded() + { + return KeyUtil.getEncodedSubjectPublicKeyInfo(new AlgorithmIdentifier(PQCObjectIdentifiers.gmss, new ParSet(gmssParameterSet.getNumOfLayers(), gmssParameterSet.getHeightOfTrees(), gmssParameterSet.getWinternitzParameter(), gmssParameterSet.getK()).toASN1Primitive()), new GMSSPublicKey(publicKeyBytes)); + } + + public String getFormat() + { + return "X.509"; + } +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/BCMcElieceCCA2PrivateKey.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/BCMcElieceCCA2PrivateKey.java new file mode 100644 index 00000000..0a22dd6f --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/BCMcElieceCCA2PrivateKey.java @@ -0,0 +1,307 @@ +package org.spongycastle.pqc.jcajce.provider.mceliece; + +import java.io.IOException; +import java.security.PrivateKey; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.pqc.asn1.McElieceCCA2PrivateKey; +import org.spongycastle.pqc.crypto.mceliece.McElieceCCA2KeyPairGenerator; +import org.spongycastle.pqc.crypto.mceliece.McElieceCCA2Parameters; +import org.spongycastle.pqc.crypto.mceliece.McElieceCCA2PrivateKeyParameters; +import org.spongycastle.pqc.jcajce.spec.McElieceCCA2PrivateKeySpec; +import org.spongycastle.pqc.math.linearalgebra.GF2Matrix; +import org.spongycastle.pqc.math.linearalgebra.GF2mField; +import org.spongycastle.pqc.math.linearalgebra.Permutation; +import org.spongycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM; + +/** + * This class implements a McEliece CCA2 private key and is usually instantiated + * by the {@link McElieceCCA2KeyPairGenerator} or {@link McElieceCCA2KeyFactorySpi}. + * + * @see McElieceCCA2KeyPairGenerator + */ +public class BCMcElieceCCA2PrivateKey + implements CipherParameters, PrivateKey +{ + + + /** + * + */ + private static final long serialVersionUID = 1L; + + // the OID of the algorithm + private String oid; + + // the length of the code + private int n; + + // the dimension of the code, k>=n-mt + private int k; + + // the finte field GF(2^m) + private GF2mField field; + + // the irreducible Goppa polynomial + private PolynomialGF2mSmallM goppaPoly; + + // the permutation + private Permutation p; + + // the canonical check matrix + private GF2Matrix h; + + // the matrix used to compute square roots in (GF(2^m))^t + private PolynomialGF2mSmallM[] qInv; + + private McElieceCCA2Parameters mcElieceCCA2Params; + + /** + * Constructor (used by the {@link McElieceCCA2KeyPairGenerator}). + * + * @param n the length of the code + * @param k the dimension of the code + * @param field the field polynomial + * @param gp the irreducible Goppa polynomial + * @param p the permutation + * @param h the canonical check matrix + * @param qInv the matrix used to compute square roots in + * <tt>(GF(2^m))^t</tt> + */ + public BCMcElieceCCA2PrivateKey(String oid, int n, int k, GF2mField field, + PolynomialGF2mSmallM gp, Permutation p, GF2Matrix h, + PolynomialGF2mSmallM[] qInv) + { + this.oid = oid; + this.n = n; + this.k = k; + this.field = field; + this.goppaPoly = gp; + this.p = p; + this.h = h; + this.qInv = qInv; + } + + /** + * Constructor (used by the {@link McElieceCCA2KeyFactorySpi}). + * + * @param keySpec a {@link McElieceCCA2PrivateKeySpec} + */ + public BCMcElieceCCA2PrivateKey(McElieceCCA2PrivateKeySpec keySpec) + { + this(keySpec.getOIDString(), keySpec.getN(), keySpec.getK(), keySpec.getField(), keySpec + .getGoppaPoly(), keySpec.getP(), keySpec.getH(), keySpec + .getQInv()); + } + + public BCMcElieceCCA2PrivateKey(McElieceCCA2PrivateKeyParameters params) + { + this(params.getOIDString(), params.getN(), params.getK(), params.getField(), params.getGoppaPoly(), + params.getP(), params.getH(), params.getQInv()); + this.mcElieceCCA2Params = params.getParameters(); + } + + /** + * Return the name of the algorithm. + * + * @return "McEliece" + */ + public String getAlgorithm() + { + return "McEliece"; + } + + /** + * @return the length of the code + */ + public int getN() + { + return n; + } + + /** + * @return the dimension of the code + */ + public int getK() + { + return k; + } + + /** + * @return the degree of the Goppa polynomial (error correcting capability) + */ + public int getT() + { + return goppaPoly.getDegree(); + } + + /** + * @return the finite field + */ + public GF2mField getField() + { + return field; + } + + /** + * @return the irreducible Goppa polynomial + */ + public PolynomialGF2mSmallM getGoppaPoly() + { + return goppaPoly; + } + + /** + * @return the permutation vector + */ + public Permutation getP() + { + return p; + } + + /** + * @return the canonical check matrix + */ + public GF2Matrix getH() + { + return h; + } + + /** + * @return the matrix used to compute square roots in <tt>(GF(2^m))^t</tt> + */ + public PolynomialGF2mSmallM[] getQInv() + { + return qInv; + } + + /** + * @return a human readable form of the key + */ + public String toString() + { + String result = ""; + result += " extension degree of the field : " + n + "\n"; + result += " dimension of the code : " + k + "\n"; + result += " irreducible Goppa polynomial : " + goppaPoly + "\n"; + return result; + } + + /** + * Compare this key with another object. + * + * @param other the other object + * @return the result of the comparison + */ + public boolean equals(Object other) + { + if (other == null || !(other instanceof BCMcElieceCCA2PrivateKey)) + { + return false; + } + + BCMcElieceCCA2PrivateKey otherKey = (BCMcElieceCCA2PrivateKey)other; + + return (n == otherKey.n) && (k == otherKey.k) + && field.equals(otherKey.field) + && goppaPoly.equals(otherKey.goppaPoly) && p.equals(otherKey.p) + && h.equals(otherKey.h); + } + + /** + * @return the hash code of this key + */ + public int hashCode() + { + return k + n + field.hashCode() + goppaPoly.hashCode() + p.hashCode() + + h.hashCode(); + } + + /** + * @return the OID of the algorithm + */ + public String getOIDString() + { + return oid; + } + + /** + * @return the OID to encode in the SubjectPublicKeyInfo structure + */ + protected ASN1ObjectIdentifier getOID() + { + return new ASN1ObjectIdentifier(McElieceCCA2KeyFactorySpi.OID); + } + + /** + * @return the algorithm parameters to encode in the SubjectPublicKeyInfo + * structure + */ + protected ASN1Primitive getAlgParams() + { + return null; // FIXME: needed at all? + } + + + /** + * Return the keyData to encode in the SubjectPublicKeyInfo structure. + * <p/> + * The ASN.1 definition of the key structure is + * <p/> + * <pre> + * McEliecePrivateKey ::= SEQUENCE { + * m INTEGER -- extension degree of the field + * k INTEGER -- dimension of the code + * field OCTET STRING -- field polynomial + * goppaPoly OCTET STRING -- irreducible Goppa polynomial + * p OCTET STRING -- permutation vector + * matrixH OCTET STRING -- canonical check matrix + * sqRootMatrix SEQUENCE OF OCTET STRING -- square root matrix + * } + * </pre> + * + * @return the keyData to encode in the SubjectPublicKeyInfo structure + */ + public byte[] getEncoded() + { + McElieceCCA2PrivateKey privateKey = new McElieceCCA2PrivateKey(new ASN1ObjectIdentifier(oid), n, k, field, goppaPoly, p, h, qInv); + PrivateKeyInfo pki; + try + { + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(this.getOID(), DERNull.INSTANCE); + pki = new PrivateKeyInfo(algorithmIdentifier, privateKey); + } + catch (IOException e) + { + e.printStackTrace(); + return null; + } + try + { + byte[] encoded = pki.getEncoded(); + return encoded; + } + catch (IOException e) + { + e.printStackTrace(); + return null; + } + } + + public String getFormat() + { + // TODO Auto-generated method stub + return null; + } + + public McElieceCCA2Parameters getMcElieceCCA2Parameters() + { + return mcElieceCCA2Params; + } + +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/BCMcElieceCCA2PublicKey.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/BCMcElieceCCA2PublicKey.java new file mode 100644 index 00000000..9c3361bc --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/BCMcElieceCCA2PublicKey.java @@ -0,0 +1,227 @@ +package org.spongycastle.pqc.jcajce.provider.mceliece; + + +import java.io.IOException; +import java.security.PublicKey; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.pqc.asn1.McElieceCCA2PublicKey; +import org.spongycastle.pqc.crypto.mceliece.McElieceCCA2KeyPairGenerator; +import org.spongycastle.pqc.crypto.mceliece.McElieceCCA2Parameters; +import org.spongycastle.pqc.crypto.mceliece.McElieceCCA2PublicKeyParameters; +import org.spongycastle.pqc.jcajce.spec.McElieceCCA2PublicKeySpec; +import org.spongycastle.pqc.math.linearalgebra.GF2Matrix; + +/** + * This class implements a McEliece CCA2 public key and is usually instantiated + * by the {@link McElieceCCA2KeyPairGenerator} or {@link McElieceCCA2KeyFactorySpi}. + */ +public class BCMcElieceCCA2PublicKey + implements CipherParameters, PublicKey +{ + + /** + * + */ + private static final long serialVersionUID = 1L; + + // the OID of the algorithm + private String oid; + + // the length of the code + private int n; + + // the error correction capability of the code + private int t; + + // the generator matrix + private GF2Matrix g; + + private McElieceCCA2Parameters McElieceCCA2Params; + + /** + * Constructor (used by the {@link McElieceCCA2KeyPairGenerator}). + * + * @param n the length of the code + * @param t the error correction capability of the code + * @param g the generator matrix + */ + public BCMcElieceCCA2PublicKey(String oid, int n, int t, GF2Matrix g) + { + this.oid = oid; + this.n = n; + this.t = t; + this.g = g; + } + + /** + * Constructor (used by the {@link McElieceCCA2KeyFactorySpi}). + * + * @param keySpec a {@link McElieceCCA2PublicKeySpec} + */ + public BCMcElieceCCA2PublicKey(McElieceCCA2PublicKeySpec keySpec) + { + this(keySpec.getOIDString(), keySpec.getN(), keySpec.getT(), keySpec.getMatrixG()); + } + + public BCMcElieceCCA2PublicKey(McElieceCCA2PublicKeyParameters params) + { + this(params.getOIDString(), params.getN(), params.getT(), params.getMatrixG()); + this.McElieceCCA2Params = params.getParameters(); + } + + /** + * Return the name of the algorithm. + * + * @return "McEliece" + */ + public String getAlgorithm() + { + return "McEliece"; + } + + /** + * @return the length of the code + */ + public int getN() + { + return n; + } + + /** + * @return the dimension of the code + */ + public int getK() + { + return g.getNumRows(); + } + + /** + * @return the error correction capability of the code + */ + public int getT() + { + return t; + } + + /** + * @return the generator matrix + */ + public GF2Matrix getG() + { + return g; + } + + /** + * @return a human readable form of the key + */ + public String toString() + { + String result = "McEliecePublicKey:\n"; + result += " length of the code : " + n + "\n"; + result += " error correction capability: " + t + "\n"; + result += " generator matrix : " + g.toString(); + return result; + } + + /** + * Compare this key with another object. + * + * @param other the other object + * @return the result of the comparison + */ + public boolean equals(Object other) + { + if (other == null || !(other instanceof BCMcElieceCCA2PublicKey)) + { + return false; + } + + BCMcElieceCCA2PublicKey otherKey = (BCMcElieceCCA2PublicKey)other; + + return (n == otherKey.n) && (t == otherKey.t) && (g.equals(otherKey.g)); + } + + /** + * @return the hash code of this key + */ + public int hashCode() + { + return n + t + g.hashCode(); + } + + /** + * @return the OID of the algorithm + */ + public String getOIDString() + { + return oid; + } + + /** + * @return the OID to encode in the SubjectPublicKeyInfo structure + */ + protected ASN1ObjectIdentifier getOID() + { + return new ASN1ObjectIdentifier(McElieceCCA2KeyFactorySpi.OID); + } + + /** + * @return the algorithm parameters to encode in the SubjectPublicKeyInfo + * structure + */ + protected ASN1Primitive getAlgParams() + { + return null; // FIXME: needed at all? + } + + /** + * Return the keyData to encode in the SubjectPublicKeyInfo structure. + * <p/> + * The ASN.1 definition of the key structure is + * <p/> + * <pre> + * McEliecePublicKey ::= SEQUENCE { + * n Integer -- length of the code + * t Integer -- error correcting capability + * matrixG OctetString -- generator matrix as octet string + * } + * </pre> + * + * @return the keyData to encode in the SubjectPublicKeyInfo structure + */ + public byte[] getEncoded() + { + McElieceCCA2PublicKey key = new McElieceCCA2PublicKey(new ASN1ObjectIdentifier(oid), n, t, g); + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(this.getOID(), DERNull.INSTANCE); + + try + { + SubjectPublicKeyInfo subjectPublicKeyInfo = new SubjectPublicKeyInfo(algorithmIdentifier, key); + + return subjectPublicKeyInfo.getEncoded(); + } + catch (IOException e) + { + return null; + } + + } + + public String getFormat() + { + // TODO Auto-generated method stub + return null; + } + + public McElieceCCA2Parameters getMcElieceCCA2Parameters() + { + return McElieceCCA2Params; + } + +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/BCMcEliecePrivateKey.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/BCMcEliecePrivateKey.java new file mode 100644 index 00000000..21e778a4 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/BCMcEliecePrivateKey.java @@ -0,0 +1,334 @@ +package org.spongycastle.pqc.jcajce.provider.mceliece; + +import java.io.IOException; +import java.security.PrivateKey; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.pqc.asn1.McEliecePrivateKey; +import org.spongycastle.pqc.crypto.mceliece.McElieceKeyPairGenerator; +import org.spongycastle.pqc.crypto.mceliece.McElieceParameters; +import org.spongycastle.pqc.crypto.mceliece.McEliecePrivateKeyParameters; +import org.spongycastle.pqc.jcajce.spec.McEliecePrivateKeySpec; +import org.spongycastle.pqc.math.linearalgebra.GF2Matrix; +import org.spongycastle.pqc.math.linearalgebra.GF2mField; +import org.spongycastle.pqc.math.linearalgebra.Permutation; +import org.spongycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM; + +/** + * This class implements a McEliece private key and is usually instantiated by + * the {@link McElieceKeyPairGenerator} or {@link McElieceKeyFactorySpi}. + */ +public class BCMcEliecePrivateKey + implements CipherParameters, PrivateKey +{ + + /** + * + */ + private static final long serialVersionUID = 1L; + + // the OID of the algorithm + private String oid; + + // the length of the code + private int n; + + // the dimension of the code, where <tt>k >= n - mt</tt> + private int k; + + // the underlying finite field + private GF2mField field; + + // the irreducible Goppa polynomial + private PolynomialGF2mSmallM goppaPoly; + + // the matrix S^-1 + private GF2Matrix sInv; + + // the permutation P1 used to generate the systematic check matrix + private Permutation p1; + + // the permutation P2 used to compute the public generator matrix + private Permutation p2; + + // the canonical check matrix of the code + private GF2Matrix h; + + // the matrix used to compute square roots in <tt>(GF(2^m))^t</tt> + private PolynomialGF2mSmallM[] qInv; + + private McElieceParameters mcElieceParams; + + + /** + * Constructor (used by the {@link McElieceKeyPairGenerator}). + * + * @param oid + * @param n the length of the code + * @param k the dimension of the code + * @param field the field polynomial defining the finite field + * <tt>GF(2<sup>m</sup>)</tt> + * @param goppaPoly the irreducible Goppa polynomial + * @param sInv the matrix <tt>S<sup>-1</sup></tt> + * @param p1 the permutation used to generate the systematic check + * matrix + * @param p2 the permutation used to compute the public generator + * matrix + * @param h the canonical check matrix + * @param qInv the matrix used to compute square roots in + * <tt>(GF(2<sup>m</sup>))<sup>t</sup></tt> + */ + public BCMcEliecePrivateKey(String oid, int n, int k, GF2mField field, + PolynomialGF2mSmallM goppaPoly, GF2Matrix sInv, Permutation p1, + Permutation p2, GF2Matrix h, PolynomialGF2mSmallM[] qInv) + { + this.oid = oid; + this.n = n; + this.k = k; + this.field = field; + this.goppaPoly = goppaPoly; + this.sInv = sInv; + this.p1 = p1; + this.p2 = p2; + this.h = h; + this.qInv = qInv; + } + + /** + * Constructor (used by the {@link McElieceKeyFactorySpi}). + * + * @param keySpec a {@link McEliecePrivateKeySpec} + */ + public BCMcEliecePrivateKey(McEliecePrivateKeySpec keySpec) + { + this(keySpec.getOIDString(), keySpec.getN(), keySpec.getK(), keySpec.getField(), keySpec + .getGoppaPoly(), keySpec.getSInv(), keySpec.getP1(), keySpec + .getP2(), keySpec.getH(), keySpec.getQInv()); + } + + public BCMcEliecePrivateKey(McEliecePrivateKeyParameters params) + { + this(params.getOIDString(), params.getN(), params.getK(), params.getField(), params.getGoppaPoly(), + params.getSInv(), params.getP1(), params.getP2(), params.getH(), params.getQInv()); + + this.mcElieceParams = params.getParameters(); + } + + + /** + * Return the name of the algorithm. + * + * @return "McEliece" + */ + public String getAlgorithm() + { + return "McEliece"; + } + + /** + * @return the length of the code + */ + public int getN() + { + return n; + } + + /** + * @return the dimension of the code + */ + public int getK() + { + return k; + } + + /** + * @return the finite field + */ + public GF2mField getField() + { + return field; + } + + /** + * @return the irreducible Goppa polynomial + */ + public PolynomialGF2mSmallM getGoppaPoly() + { + return goppaPoly; + } + + /** + * @return the k x k random binary non-singular matrix S + */ + public GF2Matrix getSInv() + { + return sInv; + } + + /** + * @return the permutation used to generate the systematic check matrix + */ + public Permutation getP1() + { + return p1; + } + + /** + * @return the permutation used to compute the public generator matrix + */ + public Permutation getP2() + { + return p2; + } + + /** + * @return the canonical check matrix + */ + public GF2Matrix getH() + { + return h; + } + + /** + * @return the matrix for computing square roots in <tt>(GF(2^m))^t</tt> + */ + public PolynomialGF2mSmallM[] getQInv() + { + return qInv; + } + + /** + * @return the OID of the algorithm + */ + public String getOIDString() + { + return oid; + } + + /** + * @return a human readable form of the key + */ + public String toString() + { + String result = " length of the code : " + n + "\n"; + result += " dimension of the code : " + k + "\n"; + result += " irreducible Goppa polynomial: " + goppaPoly + "\n"; + result += " (k x k)-matrix S^-1 : " + sInv + "\n"; + result += " permutation P1 : " + p1 + "\n"; + result += " permutation P2 : " + p2; + return result; + } + + /** + * Compare this key with another object. + * + * @param other the other object + * @return the result of the comparison + */ + public boolean equals(Object other) + { + if (!(other instanceof BCMcEliecePrivateKey)) + { + return false; + } + BCMcEliecePrivateKey otherKey = (BCMcEliecePrivateKey)other; + + return (n == otherKey.n) && (k == otherKey.k) + && field.equals(otherKey.field) + && goppaPoly.equals(otherKey.goppaPoly) + && sInv.equals(otherKey.sInv) && p1.equals(otherKey.p1) + && p2.equals(otherKey.p2) && h.equals(otherKey.h); + } + + /** + * @return the hash code of this key + */ + public int hashCode() + { + return k + n + field.hashCode() + goppaPoly.hashCode() + + sInv.hashCode() + p1.hashCode() + p2.hashCode() + + h.hashCode(); + } + + /** + * @return the OID to encode in the SubjectPublicKeyInfo structure + */ + protected ASN1ObjectIdentifier getOID() + { + return new ASN1ObjectIdentifier(McElieceKeyFactorySpi.OID); + } + + /** + * @return the algorithm parameters to encode in the SubjectPublicKeyInfo + * structure + */ + protected ASN1Primitive getAlgParams() + { + return null; // FIXME: needed at all? + } + + /** + * Return the key data to encode in the SubjectPublicKeyInfo structure. + * <p/> + * The ASN.1 definition of the key structure is + * <p/> + * <pre> + * McEliecePrivateKey ::= SEQUENCE { + * n INTEGER -- length of the code + * k INTEGER -- dimension of the code + * fieldPoly OCTET STRING -- field polynomial defining GF(2ˆm) + * goppaPoly OCTET STRING -- irreducible Goppa polynomial + * sInv OCTET STRING -- matrix Sˆ-1 + * p1 OCTET STRING -- permutation P1 + * p2 OCTET STRING -- permutation P2 + * h OCTET STRING -- canonical check matrix + * qInv SEQUENCE OF OCTET STRING -- matrix used to compute square roots + * } + * </pre> + * + * @return the key data to encode in the SubjectPublicKeyInfo structure + */ + public byte[] getEncoded() + { + McEliecePrivateKey privateKey = new McEliecePrivateKey(new ASN1ObjectIdentifier(oid), n, k, field, goppaPoly, sInv, p1, p2, h, qInv); + PrivateKeyInfo pki; + try + { + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(this.getOID(), DERNull.INSTANCE); + pki = new PrivateKeyInfo(algorithmIdentifier, privateKey); + } + catch (IOException e) + { + e.printStackTrace(); + return null; + } + try + { + byte[] encoded = pki.getEncoded(); + return encoded; + } + catch (IOException e) + { + e.printStackTrace(); + return null; + } + } + + public String getFormat() + { + // TODO Auto-generated method stub + return null; + } + + public McElieceParameters getMcElieceParameters() + { + return mcElieceParams; + } + + +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/BCMcEliecePublicKey.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/BCMcEliecePublicKey.java new file mode 100644 index 00000000..25c6fd14 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/BCMcEliecePublicKey.java @@ -0,0 +1,231 @@ +package org.spongycastle.pqc.jcajce.provider.mceliece; + +import java.io.IOException; +import java.security.PublicKey; + +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.pqc.asn1.McEliecePublicKey; +import org.spongycastle.pqc.crypto.mceliece.McElieceKeyPairGenerator; +import org.spongycastle.pqc.crypto.mceliece.McElieceParameters; +import org.spongycastle.pqc.crypto.mceliece.McEliecePublicKeyParameters; +import org.spongycastle.pqc.jcajce.spec.McEliecePublicKeySpec; +import org.spongycastle.pqc.math.linearalgebra.GF2Matrix; + +/** + * This class implements a McEliece public key and is usually instantiated by + * the {@link McElieceKeyPairGenerator} or {@link McElieceKeyFactorySpi}. + */ +public class BCMcEliecePublicKey + implements CipherParameters, PublicKey +{ + + /** + * + */ + private static final long serialVersionUID = 1L; + + // the OID of the algorithm + private String oid; + + /** + * the length of the code + */ + private int n; + + /** + * the error correction capability of the code + */ + private int t; + + /** + * the generator matrix + */ + private GF2Matrix g; + + private McElieceParameters McElieceParams; + + /** + * Constructor (used by the {@link McElieceKeyPairGenerator}). + * + * @param oid + * @param n the length of the code + * @param t the error correction capability of the code + * @param g the generator matrix + */ + public BCMcEliecePublicKey(String oid, int n, int t, GF2Matrix g) + { + this.oid = oid; + this.n = n; + this.t = t; + this.g = g; + } + + /** + * Constructor (used by the {@link McElieceKeyFactorySpi}). + * + * @param keySpec a {@link McEliecePublicKeySpec} + */ + public BCMcEliecePublicKey(McEliecePublicKeySpec keySpec) + { + this(keySpec.getOIDString(), keySpec.getN(), keySpec.getT(), keySpec.getG()); + } + + public BCMcEliecePublicKey(McEliecePublicKeyParameters params) + { + this(params.getOIDString(), params.getN(), params.getT(), params.getG()); + this.McElieceParams = params.getParameters(); + } + + /** + * Return the name of the algorithm. + * + * @return "McEliece" + */ + public String getAlgorithm() + { + return "McEliece"; + } + + /** + * @return the length of the code + */ + public int getN() + { + return n; + } + + /** + * @return the dimension of the code + */ + public int getK() + { + return g.getNumRows(); + } + + /** + * @return the error correction capability of the code + */ + public int getT() + { + return t; + } + + /** + * @return the generator matrix + */ + public GF2Matrix getG() + { + return g; + } + + /** + * @return a human readable form of the key + */ + public String toString() + { + String result = "McEliecePublicKey:\n"; + result += " length of the code : " + n + "\n"; + result += " error correction capability: " + t + "\n"; + result += " generator matrix : " + g.toString(); + return result; + } + + /** + * Compare this key with another object. + * + * @param other the other object + * @return the result of the comparison + */ + public boolean equals(Object other) + { + if (!(other instanceof BCMcEliecePublicKey)) + { + return false; + } + BCMcEliecePublicKey otherKey = (BCMcEliecePublicKey)other; + + return (n == otherKey.n) && (t == otherKey.t) && g.equals(otherKey.g); + } + + /** + * @return the hash code of this key + */ + public int hashCode() + { + return n + t + g.hashCode(); + } + + + /** + * @return the OID of the algorithm + */ + public String getOIDString() + { + return oid; + } + + /** + * @return the OID to encode in the SubjectPublicKeyInfo structure + */ + protected ASN1ObjectIdentifier getOID() + { + return new ASN1ObjectIdentifier(McElieceKeyFactorySpi.OID); + } + + /** + * @return the algorithm parameters to encode in the SubjectPublicKeyInfo + * structure + */ + protected ASN1Primitive getAlgParams() + { + return null; // FIXME: needed at all? + } + + + /** + * Return the keyData to encode in the SubjectPublicKeyInfo structure. + * <p/> + * The ASN.1 definition of the key structure is + * <p/> + * <pre> + * McEliecePublicKey ::= SEQUENCE { + * n Integer -- length of the code + * t Integer -- error correcting capability + * matrixG OctetString -- generator matrix as octet string + * } + * </pre> + * + * @return the keyData to encode in the SubjectPublicKeyInfo structure + */ + public byte[] getEncoded() + { + McEliecePublicKey key = new McEliecePublicKey(new ASN1ObjectIdentifier(oid), n, t, g); + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(this.getOID(), DERNull.INSTANCE); + + try + { + SubjectPublicKeyInfo subjectPublicKeyInfo = new SubjectPublicKeyInfo(algorithmIdentifier, key); + + return subjectPublicKeyInfo.getEncoded(); + } + catch (IOException e) + { + return null; + } + } + + public String getFormat() + { + return null; + } + + public McElieceParameters getMcElieceParameters() + { + return McElieceParams; + } +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceCCA2KeyFactorySpi.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceCCA2KeyFactorySpi.java new file mode 100644 index 00000000..d3de79bf --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceCCA2KeyFactorySpi.java @@ -0,0 +1,346 @@ +package org.spongycastle.pqc.jcajce.provider.mceliece; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactorySpi; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.pqc.asn1.McElieceCCA2PrivateKey; +import org.spongycastle.pqc.asn1.McElieceCCA2PublicKey; +import org.spongycastle.pqc.jcajce.spec.McElieceCCA2PrivateKeySpec; +import org.spongycastle.pqc.jcajce.spec.McElieceCCA2PublicKeySpec; + +/** + * This class is used to translate between McEliece CCA2 keys and key + * specifications. + * + * @see BCMcElieceCCA2PrivateKey + * @see McElieceCCA2PrivateKeySpec + * @see BCMcElieceCCA2PublicKey + * @see McElieceCCA2PublicKeySpec + */ +public class McElieceCCA2KeyFactorySpi + extends KeyFactorySpi +{ + + /** + * The OID of the algorithm. + */ + public static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.2"; + + /** + * Converts, if possible, a key specification into a + * {@link BCMcElieceCCA2PublicKey}. Currently, the following key + * specifications are supported: {@link McElieceCCA2PublicKeySpec}, + * {@link X509EncodedKeySpec}. + * + * @param keySpec the key specification + * @return the McEliece CCA2 public key + * @throws InvalidKeySpecException if the key specification is not supported. + */ + public PublicKey generatePublic(KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof McElieceCCA2PublicKeySpec) + { + return new BCMcElieceCCA2PublicKey( + (McElieceCCA2PublicKeySpec)keySpec); + } + else if (keySpec instanceof X509EncodedKeySpec) + { + // get the DER-encoded Key according to X.509 from the spec + byte[] encKey = ((X509EncodedKeySpec)keySpec).getEncoded(); + + // decode the SubjectPublicKeyInfo data structure to the pki object + SubjectPublicKeyInfo pki; + try + { + pki = SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(encKey)); + } + catch (IOException e) + { + throw new InvalidKeySpecException(e.toString()); + } + + + try + { + // --- Build and return the actual key. + ASN1Primitive innerType = pki.parsePublicKey(); + ASN1Sequence publicKey = (ASN1Sequence)innerType; + + // decode oidString (but we don't need it right now) + String oidString = ((ASN1ObjectIdentifier)publicKey.getObjectAt(0)) + .toString(); + + // decode <n> + BigInteger bigN = ((ASN1Integer)publicKey.getObjectAt(1)).getValue(); + int n = bigN.intValue(); + + // decode <t> + BigInteger bigT = ((ASN1Integer)publicKey.getObjectAt(2)).getValue(); + int t = bigT.intValue(); + + // decode <matrixG> + byte[] matrixG = ((ASN1OctetString)publicKey.getObjectAt(3)).getOctets(); + + return new BCMcElieceCCA2PublicKey(new McElieceCCA2PublicKeySpec( + OID, n, t, matrixG)); + } + catch (IOException cce) + { + throw new InvalidKeySpecException( + "Unable to decode X509EncodedKeySpec: " + + cce.getMessage()); + } + } + + throw new InvalidKeySpecException("Unsupported key specification: " + + keySpec.getClass() + "."); + } + + /** + * Converts, if possible, a key specification into a + * {@link BCMcElieceCCA2PrivateKey}. Currently, the following key + * specifications are supported: {@link McElieceCCA2PrivateKeySpec}, + * {@link PKCS8EncodedKeySpec}. + * + * @param keySpec the key specification + * @return the McEliece CCA2 private key + * @throws InvalidKeySpecException if the KeySpec is not supported. + */ + public PrivateKey generatePrivate(KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof McElieceCCA2PrivateKeySpec) + { + return new BCMcElieceCCA2PrivateKey( + (McElieceCCA2PrivateKeySpec)keySpec); + } + else if (keySpec instanceof PKCS8EncodedKeySpec) + { + // get the DER-encoded Key according to PKCS#8 from the spec + byte[] encKey = ((PKCS8EncodedKeySpec)keySpec).getEncoded(); + + // decode the PKCS#8 data structure to the pki object + PrivateKeyInfo pki; + + try + { + pki = PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(encKey)); + } + catch (IOException e) + { + throw new InvalidKeySpecException("Unable to decode PKCS8EncodedKeySpec: " + e); + } + + try + { + // get the inner type inside the BIT STRING + ASN1Primitive innerType = pki.parsePrivateKey().toASN1Primitive(); + + // build and return the actual key + ASN1Sequence privKey = (ASN1Sequence)innerType; + + // decode oidString (but we don't need it right now) + String oidString = ((ASN1ObjectIdentifier)privKey.getObjectAt(0)) + .toString(); + + // decode <n> + BigInteger bigN = ((ASN1Integer)privKey.getObjectAt(1)).getValue(); + int n = bigN.intValue(); + + // decode <k> + BigInteger bigK = ((ASN1Integer)privKey.getObjectAt(2)).getValue(); + int k = bigK.intValue(); + + + // decode <fieldPoly> + byte[] encFieldPoly = ((ASN1OctetString)privKey.getObjectAt(3)) + .getOctets(); + // decode <goppaPoly> + byte[] encGoppaPoly = ((ASN1OctetString)privKey.getObjectAt(4)) + .getOctets(); + // decode <p> + byte[] encP = ((ASN1OctetString)privKey.getObjectAt(5)).getOctets(); + // decode <h> + byte[] encH = ((ASN1OctetString)privKey.getObjectAt(6)).getOctets(); + // decode <qInv> + ASN1Sequence qSeq = (ASN1Sequence)privKey.getObjectAt(7); + byte[][] encQInv = new byte[qSeq.size()][]; + for (int i = 0; i < qSeq.size(); i++) + { + encQInv[i] = ((ASN1OctetString)qSeq.getObjectAt(i)).getOctets(); + } + + return new BCMcElieceCCA2PrivateKey( + new McElieceCCA2PrivateKeySpec(OID, n, k, encFieldPoly, + encGoppaPoly, encP, encH, encQInv)); + + } + catch (IOException cce) + { + throw new InvalidKeySpecException( + "Unable to decode PKCS8EncodedKeySpec."); + } + } + + throw new InvalidKeySpecException("Unsupported key specification: " + + keySpec.getClass() + "."); + } + + /** + * Converts, if possible, a given key into a key specification. Currently, + * the following key specifications are supported: + * <ul> + * <li>for McElieceCCA2PublicKey: {@link X509EncodedKeySpec}, + * {@link McElieceCCA2PublicKeySpec}</li> + * <li>for McElieceCCA2PrivateKey: {@link PKCS8EncodedKeySpec}, + * {@link McElieceCCA2PrivateKeySpec}</li>. + * </ul> + * + * @param key the key + * @param keySpec the key specification + * @return the specification of the McEliece CCA2 key + * @throws InvalidKeySpecException if the key type or the key specification is not + * supported. + * @see BCMcElieceCCA2PrivateKey + * @see McElieceCCA2PrivateKeySpec + * @see BCMcElieceCCA2PublicKey + * @see McElieceCCA2PublicKeySpec + */ + public KeySpec getKeySpec(Key key, Class keySpec) + throws InvalidKeySpecException + { + if (key instanceof BCMcElieceCCA2PrivateKey) + { + if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) + { + return new PKCS8EncodedKeySpec(key.getEncoded()); + } + else if (McElieceCCA2PrivateKeySpec.class + .isAssignableFrom(keySpec)) + { + BCMcElieceCCA2PrivateKey privKey = (BCMcElieceCCA2PrivateKey)key; + return new McElieceCCA2PrivateKeySpec(OID, privKey.getN(), privKey + .getK(), privKey.getField(), privKey.getGoppaPoly(), + privKey.getP(), privKey.getH(), privKey.getQInv()); + } + } + else if (key instanceof BCMcElieceCCA2PublicKey) + { + if (X509EncodedKeySpec.class.isAssignableFrom(keySpec)) + { + return new X509EncodedKeySpec(key.getEncoded()); + } + else if (McElieceCCA2PublicKeySpec.class + .isAssignableFrom(keySpec)) + { + BCMcElieceCCA2PublicKey pubKey = (BCMcElieceCCA2PublicKey)key; + return new McElieceCCA2PublicKeySpec(OID, pubKey.getN(), pubKey + .getT(), pubKey.getG()); + } + } + else + { + throw new InvalidKeySpecException("Unsupported key type: " + + key.getClass() + "."); + } + + throw new InvalidKeySpecException("Unknown key specification: " + + keySpec + "."); + } + + /** + * Translates a key into a form known by the FlexiProvider. Currently, only + * the following "source" keys are supported: {@link BCMcElieceCCA2PrivateKey}, + * {@link BCMcElieceCCA2PublicKey}. + * + * @param key the key + * @return a key of a known key type + * @throws InvalidKeyException if the key type is not supported. + */ + public Key translateKey(Key key) + throws InvalidKeyException + { + if ((key instanceof BCMcElieceCCA2PrivateKey) + || (key instanceof BCMcElieceCCA2PublicKey)) + { + return key; + } + throw new InvalidKeyException("Unsupported key type."); + + } + + + public PublicKey generatePublic(SubjectPublicKeyInfo pki) + throws InvalidKeySpecException + { + // get the inner type inside the BIT STRING + try + { + ASN1Primitive innerType = pki.parsePublicKey(); + McElieceCCA2PublicKey key = McElieceCCA2PublicKey.getInstance((ASN1Sequence)innerType); + return new BCMcElieceCCA2PublicKey(key.getOID().getId(), key.getN(), key.getT(), key.getG()); + } + catch (IOException cce) + { + throw new InvalidKeySpecException("Unable to decode X509EncodedKeySpec"); + } + } + + + public PrivateKey generatePrivate(PrivateKeyInfo pki) + throws InvalidKeySpecException + { + // get the inner type inside the BIT STRING + try + { + ASN1Primitive innerType = pki.parsePrivateKey().toASN1Primitive(); + McElieceCCA2PrivateKey key = McElieceCCA2PrivateKey.getInstance(innerType); + return new BCMcElieceCCA2PrivateKey(key.getOID().getId(), key.getN(), key.getK(), key.getField(), key.getGoppaPoly(), key.getP(), key.getH(), key.getQInv()); + } + catch (IOException cce) + { + throw new InvalidKeySpecException("Unable to decode PKCS8EncodedKeySpec"); + } + } + + protected PublicKey engineGeneratePublic(KeySpec keySpec) + throws InvalidKeySpecException + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + protected PrivateKey engineGeneratePrivate(KeySpec keySpec) + throws InvalidKeySpecException + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + protected KeySpec engineGetKeySpec(Key key, Class tClass) + throws InvalidKeySpecException + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + protected Key engineTranslateKey(Key key) + throws InvalidKeyException + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceCCA2KeysToParams.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceCCA2KeysToParams.java new file mode 100644 index 00000000..a733d2a0 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceCCA2KeysToParams.java @@ -0,0 +1,47 @@ +package org.spongycastle.pqc.jcajce.provider.mceliece; + +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; + +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.pqc.crypto.mceliece.McElieceCCA2PrivateKeyParameters; +import org.spongycastle.pqc.crypto.mceliece.McElieceCCA2PublicKeyParameters; + +/** + * utility class for converting jce/jca McElieceCCA2 objects + * objects into their org.spongycastle.crypto counterparts. + */ +public class McElieceCCA2KeysToParams +{ + + + static public AsymmetricKeyParameter generatePublicKeyParameter( + PublicKey key) + throws InvalidKeyException + { + if (key instanceof BCMcElieceCCA2PublicKey) + { + BCMcElieceCCA2PublicKey k = (BCMcElieceCCA2PublicKey)key; + + return new McElieceCCA2PublicKeyParameters(k.getOIDString(), k.getN(), k.getT(), k.getG(), k.getMcElieceCCA2Parameters()); + } + + throw new InvalidKeyException("can't identify McElieceCCA2 public key: " + key.getClass().getName()); + } + + + static public AsymmetricKeyParameter generatePrivateKeyParameter( + PrivateKey key) + throws InvalidKeyException + { + if (key instanceof BCMcElieceCCA2PrivateKey) + { + BCMcElieceCCA2PrivateKey k = (BCMcElieceCCA2PrivateKey)key; + return new McElieceCCA2PrivateKeyParameters(k.getOIDString(), k.getN(), k.getK(), k.getField(), k.getGoppaPoly(), + k.getP(), k.getH(), k.getQInv(), k.getMcElieceCCA2Parameters()); + } + + throw new InvalidKeyException("can't identify McElieceCCA2 private key."); + } +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceCCA2Primitives.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceCCA2Primitives.java new file mode 100644 index 00000000..a63c1852 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceCCA2Primitives.java @@ -0,0 +1,131 @@ +package org.spongycastle.pqc.jcajce.provider.mceliece; + +import org.spongycastle.pqc.crypto.mceliece.McElieceCCA2PrivateKeyParameters; +import org.spongycastle.pqc.crypto.mceliece.McElieceCCA2PublicKeyParameters; +import org.spongycastle.pqc.math.linearalgebra.GF2Matrix; +import org.spongycastle.pqc.math.linearalgebra.GF2Vector; +import org.spongycastle.pqc.math.linearalgebra.GF2mField; +import org.spongycastle.pqc.math.linearalgebra.GoppaCode; +import org.spongycastle.pqc.math.linearalgebra.Permutation; +import org.spongycastle.pqc.math.linearalgebra.PolynomialGF2mSmallM; +import org.spongycastle.pqc.math.linearalgebra.Vector; + +/** + * Core operations for the CCA-secure variants of McEliece. + */ +public final class McElieceCCA2Primitives +{ + + /** + * Default constructor (private). + */ + private McElieceCCA2Primitives() + { + } + + /** + * The McEliece encryption primitive. + * + * @param pubKey the public key + * @param m the message vector + * @param z the error vector + * @return <tt>m*G + z</tt> + */ + public static GF2Vector encryptionPrimitive(BCMcElieceCCA2PublicKey pubKey, + GF2Vector m, GF2Vector z) + { + + GF2Matrix matrixG = pubKey.getG(); + Vector mG = matrixG.leftMultiplyLeftCompactForm(m); + return (GF2Vector)mG.add(z); + } + + public static GF2Vector encryptionPrimitive(McElieceCCA2PublicKeyParameters pubKey, + GF2Vector m, GF2Vector z) + { + + GF2Matrix matrixG = pubKey.getMatrixG(); + Vector mG = matrixG.leftMultiplyLeftCompactForm(m); + return (GF2Vector)mG.add(z); + } + + /** + * The McEliece decryption primitive. + * + * @param privKey the private key + * @param c the ciphertext vector <tt>c = m*G + z</tt> + * @return the message vector <tt>m</tt> and the error vector <tt>z</tt> + */ + public static GF2Vector[] decryptionPrimitive( + BCMcElieceCCA2PrivateKey privKey, GF2Vector c) + { + + // obtain values from private key + int k = privKey.getK(); + Permutation p = privKey.getP(); + GF2mField field = privKey.getField(); + PolynomialGF2mSmallM gp = privKey.getGoppaPoly(); + GF2Matrix h = privKey.getH(); + PolynomialGF2mSmallM[] q = privKey.getQInv(); + + // compute inverse permutation P^-1 + Permutation pInv = p.computeInverse(); + + // multiply c with permutation P^-1 + GF2Vector cPInv = (GF2Vector)c.multiply(pInv); + + // compute syndrome of cP^-1 + GF2Vector syndVec = (GF2Vector)h.rightMultiply(cPInv); + + // decode syndrome + GF2Vector errors = GoppaCode.syndromeDecode(syndVec, field, gp, q); + GF2Vector mG = (GF2Vector)cPInv.add(errors); + + // multiply codeword and error vector with P + mG = (GF2Vector)mG.multiply(p); + errors = (GF2Vector)errors.multiply(p); + + // extract plaintext vector (last k columns of mG) + GF2Vector m = mG.extractRightVector(k); + + // return vectors + return new GF2Vector[]{m, errors}; + } + + public static GF2Vector[] decryptionPrimitive( + McElieceCCA2PrivateKeyParameters privKey, GF2Vector c) + { + + // obtain values from private key + int k = privKey.getK(); + Permutation p = privKey.getP(); + GF2mField field = privKey.getField(); + PolynomialGF2mSmallM gp = privKey.getGoppaPoly(); + GF2Matrix h = privKey.getH(); + PolynomialGF2mSmallM[] q = privKey.getQInv(); + + // compute inverse permutation P^-1 + Permutation pInv = p.computeInverse(); + + // multiply c with permutation P^-1 + GF2Vector cPInv = (GF2Vector)c.multiply(pInv); + + // compute syndrome of cP^-1 + GF2Vector syndVec = (GF2Vector)h.rightMultiply(cPInv); + + // decode syndrome + GF2Vector errors = GoppaCode.syndromeDecode(syndVec, field, gp, q); + GF2Vector mG = (GF2Vector)cPInv.add(errors); + + // multiply codeword and error vector with P + mG = (GF2Vector)mG.multiply(p); + errors = (GF2Vector)errors.multiply(p); + + // extract plaintext vector (last k columns of mG) + GF2Vector m = mG.extractRightVector(k); + + // return vectors + return new GF2Vector[]{m, errors}; + } + +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceFujisakiCipherSpi.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceFujisakiCipherSpi.java new file mode 100644 index 00000000..77e31c6a --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceFujisakiCipherSpi.java @@ -0,0 +1,253 @@ +package org.spongycastle.pqc.jcajce.provider.mceliece; + +import java.io.ByteArrayOutputStream; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; + +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x509.X509ObjectIdentifiers; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.crypto.digests.SHA224Digest; +import org.spongycastle.crypto.digests.SHA256Digest; +import org.spongycastle.crypto.digests.SHA384Digest; +import org.spongycastle.crypto.digests.SHA512Digest; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.pqc.crypto.mceliece.McElieceCCA2KeyParameters; +import org.spongycastle.pqc.crypto.mceliece.McElieceFujisakiCipher; +import org.spongycastle.pqc.jcajce.provider.util.AsymmetricHybridCipher; + +public class McElieceFujisakiCipherSpi + extends AsymmetricHybridCipher + implements PKCSObjectIdentifiers, X509ObjectIdentifiers +{ + // TODO digest needed? + private Digest digest; + private McElieceFujisakiCipher cipher; + + /** + * buffer to store the input data + */ + private ByteArrayOutputStream buf; + + + protected McElieceFujisakiCipherSpi(Digest digest, McElieceFujisakiCipher cipher) + { + this.digest = digest; + this.cipher = cipher; + buf = new ByteArrayOutputStream(); + + } + + /** + * Continue a multiple-part encryption or decryption operation. + * + * @param input byte array containing the next part of the input + * @param inOff index in the array where the input starts + * @param inLen length of the input + * @return the processed byte array. + */ + public byte[] update(byte[] input, int inOff, int inLen) + { + buf.write(input, inOff, inLen); + return new byte[0]; + } + + + /** + * Encrypts or decrypts data in a single-part operation, or finishes a + * multiple-part operation. The data is encrypted or decrypted, depending on + * how this cipher was initialized. + * + * @param input the input buffer + * @param inOff the offset in input where the input starts + * @param inLen the input length + * @return the new buffer with the result + * @throws BadPaddingException on deryption errors. + */ + public byte[] doFinal(byte[] input, int inOff, int inLen) + throws BadPaddingException + { + update(input, inOff, inLen); + byte[] data = buf.toByteArray(); + buf.reset(); + if (opMode == ENCRYPT_MODE) + { + + try + { + return cipher.messageEncrypt(data); + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + else if (opMode == DECRYPT_MODE) + { + + try + { + return cipher.messageDecrypt(data); + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + return null; + } + + + protected int encryptOutputSize(int inLen) + { + return 0; + } + + protected int decryptOutputSize(int inLen) + { + return 0; + } + + protected void initCipherEncrypt(Key key, AlgorithmParameterSpec params, + SecureRandom sr) + throws InvalidKeyException, + InvalidAlgorithmParameterException + { + + CipherParameters param; + param = McElieceCCA2KeysToParams.generatePublicKeyParameter((PublicKey)key); + + param = new ParametersWithRandom(param, sr); + digest.reset(); + cipher.init(true, param); + + } + + protected void initCipherDecrypt(Key key, AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + + CipherParameters param; + param = McElieceCCA2KeysToParams.generatePrivateKeyParameter((PrivateKey)key); + + digest.reset(); + cipher.init(false, param); + } + + public String getName() + { + return "McElieceFujisakiCipher"; + } + + public int getKeySize(Key key) + throws InvalidKeyException + { + McElieceCCA2KeyParameters mcElieceCCA2KeyParameters; + if (key instanceof PublicKey) + { + mcElieceCCA2KeyParameters = (McElieceCCA2KeyParameters)McElieceCCA2KeysToParams.generatePublicKeyParameter((PublicKey)key); + } + else + { + mcElieceCCA2KeyParameters = (McElieceCCA2KeyParameters)McElieceCCA2KeysToParams.generatePrivateKeyParameter((PrivateKey)key); + + } + + + return cipher.getKeySize(mcElieceCCA2KeyParameters); + } + + public byte[] messageEncrypt(byte[] input) + throws IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException + { + byte[] output = null; + try + { + output = cipher.messageEncrypt(input); + } + catch (Exception e) + { + e.printStackTrace(); + } + return output; + } + + + public byte[] messageDecrypt(byte[] input) + throws IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException + { + byte[] output = null; + try + { + output = cipher.messageDecrypt(input); + } + catch (Exception e) + { + e.printStackTrace(); + } + return output; + } + + + ////////////////////////////////////////////////////////////////////////////////// + + static public class McElieceFujisaki + extends McElieceFujisakiCipherSpi + { + public McElieceFujisaki() + { + super(new SHA1Digest(), new McElieceFujisakiCipher()); + } + } + + static public class McElieceFujisaki224 + extends McElieceFujisakiCipherSpi + { + public McElieceFujisaki224() + { + super(new SHA224Digest(), new McElieceFujisakiCipher()); + } + } + + static public class McElieceFujisaki256 + extends McElieceFujisakiCipherSpi + { + public McElieceFujisaki256() + { + super(new SHA256Digest(), new McElieceFujisakiCipher()); + } + } + + static public class McElieceFujisaki384 + extends McElieceFujisakiCipherSpi + { + public McElieceFujisaki384() + { + super(new SHA384Digest(), new McElieceFujisakiCipher()); + } + } + + static public class McElieceFujisaki512 + extends McElieceFujisakiCipherSpi + { + public McElieceFujisaki512() + { + super(new SHA512Digest(), new McElieceFujisakiCipher()); + } + } + + +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceKeyFactorySpi.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceKeyFactorySpi.java new file mode 100644 index 00000000..1c2007a0 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceKeyFactorySpi.java @@ -0,0 +1,343 @@ +package org.spongycastle.pqc.jcajce.provider.mceliece; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactorySpi; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.ASN1OctetString; +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.ASN1Sequence; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.pqc.asn1.McEliecePrivateKey; +import org.spongycastle.pqc.asn1.McEliecePublicKey; +import org.spongycastle.pqc.jcajce.spec.McEliecePrivateKeySpec; +import org.spongycastle.pqc.jcajce.spec.McEliecePublicKeySpec; + +/** + * This class is used to translate between McEliece keys and key specifications. + * + * @see BCMcEliecePrivateKey + * @see McEliecePrivateKeySpec + * @see BCMcEliecePublicKey + * @see McEliecePublicKeySpec + */ +public class McElieceKeyFactorySpi + extends KeyFactorySpi +{ + /** + * The OID of the algorithm. + */ + public static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.1"; + + /** + * Converts, if possible, a key specification into a + * {@link BCMcEliecePublicKey}. Currently, the following key specifications + * are supported: {@link McEliecePublicKeySpec}, {@link X509EncodedKeySpec}. + * + * @param keySpec the key specification + * @return the McEliece public key + * @throws InvalidKeySpecException if the key specification is not supported. + */ + public PublicKey generatePublic(KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof McEliecePublicKeySpec) + { + return new BCMcEliecePublicKey((McEliecePublicKeySpec)keySpec); + } + else if (keySpec instanceof X509EncodedKeySpec) + { + // get the DER-encoded Key according to X.509 from the spec + byte[] encKey = ((X509EncodedKeySpec)keySpec).getEncoded(); + + // decode the SubjectPublicKeyInfo data structure to the pki object + SubjectPublicKeyInfo pki; + try + { + pki = SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(encKey)); + } + catch (IOException e) + { + throw new InvalidKeySpecException(e.toString()); + } + + try + { + // --- Build and return the actual key. + ASN1Primitive innerType = pki.parsePublicKey(); + ASN1Sequence publicKey = (ASN1Sequence)innerType; + + // decode oidString (but we don't need it right now) + String oidString = ((ASN1ObjectIdentifier)publicKey.getObjectAt(0)) + .toString(); + + // decode <n> + BigInteger bigN = ((ASN1Integer)publicKey.getObjectAt(1)).getValue(); + int n = bigN.intValue(); + + // decode <t> + BigInteger bigT = ((ASN1Integer)publicKey.getObjectAt(2)).getValue(); + int t = bigT.intValue(); + + // decode <matrixG> + byte[] matrixG = ((ASN1OctetString)publicKey.getObjectAt(3)).getOctets(); + + + return new BCMcEliecePublicKey(new McEliecePublicKeySpec(OID, t, n, + matrixG)); + } + catch (IOException cce) + { + throw new InvalidKeySpecException( + "Unable to decode X509EncodedKeySpec: " + + cce.getMessage()); + } + } + + throw new InvalidKeySpecException("Unsupported key specification: " + + keySpec.getClass() + "."); + } + + /** + * Converts, if possible, a key specification into a + * {@link BCMcEliecePrivateKey}. Currently, the following key specifications + * are supported: {@link McEliecePrivateKeySpec}, + * {@link PKCS8EncodedKeySpec}. + * + * @param keySpec the key specification + * @return the McEliece private key + * @throws InvalidKeySpecException if the KeySpec is not supported. + */ + public PrivateKey generatePrivate(KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof McEliecePrivateKeySpec) + { + return new BCMcEliecePrivateKey((McEliecePrivateKeySpec)keySpec); + } + else if (keySpec instanceof PKCS8EncodedKeySpec) + { + // get the DER-encoded Key according to PKCS#8 from the spec + byte[] encKey = ((PKCS8EncodedKeySpec)keySpec).getEncoded(); + + // decode the PKCS#8 data structure to the pki object + PrivateKeyInfo pki; + + try + { + pki = PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(encKey)); + } + catch (IOException e) + { + throw new InvalidKeySpecException("Unable to decode PKCS8EncodedKeySpec: " + e); + } + + try + { + ASN1Primitive innerType = pki.parsePrivateKey().toASN1Primitive(); + + // build and return the actual key + ASN1Sequence privKey = (ASN1Sequence)innerType; + + // decode oidString (but we don't need it right now) + String oidString = ((ASN1ObjectIdentifier)privKey.getObjectAt(0)) + .toString(); + + // decode <n> + BigInteger bigN = ((ASN1Integer)privKey.getObjectAt(1)).getValue(); + int n = bigN.intValue(); + + // decode <k> + BigInteger bigK = ((ASN1Integer)privKey.getObjectAt(2)).getValue(); + int k = bigK.intValue(); + + // decode <fieldPoly> + byte[] encFieldPoly = ((ASN1OctetString)privKey.getObjectAt(3)) + .getOctets(); + // decode <goppaPoly> + byte[] encGoppaPoly = ((ASN1OctetString)privKey.getObjectAt(4)) + .getOctets(); + + // decode <sInv> + byte[] encSInv = ((ASN1OctetString)privKey.getObjectAt(5)).getOctets(); + // decode <p1> + byte[] encP1 = ((ASN1OctetString)privKey.getObjectAt(6)).getOctets(); + // decode <p2> + byte[] encP2 = ((ASN1OctetString)privKey.getObjectAt(7)).getOctets(); + + //decode <h> + byte[] encH = ((ASN1OctetString)privKey.getObjectAt(8)).getOctets(); + + // decode <qInv> + ASN1Sequence qSeq = (ASN1Sequence)privKey.getObjectAt(9); + byte[][] encQInv = new byte[qSeq.size()][]; + for (int i = 0; i < qSeq.size(); i++) + { + encQInv[i] = ((ASN1OctetString)qSeq.getObjectAt(i)).getOctets(); + } + + return new BCMcEliecePrivateKey(new McEliecePrivateKeySpec(OID, n, k, + encFieldPoly, encGoppaPoly, encSInv, encP1, encP2, + encH, encQInv)); + + } + catch (IOException cce) + { + throw new InvalidKeySpecException( + "Unable to decode PKCS8EncodedKeySpec."); + } + } + + throw new InvalidKeySpecException("Unsupported key specification: " + + keySpec.getClass() + "."); + } + + /** + * Converts, if possible, a given key into a key specification. Currently, + * the following key specifications are supported: + * <ul> + * <li>for McEliecePublicKey: {@link X509EncodedKeySpec}, + * {@link McEliecePublicKeySpec}</li> + * <li>for McEliecePrivateKey: {@link PKCS8EncodedKeySpec}, + * {@link McEliecePrivateKeySpec}</li>. + * </ul> + * + * @param key the key + * @param keySpec the key specification + * @return the specification of the McEliece key + * @throws InvalidKeySpecException if the key type or the key specification is not + * supported. + * @see BCMcEliecePrivateKey + * @see McEliecePrivateKeySpec + * @see BCMcEliecePublicKey + * @see McEliecePublicKeySpec + */ + public KeySpec getKeySpec(Key key, Class keySpec) + throws InvalidKeySpecException + { + if (key instanceof BCMcEliecePrivateKey) + { + if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) + { + return new PKCS8EncodedKeySpec(key.getEncoded()); + } + else if (McEliecePrivateKeySpec.class.isAssignableFrom(keySpec)) + { + BCMcEliecePrivateKey privKey = (BCMcEliecePrivateKey)key; + return new McEliecePrivateKeySpec(OID, privKey.getN(), privKey + .getK(), privKey.getField(), privKey.getGoppaPoly(), + privKey.getSInv(), privKey.getP1(), privKey.getP2(), + privKey.getH(), privKey.getQInv()); + } + } + else if (key instanceof BCMcEliecePublicKey) + { + if (X509EncodedKeySpec.class.isAssignableFrom(keySpec)) + { + return new X509EncodedKeySpec(key.getEncoded()); + } + else if (McEliecePublicKeySpec.class.isAssignableFrom(keySpec)) + { + BCMcEliecePublicKey pubKey = (BCMcEliecePublicKey)key; + return new McEliecePublicKeySpec(OID, pubKey.getN(), pubKey.getT(), + pubKey.getG()); + } + } + else + { + throw new InvalidKeySpecException("Unsupported key type: " + + key.getClass() + "."); + } + + throw new InvalidKeySpecException("Unknown key specification: " + + keySpec + "."); + } + + /** + * Translates a key into a form known by the FlexiProvider. Currently, only + * the following "source" keys are supported: {@link BCMcEliecePrivateKey}, + * {@link BCMcEliecePublicKey}. + * + * @param key the key + * @return a key of a known key type + * @throws InvalidKeyException if the key type is not supported. + */ + public Key translateKey(Key key) + throws InvalidKeyException + { + if ((key instanceof BCMcEliecePrivateKey) + || (key instanceof BCMcEliecePublicKey)) + { + return key; + } + throw new InvalidKeyException("Unsupported key type."); + + } + + public PublicKey generatePublic(SubjectPublicKeyInfo pki) + throws InvalidKeySpecException + { + // get the inner type inside the BIT STRING + try + { + ASN1Primitive innerType = pki.parsePublicKey(); + McEliecePublicKey key = McEliecePublicKey.getInstance(innerType); + return new BCMcEliecePublicKey(key.getOID().getId(), key.getN(), key.getT(), key.getG()); + } + catch (IOException cce) + { + throw new InvalidKeySpecException("Unable to decode X509EncodedKeySpec"); + } + } + + public PrivateKey generatePrivate(PrivateKeyInfo pki) + throws InvalidKeySpecException + { + // get the inner type inside the BIT STRING + try + { + ASN1Primitive innerType = pki.parsePrivateKey().toASN1Primitive(); + McEliecePrivateKey key = McEliecePrivateKey.getInstance(innerType); + return new BCMcEliecePrivateKey(key.getOID().getId(), key.getN(), key.getK(), key.getField(), key.getGoppaPoly(), key.getSInv(), key.getP1(), key.getP2(), key.getH(), key.getQInv()); + } + catch (IOException cce) + { + throw new InvalidKeySpecException("Unable to decode PKCS8EncodedKeySpec"); + } + } + + protected PublicKey engineGeneratePublic(KeySpec keySpec) + throws InvalidKeySpecException + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + protected PrivateKey engineGeneratePrivate(KeySpec keySpec) + throws InvalidKeySpecException + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + protected KeySpec engineGetKeySpec(Key key, Class tClass) + throws InvalidKeySpecException + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + protected Key engineTranslateKey(Key key) + throws InvalidKeyException + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceKeyPairGeneratorSpi.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceKeyPairGeneratorSpi.java new file mode 100644 index 00000000..9e0115fd --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceKeyPairGeneratorSpi.java @@ -0,0 +1,146 @@ +package org.spongycastle.pqc.jcajce.provider.mceliece; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.pqc.crypto.mceliece.McElieceCCA2KeyGenerationParameters; +import org.spongycastle.pqc.crypto.mceliece.McElieceCCA2KeyPairGenerator; +import org.spongycastle.pqc.crypto.mceliece.McElieceCCA2Parameters; +import org.spongycastle.pqc.crypto.mceliece.McElieceCCA2PrivateKeyParameters; +import org.spongycastle.pqc.crypto.mceliece.McElieceCCA2PublicKeyParameters; +import org.spongycastle.pqc.crypto.mceliece.McElieceKeyGenerationParameters; +import org.spongycastle.pqc.crypto.mceliece.McElieceKeyPairGenerator; +import org.spongycastle.pqc.crypto.mceliece.McElieceParameters; +import org.spongycastle.pqc.crypto.mceliece.McEliecePrivateKeyParameters; +import org.spongycastle.pqc.crypto.mceliece.McEliecePublicKeyParameters; +import org.spongycastle.pqc.jcajce.spec.ECCKeyGenParameterSpec; +import org.spongycastle.pqc.jcajce.spec.McElieceCCA2ParameterSpec; + +public abstract class McElieceKeyPairGeneratorSpi + extends KeyPairGenerator +{ + public McElieceKeyPairGeneratorSpi( + String algorithmName) + { + super(algorithmName); + } + + /** + * + * + * + */ + + public static class McElieceCCA2 + extends McElieceKeyPairGeneratorSpi + { + + McElieceCCA2KeyPairGenerator kpg; + + + public McElieceCCA2() + { + super("McElieceCCA-2"); + } + + public McElieceCCA2(String s) + { + super(s); + } + + public void initialize(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException + { + kpg = new McElieceCCA2KeyPairGenerator(); + super.initialize(params); + ECCKeyGenParameterSpec ecc = (ECCKeyGenParameterSpec)params; + + McElieceCCA2KeyGenerationParameters mccca2KGParams = new McElieceCCA2KeyGenerationParameters(new SecureRandom(), new McElieceCCA2Parameters(ecc.getM(), ecc.getT())); + kpg.init(mccca2KGParams); + } + + public void initialize(int keySize, SecureRandom random) + { + McElieceCCA2ParameterSpec paramSpec = new McElieceCCA2ParameterSpec(); + + // call the initializer with the chosen parameters + try + { + this.initialize(paramSpec); + } + catch (InvalidAlgorithmParameterException ae) + { + } + } + + public KeyPair generateKeyPair() + { + AsymmetricCipherKeyPair generateKeyPair = kpg.generateKeyPair(); + McElieceCCA2PrivateKeyParameters sk = (McElieceCCA2PrivateKeyParameters)generateKeyPair.getPrivate(); + McElieceCCA2PublicKeyParameters pk = (McElieceCCA2PublicKeyParameters)generateKeyPair.getPublic(); + + return new KeyPair(new BCMcElieceCCA2PublicKey(pk), new BCMcElieceCCA2PrivateKey(sk)); + + } + + } + + /** + * + * + * + */ + + public static class McEliece + extends McElieceKeyPairGeneratorSpi + { + + McElieceKeyPairGenerator kpg; + + + public McEliece() + { + super("McEliece"); + } + + public void initialize(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException + { + kpg = new McElieceKeyPairGenerator(); + super.initialize(params); + ECCKeyGenParameterSpec ecc = (ECCKeyGenParameterSpec)params; + + McElieceKeyGenerationParameters mccKGParams = new McElieceKeyGenerationParameters(new SecureRandom(), new McElieceParameters(ecc.getM(), ecc.getT())); + kpg.init(mccKGParams); + } + + public void initialize(int keySize, SecureRandom random) + { + ECCKeyGenParameterSpec paramSpec = new ECCKeyGenParameterSpec(); + + // call the initializer with the chosen parameters + try + { + this.initialize(paramSpec); + } + catch (InvalidAlgorithmParameterException ae) + { + } + } + + public KeyPair generateKeyPair() + { + AsymmetricCipherKeyPair generateKeyPair = kpg.generateKeyPair(); + McEliecePrivateKeyParameters sk = (McEliecePrivateKeyParameters)generateKeyPair.getPrivate(); + McEliecePublicKeyParameters pk = (McEliecePublicKeyParameters)generateKeyPair.getPublic(); + + return new KeyPair(new BCMcEliecePublicKey(pk), new BCMcEliecePrivateKey(sk)); + } + + } + +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceKeysToParams.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceKeysToParams.java new file mode 100644 index 00000000..649d1d45 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceKeysToParams.java @@ -0,0 +1,47 @@ +package org.spongycastle.pqc.jcajce.provider.mceliece; + +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; + +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.pqc.crypto.mceliece.McEliecePrivateKeyParameters; +import org.spongycastle.pqc.crypto.mceliece.McEliecePublicKeyParameters; + +/** + * utility class for converting jce/jca McEliece objects + * objects into their org.spongycastle.crypto counterparts. + */ +public class McElieceKeysToParams +{ + + + static public AsymmetricKeyParameter generatePublicKeyParameter( + PublicKey key) + throws InvalidKeyException + { + if (key instanceof BCMcEliecePublicKey) + { + BCMcEliecePublicKey k = (BCMcEliecePublicKey)key; + + return new McEliecePublicKeyParameters(k.getOIDString(), k.getN(), k.getT(), k.getG(), k.getMcElieceParameters()); + } + + throw new InvalidKeyException("can't identify McEliece public key: " + key.getClass().getName()); + } + + + static public AsymmetricKeyParameter generatePrivateKeyParameter( + PrivateKey key) + throws InvalidKeyException + { + if (key instanceof BCMcEliecePrivateKey) + { + BCMcEliecePrivateKey k = (BCMcEliecePrivateKey)key; + return new McEliecePrivateKeyParameters(k.getOIDString(), k.getN(), k.getK(), k.getField(), k.getGoppaPoly(), + k.getSInv(), k.getP1(), k.getP2(), k.getH(), k.getQInv(), k.getMcElieceParameters()); + } + + throw new InvalidKeyException("can't identify McEliece private key."); + } +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceKobaraImaiCipherSpi.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceKobaraImaiCipherSpi.java new file mode 100644 index 00000000..60e77a5c --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McElieceKobaraImaiCipherSpi.java @@ -0,0 +1,307 @@ +package org.spongycastle.pqc.jcajce.provider.mceliece; + +import java.io.ByteArrayOutputStream; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; + +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x509.X509ObjectIdentifiers; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.crypto.digests.SHA224Digest; +import org.spongycastle.crypto.digests.SHA256Digest; +import org.spongycastle.crypto.digests.SHA384Digest; +import org.spongycastle.crypto.digests.SHA512Digest; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.pqc.crypto.mceliece.McElieceCCA2KeyParameters; +import org.spongycastle.pqc.crypto.mceliece.McElieceKobaraImaiCipher; +import org.spongycastle.pqc.jcajce.provider.util.AsymmetricHybridCipher; + +public class McElieceKobaraImaiCipherSpi + extends AsymmetricHybridCipher + implements PKCSObjectIdentifiers, X509ObjectIdentifiers +{ + + // TODO digest needed? + private Digest digest; + private McElieceKobaraImaiCipher cipher; + + /** + * buffer to store the input data + */ + private ByteArrayOutputStream buf = new ByteArrayOutputStream(); + + + public McElieceKobaraImaiCipherSpi() + { + buf = new ByteArrayOutputStream(); + } + + protected McElieceKobaraImaiCipherSpi(Digest digest, McElieceKobaraImaiCipher cipher) + { + this.digest = digest; + this.cipher = cipher; + buf = new ByteArrayOutputStream(); + } + + /** + * Continue a multiple-part encryption or decryption operation. + * + * @param input byte array containing the next part of the input + * @param inOff index in the array where the input starts + * @param inLen length of the input + * @return the processed byte array. + */ + public byte[] update(byte[] input, int inOff, int inLen) + { + buf.write(input, inOff, inLen); + return new byte[0]; + } + + + /** + * Encrypts or decrypts data in a single-part operation, or finishes a + * multiple-part operation. The data is encrypted or decrypted, depending on + * how this cipher was initialized. + * + * @param input the input buffer + * @param inOff the offset in input where the input starts + * @param inLen the input length + * @return the new buffer with the result + * @throws BadPaddingException if this cipher is in decryption mode, and (un)padding has + * been requested, but the decrypted data is not bounded by + * the appropriate padding bytes + */ + public byte[] doFinal(byte[] input, int inOff, int inLen) + throws BadPaddingException + { + update(input, inOff, inLen); + if (opMode == ENCRYPT_MODE) + { + + try + { + return cipher.messageEncrypt(this.pad()); + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + else if (opMode == DECRYPT_MODE) + { + byte[] inputOfDecr = buf.toByteArray(); + buf.reset(); + + try + { + return unpad(cipher.messageDecrypt(inputOfDecr)); + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + return null; + } + + protected int encryptOutputSize(int inLen) + { + return 0; + } + + protected int decryptOutputSize(int inLen) + { + return 0; + } + + protected void initCipherEncrypt(Key key, AlgorithmParameterSpec params, + SecureRandom sr) + throws InvalidKeyException, + InvalidAlgorithmParameterException + { + + buf.reset(); + CipherParameters param; + param = McElieceCCA2KeysToParams.generatePublicKeyParameter((PublicKey)key); + + param = new ParametersWithRandom(param, sr); + digest.reset(); + cipher.init(true, param); + } + + protected void initCipherDecrypt(Key key, AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + + buf.reset(); + CipherParameters param; + param = McElieceCCA2KeysToParams.generatePrivateKeyParameter((PrivateKey)key); + + digest.reset(); + cipher.init(false, param); + } + + public String getName() + { + return "McElieceKobaraImaiCipher"; + } + + public int getKeySize(Key key) + throws InvalidKeyException + { + McElieceCCA2KeyParameters mcElieceCCA2KeyParameters; + if (key instanceof PublicKey) + { + mcElieceCCA2KeyParameters = (McElieceCCA2KeyParameters)McElieceCCA2KeysToParams.generatePublicKeyParameter((PublicKey)key); + return cipher.getKeySize(mcElieceCCA2KeyParameters); + } + else if (key instanceof PrivateKey) + { + mcElieceCCA2KeyParameters = (McElieceCCA2KeyParameters)McElieceCCA2KeysToParams.generatePrivateKeyParameter((PrivateKey)key); + return cipher.getKeySize(mcElieceCCA2KeyParameters); + } + else + { + throw new InvalidKeyException(); + } + + + } + + /** + * Pad and return the message stored in the message buffer. + * + * @return the padded message + */ + private byte[] pad() + { + buf.write(0x01); + byte[] result = buf.toByteArray(); + buf.reset(); + return result; + } + + /** + * Unpad a message. + * + * @param pmBytes the padded message + * @return the message + * @throws BadPaddingException if the padded message is invalid. + */ + private byte[] unpad(byte[] pmBytes) + throws BadPaddingException + { + // find first non-zero byte + int index; + for (index = pmBytes.length - 1; index >= 0 && pmBytes[index] == 0; index--) + { + ; + } + + // check if padding byte is valid + if (pmBytes[index] != 0x01) + { + throw new BadPaddingException("invalid ciphertext"); + } + + // extract and return message + byte[] mBytes = new byte[index]; + System.arraycopy(pmBytes, 0, mBytes, 0, index); + return mBytes; + } + + + public byte[] messageEncrypt() + throws IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException + { + byte[] output = null; + try + { + output = cipher.messageEncrypt((this.pad())); + } + catch (Exception e) + { + e.printStackTrace(); + } + return output; + } + + + public byte[] messageDecrypt() + throws IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException + { + byte[] output = null; + byte[] inputOfDecr = buf.toByteArray(); + buf.reset(); + try + { + output = unpad(cipher.messageDecrypt(inputOfDecr)); + } + catch (Exception e) + { + e.printStackTrace(); + } + return output; + } + + + static public class McElieceKobaraImai + extends McElieceKobaraImaiCipherSpi + { + public McElieceKobaraImai() + { + super(new SHA1Digest(), new McElieceKobaraImaiCipher()); + } + } + + static public class McElieceKobaraImai224 + extends McElieceKobaraImaiCipherSpi + { + public McElieceKobaraImai224() + { + super(new SHA224Digest(), new McElieceKobaraImaiCipher()); + } + } + + static public class McElieceKobaraImai256 + extends McElieceKobaraImaiCipherSpi + { + public McElieceKobaraImai256() + { + super(new SHA256Digest(), new McElieceKobaraImaiCipher()); + } + } + + static public class McElieceKobaraImai384 + extends McElieceKobaraImaiCipherSpi + { + public McElieceKobaraImai384() + { + super(new SHA384Digest(), new McElieceKobaraImaiCipher()); + } + } + + static public class McElieceKobaraImai512 + extends McElieceKobaraImaiCipherSpi + { + public McElieceKobaraImai512() + { + super(new SHA512Digest(), new McElieceKobaraImaiCipher()); + } + } + + +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McEliecePKCSCipherSpi.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McEliecePKCSCipherSpi.java new file mode 100644 index 00000000..cbabfdcc --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McEliecePKCSCipherSpi.java @@ -0,0 +1,171 @@ +package org.spongycastle.pqc.jcajce.provider.mceliece; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; + +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x509.X509ObjectIdentifiers; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.crypto.digests.SHA224Digest; +import org.spongycastle.crypto.digests.SHA256Digest; +import org.spongycastle.crypto.digests.SHA384Digest; +import org.spongycastle.crypto.digests.SHA512Digest; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.pqc.crypto.mceliece.McElieceKeyParameters; +import org.spongycastle.pqc.crypto.mceliece.McEliecePKCSCipher; +import org.spongycastle.pqc.jcajce.provider.util.AsymmetricBlockCipher; + +public class McEliecePKCSCipherSpi + extends AsymmetricBlockCipher + implements PKCSObjectIdentifiers, X509ObjectIdentifiers +{ + // TODO digest needed? + private Digest digest; + private McEliecePKCSCipher cipher; + + public McEliecePKCSCipherSpi(Digest digest, McEliecePKCSCipher cipher) + { + this.digest = digest; + this.cipher = cipher; + } + + protected void initCipherEncrypt(Key key, AlgorithmParameterSpec params, + SecureRandom sr) + throws InvalidKeyException, + InvalidAlgorithmParameterException + { + + CipherParameters param; + param = McElieceKeysToParams.generatePublicKeyParameter((PublicKey)key); + + param = new ParametersWithRandom(param, sr); + digest.reset(); + cipher.init(true, param); + this.maxPlainTextSize = cipher.maxPlainTextSize; + this.cipherTextSize = cipher.cipherTextSize; + } + + protected void initCipherDecrypt(Key key, AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + CipherParameters param; + param = McElieceKeysToParams.generatePrivateKeyParameter((PrivateKey)key); + + digest.reset(); + cipher.init(false, param); + this.maxPlainTextSize = cipher.maxPlainTextSize; + this.cipherTextSize = cipher.cipherTextSize; + } + + protected byte[] messageEncrypt(byte[] input) + throws IllegalBlockSizeException, BadPaddingException + { + byte[] output = null; + try + { + output = cipher.messageEncrypt(input); + } + catch (Exception e) + { + e.printStackTrace(); + } + return output; + } + + protected byte[] messageDecrypt(byte[] input) + throws IllegalBlockSizeException, BadPaddingException + { + byte[] output = null; + try + { + output = cipher.messageDecrypt(input); + } + catch (Exception e) + { + e.printStackTrace(); + } + return output; + } + + public String getName() + { + return "McEliecePKCS"; + } + + public int getKeySize(Key key) + throws InvalidKeyException + { + McElieceKeyParameters mcElieceKeyParameters; + if (key instanceof PublicKey) + { + mcElieceKeyParameters = (McElieceKeyParameters)McElieceKeysToParams.generatePublicKeyParameter((PublicKey)key); + } + else + { + mcElieceKeyParameters = (McElieceKeyParameters)McElieceKeysToParams.generatePrivateKeyParameter((PrivateKey)key); + + } + + + return cipher.getKeySize(mcElieceKeyParameters); + } + + ////////////////////////////////////////////////////////////////////////////////// + + static public class McEliecePKCS + extends McEliecePKCSCipherSpi + { + public McEliecePKCS() + { + super(new SHA1Digest(), new McEliecePKCSCipher()); + } + } + + static public class McEliecePKCS224 + extends McEliecePKCSCipherSpi + { + public McEliecePKCS224() + { + super(new SHA224Digest(), new McEliecePKCSCipher()); + } + } + + static public class McEliecePKCS256 + extends McEliecePKCSCipherSpi + { + public McEliecePKCS256() + { + super(new SHA256Digest(), new McEliecePKCSCipher()); + } + } + + static public class McEliecePKCS384 + extends McEliecePKCSCipherSpi + { + public McEliecePKCS384() + { + super(new SHA384Digest(), new McEliecePKCSCipher()); + } + } + + static public class McEliecePKCS512 + extends McEliecePKCSCipherSpi + { + public McEliecePKCS512() + { + super(new SHA512Digest(), new McEliecePKCSCipher()); + } + } + + +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McEliecePointchevalCipherSpi.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McEliecePointchevalCipherSpi.java new file mode 100644 index 00000000..314b7a30 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/mceliece/McEliecePointchevalCipherSpi.java @@ -0,0 +1,247 @@ +package org.spongycastle.pqc.jcajce.provider.mceliece; + +import java.io.ByteArrayOutputStream; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; + +import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.spongycastle.asn1.x509.X509ObjectIdentifiers; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.SHA1Digest; +import org.spongycastle.crypto.digests.SHA224Digest; +import org.spongycastle.crypto.digests.SHA256Digest; +import org.spongycastle.crypto.digests.SHA384Digest; +import org.spongycastle.crypto.digests.SHA512Digest; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.pqc.crypto.mceliece.McElieceCCA2KeyParameters; +import org.spongycastle.pqc.crypto.mceliece.McEliecePointchevalCipher; +import org.spongycastle.pqc.jcajce.provider.util.AsymmetricHybridCipher; + +public class McEliecePointchevalCipherSpi + extends AsymmetricHybridCipher + implements PKCSObjectIdentifiers, X509ObjectIdentifiers +{ + // TODO digest needed? + private Digest digest; + private McEliecePointchevalCipher cipher; + + /** + * buffer to store the input data + */ + private ByteArrayOutputStream buf = new ByteArrayOutputStream(); + + + protected McEliecePointchevalCipherSpi(Digest digest, McEliecePointchevalCipher cipher) + { + this.digest = digest; + this.cipher = cipher; + buf = new ByteArrayOutputStream(); + } + + /** + * Continue a multiple-part encryption or decryption operation. + * + * @param input byte array containing the next part of the input + * @param inOff index in the array where the input starts + * @param inLen length of the input + * @return the processed byte array. + */ + public byte[] update(byte[] input, int inOff, int inLen) + { + buf.write(input, inOff, inLen); + return new byte[0]; + } + + + /** + * Encrypts or decrypts data in a single-part operation, or finishes a + * multiple-part operation. The data is encrypted or decrypted, depending on + * how this cipher was initialized. + * + * @param input the input buffer + * @param inOff the offset in input where the input starts + * @param inLen the input length + * @return the new buffer with the result + * @throws BadPaddingException on deryption errors. + */ + public byte[] doFinal(byte[] input, int inOff, int inLen) + throws BadPaddingException + { + update(input, inOff, inLen); + byte[] data = buf.toByteArray(); + buf.reset(); + if (opMode == ENCRYPT_MODE) + { + + try + { + return cipher.messageEncrypt(data); + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + else if (opMode == DECRYPT_MODE) + { + + try + { + return cipher.messageDecrypt(data); + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + return null; + } + + protected int encryptOutputSize(int inLen) + { + return 0; + } + + protected int decryptOutputSize(int inLen) + { + return 0; + } + + protected void initCipherEncrypt(Key key, AlgorithmParameterSpec params, + SecureRandom sr) + throws InvalidKeyException, + InvalidAlgorithmParameterException + { + CipherParameters param; + param = McElieceCCA2KeysToParams.generatePublicKeyParameter((PublicKey)key); + + param = new ParametersWithRandom(param, sr); + digest.reset(); + cipher.init(true, param); + } + + protected void initCipherDecrypt(Key key, AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + CipherParameters param; + param = McElieceCCA2KeysToParams.generatePrivateKeyParameter((PrivateKey)key); + + digest.reset(); + cipher.init(false, param); + } + + public String getName() + { + return "McEliecePointchevalCipher"; + } + + + public int getKeySize(Key key) + throws InvalidKeyException + { + McElieceCCA2KeyParameters mcElieceCCA2KeyParameters; + if (key instanceof PublicKey) + { + mcElieceCCA2KeyParameters = (McElieceCCA2KeyParameters)McElieceCCA2KeysToParams.generatePublicKeyParameter((PublicKey)key); + } + else + { + mcElieceCCA2KeyParameters = (McElieceCCA2KeyParameters)McElieceCCA2KeysToParams.generatePrivateKeyParameter((PrivateKey)key); + } + + return cipher.getKeySize(mcElieceCCA2KeyParameters); + } + + public byte[] messageEncrypt(byte[] input) + throws IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException + { + byte[] output = null; + try + { + output = cipher.messageEncrypt(input); + } + catch (Exception e) + { + e.printStackTrace(); + } + return output; + } + + + public byte[] messageDecrypt(byte[] input) + throws IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException + { + byte[] output = null; + try + { + output = cipher.messageDecrypt(input); + } + catch (Exception e) + { + e.printStackTrace(); + } + return output; + } + + + //////////////////////////////////////////////////////////////////////////////////77 + + static public class McEliecePointcheval + extends McEliecePointchevalCipherSpi + { + public McEliecePointcheval() + { + super(new SHA1Digest(), new McEliecePointchevalCipher()); + } + } + + static public class McEliecePointcheval224 + extends McEliecePointchevalCipherSpi + { + public McEliecePointcheval224() + { + super(new SHA224Digest(), new McEliecePointchevalCipher()); + } + } + + static public class McEliecePointcheval256 + extends McEliecePointchevalCipherSpi + { + public McEliecePointcheval256() + { + super(new SHA256Digest(), new McEliecePointchevalCipher()); + } + } + + static public class McEliecePointcheval384 + extends McEliecePointchevalCipherSpi + { + public McEliecePointcheval384() + { + super(new SHA384Digest(), new McEliecePointchevalCipher()); + } + } + + static public class McEliecePointcheval512 + extends McEliecePointchevalCipherSpi + { + public McEliecePointcheval512() + { + super(new SHA512Digest(), new McEliecePointchevalCipher()); + } + } + + +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/rainbow/BCRainbowPrivateKey.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/rainbow/BCRainbowPrivateKey.java new file mode 100644 index 00000000..114d2009 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/rainbow/BCRainbowPrivateKey.java @@ -0,0 +1,243 @@ +package org.spongycastle.pqc.jcajce.provider.rainbow; + +import java.io.IOException; +import java.security.PrivateKey; +import java.util.Arrays; + +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.pqc.asn1.PQCObjectIdentifiers; +import org.spongycastle.pqc.asn1.RainbowPrivateKey; +import org.spongycastle.pqc.crypto.rainbow.Layer; +import org.spongycastle.pqc.crypto.rainbow.RainbowPrivateKeyParameters; +import org.spongycastle.pqc.crypto.rainbow.util.RainbowUtil; +import org.spongycastle.pqc.jcajce.spec.RainbowPrivateKeySpec; + +/** + * The Private key in Rainbow consists of the linear affine maps L1, L2 and the + * map F, consisting of quadratic polynomials. In this implementation, we + * denote: L1 = A1*x + b1 L2 = A2*x + b2 + * <p/> + * The coefficients of the polynomials in F are stored in 3-dimensional arrays + * per layer. The indices of these arrays denote the polynomial, and the + * variables. + * <p/> + * More detailed information about the private key is to be found in the paper + * of Jintai Ding, Dieter Schmidt: Rainbow, a New Multivariable Polynomial + * Signature Scheme. ACNS 2005: 164-175 (http://dx.doi.org/10.1007/11496137_12) + */ +public class BCRainbowPrivateKey + implements PrivateKey +{ + private static final long serialVersionUID = 1L; + + // the inverse of L1 + private short[][] A1inv; + + // translation vector element of L1 + private short[] b1; + + // the inverse of L2 + private short[][] A2inv; + + // translation vector of L2 + private short[] b2; + + /* + * components of F + */ + private Layer[] layers; + + // set of vinegar vars per layer. + private int[] vi; + + + /** + * Constructor. + * + * @param A1inv + * @param b1 + * @param A2inv + * @param b2 + * @param layers + */ + public BCRainbowPrivateKey(short[][] A1inv, short[] b1, short[][] A2inv, + short[] b2, int[] vi, Layer[] layers) + { + this.A1inv = A1inv; + this.b1 = b1; + this.A2inv = A2inv; + this.b2 = b2; + this.vi = vi; + this.layers = layers; + } + + /** + * Constructor (used by the {@link RainbowKeyFactorySpi}). + * + * @param keySpec a {@link RainbowPrivateKeySpec} + */ + public BCRainbowPrivateKey(RainbowPrivateKeySpec keySpec) + { + this(keySpec.getInvA1(), keySpec.getB1(), keySpec.getInvA2(), keySpec + .getB2(), keySpec.getVi(), keySpec.getLayers()); + } + + public BCRainbowPrivateKey( + RainbowPrivateKeyParameters params) + { + this(params.getInvA1(), params.getB1(), params.getInvA2(), params.getB2(), params.getVi(), params.getLayers()); + } + + /** + * Getter for the inverse matrix of A1. + * + * @return the A1inv inverse + */ + public short[][] getInvA1() + { + return this.A1inv; + } + + /** + * Getter for the translation part of the private quadratic map L1. + * + * @return b1 the translation part of L1 + */ + public short[] getB1() + { + return this.b1; + } + + /** + * Getter for the translation part of the private quadratic map L2. + * + * @return b2 the translation part of L2 + */ + public short[] getB2() + { + return this.b2; + } + + /** + * Getter for the inverse matrix of A2 + * + * @return the A2inv + */ + public short[][] getInvA2() + { + return this.A2inv; + } + + /** + * Returns the layers contained in the private key + * + * @return layers + */ + public Layer[] getLayers() + { + return this.layers; + } + + /** + * Returns the array of vi-s + * + * @return the vi + */ + public int[] getVi() + { + return vi; + } + + /** + * Compare this Rainbow private key with another object. + * + * @param other the other object + * @return the result of the comparison + */ + public boolean equals(Object other) + { + if (other == null || !(other instanceof BCRainbowPrivateKey)) + { + return false; + } + BCRainbowPrivateKey otherKey = (BCRainbowPrivateKey)other; + + boolean eq = true; + // compare using shortcut rule ( && instead of &) + eq = eq && RainbowUtil.equals(A1inv, otherKey.getInvA1()); + eq = eq && RainbowUtil.equals(A2inv, otherKey.getInvA2()); + eq = eq && RainbowUtil.equals(b1, otherKey.getB1()); + eq = eq && RainbowUtil.equals(b2, otherKey.getB2()); + eq = eq && Arrays.equals(vi, otherKey.getVi()); + if (layers.length != otherKey.getLayers().length) + { + return false; + } + for (int i = layers.length - 1; i >= 0; i--) + { + eq &= layers[i].equals(otherKey.getLayers()[i]); + } + return eq; + } + + public int hashCode() + { + int hash = layers.length; + + hash = hash * 37 + org.spongycastle.util.Arrays.hashCode(A1inv); + hash = hash * 37 + org.spongycastle.util.Arrays.hashCode(b1); + hash = hash * 37 + org.spongycastle.util.Arrays.hashCode(A2inv); + hash = hash * 37 + org.spongycastle.util.Arrays.hashCode(b2); + hash = hash * 37 + org.spongycastle.util.Arrays.hashCode(vi); + + for (int i = layers.length - 1; i >= 0; i--) + { + hash = hash * 37 + layers[i].hashCode(); + } + + + return hash; + } + + /** + * @return name of the algorithm - "Rainbow" + */ + public final String getAlgorithm() + { + return "Rainbow"; + } + + public byte[] getEncoded() + { + RainbowPrivateKey privateKey = new RainbowPrivateKey(A1inv, b1, A2inv, b2, vi, layers); + + PrivateKeyInfo pki; + try + { + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.rainbow, DERNull.INSTANCE); + pki = new PrivateKeyInfo(algorithmIdentifier, privateKey); + } + catch (IOException e) + { + e.printStackTrace(); + return null; + } + try + { + byte[] encoded = pki.getEncoded(); + return encoded; + } + catch (IOException e) + { + e.printStackTrace(); + return null; + } + } + + public String getFormat() + { + return "PKCS#8"; + } +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/rainbow/BCRainbowPublicKey.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/rainbow/BCRainbowPublicKey.java new file mode 100644 index 00000000..12f16ed9 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/rainbow/BCRainbowPublicKey.java @@ -0,0 +1,170 @@ +package org.spongycastle.pqc.jcajce.provider.rainbow; + +import java.security.PublicKey; + +import org.spongycastle.asn1.DERNull; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.pqc.asn1.PQCObjectIdentifiers; +import org.spongycastle.pqc.asn1.RainbowPublicKey; +import org.spongycastle.pqc.crypto.rainbow.RainbowParameters; +import org.spongycastle.pqc.crypto.rainbow.RainbowPublicKeyParameters; +import org.spongycastle.pqc.crypto.rainbow.util.RainbowUtil; +import org.spongycastle.pqc.jcajce.provider.util.KeyUtil; +import org.spongycastle.pqc.jcajce.spec.RainbowPublicKeySpec; +import org.spongycastle.util.Arrays; + +/** + * This class implements CipherParameters and PublicKey. + * <p/> + * The public key in Rainbow consists of n - v1 polynomial components of the + * private key's F and the field structure of the finite field k. + * <p/> + * The quadratic (or mixed) coefficients of the polynomials from the public key + * are stored in the 2-dimensional array in lexicographical order, requiring n * + * (n + 1) / 2 entries for each polynomial. The singular terms are stored in a + * 2-dimensional array requiring n entries per polynomial, the scalar term of + * each polynomial is stored in a 1-dimensional array. + * <p/> + * More detailed information on the public key is to be found in the paper of + * Jintai Ding, Dieter Schmidt: Rainbow, a New Multivariable Polynomial + * Signature Scheme. ACNS 2005: 164-175 (http://dx.doi.org/10.1007/11496137_12) + */ +public class BCRainbowPublicKey + implements PublicKey +{ + private static final long serialVersionUID = 1L; + + private short[][] coeffquadratic; + private short[][] coeffsingular; + private short[] coeffscalar; + private int docLength; // length of possible document to sign + + private RainbowParameters rainbowParams; + + /** + * Constructor + * + * @param docLength + * @param coeffQuadratic + * @param coeffSingular + * @param coeffScalar + */ + public BCRainbowPublicKey(int docLength, + short[][] coeffQuadratic, short[][] coeffSingular, + short[] coeffScalar) + { + this.docLength = docLength; + this.coeffquadratic = coeffQuadratic; + this.coeffsingular = coeffSingular; + this.coeffscalar = coeffScalar; + } + + /** + * Constructor (used by the {@link RainbowKeyFactorySpi}). + * + * @param keySpec a {@link RainbowPublicKeySpec} + */ + public BCRainbowPublicKey(RainbowPublicKeySpec keySpec) + { + this(keySpec.getDocLength(), keySpec.getCoeffQuadratic(), keySpec + .getCoeffSingular(), keySpec.getCoeffScalar()); + } + + public BCRainbowPublicKey( + RainbowPublicKeyParameters params) + { + this(params.getDocLength(), params.getCoeffQuadratic(), params.getCoeffSingular(), params.getCoeffScalar()); + } + + /** + * @return the docLength + */ + public int getDocLength() + { + return this.docLength; + } + + /** + * @return the coeffQuadratic + */ + public short[][] getCoeffQuadratic() + { + return coeffquadratic; + } + + /** + * @return the coeffSingular + */ + public short[][] getCoeffSingular() + { + short[][] copy = new short[coeffsingular.length][]; + + for (int i = 0; i != coeffsingular.length; i++) + { + copy[i] = Arrays.clone(coeffsingular[i]); + } + + return copy; + } + + + /** + * @return the coeffScalar + */ + public short[] getCoeffScalar() + { + return Arrays.clone(coeffscalar); + } + + /** + * Compare this Rainbow public key with another object. + * + * @param other the other object + * @return the result of the comparison + */ + public boolean equals(Object other) + { + if (other == null || !(other instanceof BCRainbowPublicKey)) + { + return false; + } + BCRainbowPublicKey otherKey = (BCRainbowPublicKey)other; + + return docLength == otherKey.getDocLength() + && RainbowUtil.equals(coeffquadratic, otherKey.getCoeffQuadratic()) + && RainbowUtil.equals(coeffsingular, otherKey.getCoeffSingular()) + && RainbowUtil.equals(coeffscalar, otherKey.getCoeffScalar()); + } + + public int hashCode() + { + int hash = docLength; + + hash = hash * 37 + Arrays.hashCode(coeffquadratic); + hash = hash * 37 + Arrays.hashCode(coeffsingular); + hash = hash * 37 + Arrays.hashCode(coeffscalar); + + return hash; + } + + /** + * @return name of the algorithm - "Rainbow" + */ + public final String getAlgorithm() + { + return "Rainbow"; + } + + public String getFormat() + { + return "X.509"; + } + + public byte[] getEncoded() + { + RainbowPublicKey key = new RainbowPublicKey(docLength, coeffquadratic, coeffsingular, coeffscalar); + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.rainbow, DERNull.INSTANCE); + + return KeyUtil.getEncodedSubjectPublicKeyInfo(algorithmIdentifier, key); + } +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/rainbow/RainbowKeyFactorySpi.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/rainbow/RainbowKeyFactorySpi.java new file mode 100644 index 00000000..614e0796 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/rainbow/RainbowKeyFactorySpi.java @@ -0,0 +1,236 @@ +package org.spongycastle.pqc.jcajce.provider.rainbow; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactorySpi; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import org.spongycastle.asn1.ASN1Primitive; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; +import org.spongycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; +import org.spongycastle.pqc.asn1.RainbowPrivateKey; +import org.spongycastle.pqc.asn1.RainbowPublicKey; +import org.spongycastle.pqc.jcajce.spec.RainbowPrivateKeySpec; +import org.spongycastle.pqc.jcajce.spec.RainbowPublicKeySpec; + + +/** + * This class transforms Rainbow keys and Rainbow key specifications. + * + * @see BCRainbowPublicKey + * @see RainbowPublicKeySpec + * @see BCRainbowPrivateKey + * @see RainbowPrivateKeySpec + */ +public class RainbowKeyFactorySpi + extends KeyFactorySpi + implements AsymmetricKeyInfoConverter +{ + /** + * Converts, if possible, a key specification into a + * {@link BCRainbowPrivateKey}. Currently, the following key specifications + * are supported: {@link RainbowPrivateKeySpec}, {@link PKCS8EncodedKeySpec}. + * <p/> + * <p/> + * <p/> + * The ASN.1 definition of the key structure is + * <p/> + * <pre> + * RainbowPrivateKey ::= SEQUENCE { + * oid OBJECT IDENTIFIER -- OID identifying the algorithm + * A1inv SEQUENCE OF OCTET STRING -- inversed matrix of L1 + * b1 OCTET STRING -- translation vector of L1 + * A2inv SEQUENCE OF OCTET STRING -- inversed matrix of L2 + * b2 OCTET STRING -- translation vector of L2 + * vi OCTET STRING -- num of elmts in each Set S + * layers SEQUENCE OF Layer -- layers of F + * } + * + * Layer ::= SEQUENCE OF Poly + * Poly ::= SEQUENCE { + * alpha SEQUENCE OF OCTET STRING + * beta SEQUENCE OF OCTET STRING + * gamma OCTET STRING + * eta OCTET + * } + * </pre> + * <p/> + * <p/> + * + * @param keySpec the key specification + * @return the Rainbow private key + * @throws InvalidKeySpecException if the KeySpec is not supported. + */ + public PrivateKey engineGeneratePrivate(KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof RainbowPrivateKeySpec) + { + return new BCRainbowPrivateKey((RainbowPrivateKeySpec)keySpec); + } + else if (keySpec instanceof PKCS8EncodedKeySpec) + { + // get the DER-encoded Key according to PKCS#8 from the spec + byte[] encKey = ((PKCS8EncodedKeySpec)keySpec).getEncoded(); + + try + { + return generatePrivate(PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(encKey))); + } + catch (Exception e) + { + throw new InvalidKeySpecException(e.toString()); + } + } + + throw new InvalidKeySpecException("Unsupported key specification: " + + keySpec.getClass() + "."); + } + + /** + * Converts, if possible, a key specification into a + * {@link BCRainbowPublicKey}. Currently, the following key specifications are + * supported:{@link X509EncodedKeySpec}. + * <p/> + * <p/> + * <p/> + * The ASN.1 definition of a public key's structure is + * <p/> + * <pre> + * RainbowPublicKey ::= SEQUENCE { + * oid OBJECT IDENTIFIER -- OID identifying the algorithm + * docLength Integer -- length of signable msg + * coeffquadratic SEQUENCE OF OCTET STRING -- quadratic (mixed) coefficients + * coeffsingular SEQUENCE OF OCTET STRING -- singular coefficients + * coeffscalar OCTET STRING -- scalar coefficients + * } + * </pre> + * <p/> + * <p/> + * + * @param keySpec the key specification + * @return the Rainbow public key + * @throws InvalidKeySpecException if the KeySpec is not supported. + */ + public PublicKey engineGeneratePublic(KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof RainbowPublicKeySpec) + { + return new BCRainbowPublicKey((RainbowPublicKeySpec)keySpec); + } + else if (keySpec instanceof X509EncodedKeySpec) + { + // get the DER-encoded Key according to X.509 from the spec + byte[] encKey = ((X509EncodedKeySpec)keySpec).getEncoded(); + + // decode the SubjectPublicKeyInfo data structure to the pki object + try + { + return generatePublic(SubjectPublicKeyInfo.getInstance(encKey)); + } + catch (Exception e) + { + throw new InvalidKeySpecException(e.toString()); + } + } + + throw new InvalidKeySpecException("Unknown key specification: " + keySpec + "."); + } + + /** + * Converts a given key into a key specification, if possible. Currently the + * following specs are supported: + * <ul> + * <li>for RainbowPublicKey: X509EncodedKeySpec, RainbowPublicKeySpec + * <li>for RainbowPrivateKey: PKCS8EncodedKeySpec, RainbowPrivateKeySpec + * </ul> + * + * @param key the key + * @param keySpec the key specification + * @return the specification of the CMSS key + * @throws InvalidKeySpecException if the key type or key specification is not supported. + */ + public final KeySpec engineGetKeySpec(Key key, Class keySpec) + throws InvalidKeySpecException + { + if (key instanceof BCRainbowPrivateKey) + { + if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) + { + return new PKCS8EncodedKeySpec(key.getEncoded()); + } + else if (RainbowPrivateKeySpec.class.isAssignableFrom(keySpec)) + { + BCRainbowPrivateKey privKey = (BCRainbowPrivateKey)key; + return new RainbowPrivateKeySpec(privKey.getInvA1(), privKey + .getB1(), privKey.getInvA2(), privKey.getB2(), privKey + .getVi(), privKey.getLayers()); + } + } + else if (key instanceof BCRainbowPublicKey) + { + if (X509EncodedKeySpec.class.isAssignableFrom(keySpec)) + { + return new X509EncodedKeySpec(key.getEncoded()); + } + else if (RainbowPublicKeySpec.class.isAssignableFrom(keySpec)) + { + BCRainbowPublicKey pubKey = (BCRainbowPublicKey)key; + return new RainbowPublicKeySpec(pubKey.getDocLength(), pubKey + .getCoeffQuadratic(), pubKey.getCoeffSingular(), pubKey + .getCoeffScalar()); + } + } + else + { + throw new InvalidKeySpecException("Unsupported key type: " + + key.getClass() + "."); + } + + throw new InvalidKeySpecException("Unknown key specification: " + + keySpec + "."); + } + + /** + * Translates a key into a form known by the FlexiProvider. Currently the + * following key types are supported: RainbowPrivateKey, RainbowPublicKey. + * + * @param key the key + * @return a key of a known key type + * @throws InvalidKeyException if the key is not supported. + */ + public final Key engineTranslateKey(Key key) + throws InvalidKeyException + { + if (key instanceof BCRainbowPrivateKey || key instanceof BCRainbowPublicKey) + { + return key; + } + + throw new InvalidKeyException("Unsupported key type"); + } + + public PrivateKey generatePrivate(PrivateKeyInfo keyInfo) + throws IOException + { + RainbowPrivateKey pKey = RainbowPrivateKey.getInstance(keyInfo.parsePrivateKey()); + + return new BCRainbowPrivateKey(pKey.getInvA1(), pKey.getB1(), pKey.getInvA2(), pKey.getB2(), pKey.getVi(), pKey.getLayers()); + } + + public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo) + throws IOException + { + RainbowPublicKey pKey = RainbowPublicKey.getInstance(keyInfo.parsePublicKey()); + + return new BCRainbowPublicKey(pKey.getDocLength(), pKey.getCoeffQuadratic(), pKey.getCoeffSingular(), pKey.getCoeffScalar()); + } +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/rainbow/RainbowKeyPairGeneratorSpi.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/rainbow/RainbowKeyPairGeneratorSpi.java new file mode 100644 index 00000000..82239575 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/rainbow/RainbowKeyPairGeneratorSpi.java @@ -0,0 +1,72 @@ +package org.spongycastle.pqc.jcajce.provider.rainbow; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.pqc.crypto.rainbow.RainbowKeyGenerationParameters; +import org.spongycastle.pqc.crypto.rainbow.RainbowKeyPairGenerator; +import org.spongycastle.pqc.crypto.rainbow.RainbowParameters; +import org.spongycastle.pqc.crypto.rainbow.RainbowPrivateKeyParameters; +import org.spongycastle.pqc.crypto.rainbow.RainbowPublicKeyParameters; +import org.spongycastle.pqc.jcajce.spec.RainbowParameterSpec; + +public class RainbowKeyPairGeneratorSpi + extends java.security.KeyPairGenerator +{ + RainbowKeyGenerationParameters param; + RainbowKeyPairGenerator engine = new RainbowKeyPairGenerator(); + int strength = 1024; + SecureRandom random = new SecureRandom(); + boolean initialised = false; + + public RainbowKeyPairGeneratorSpi() + { + super("Rainbow"); + } + + public void initialize( + int strength, + SecureRandom random) + { + this.strength = strength; + this.random = random; + } + + public void initialize( + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidAlgorithmParameterException + { + if (!(params instanceof RainbowParameterSpec)) + { + throw new InvalidAlgorithmParameterException("parameter object not a RainbowParameterSpec"); + } + RainbowParameterSpec rainbowParams = (RainbowParameterSpec)params; + + param = new RainbowKeyGenerationParameters(random, new RainbowParameters(rainbowParams.getVi())); + + engine.init(param); + initialised = true; + } + + public KeyPair generateKeyPair() + { + if (!initialised) + { + param = new RainbowKeyGenerationParameters(random, new RainbowParameters(new RainbowParameterSpec().getVi())); + + engine.init(param); + initialised = true; + } + + AsymmetricCipherKeyPair pair = engine.generateKeyPair(); + RainbowPublicKeyParameters pub = (RainbowPublicKeyParameters)pair.getPublic(); + RainbowPrivateKeyParameters priv = (RainbowPrivateKeyParameters)pair.getPrivate(); + + return new KeyPair(new BCRainbowPublicKey(pub), + new BCRainbowPrivateKey(priv)); + } +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/rainbow/RainbowKeysToParams.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/rainbow/RainbowKeysToParams.java new file mode 100644 index 00000000..0ae006ef --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/rainbow/RainbowKeysToParams.java @@ -0,0 +1,49 @@ +package org.spongycastle.pqc.jcajce.provider.rainbow; + +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; + +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.pqc.crypto.rainbow.RainbowPrivateKeyParameters; +import org.spongycastle.pqc.crypto.rainbow.RainbowPublicKeyParameters; + + +/** + * utility class for converting jce/jca Rainbow objects + * objects into their org.spongycastle.crypto counterparts. + */ + +public class RainbowKeysToParams +{ + static public AsymmetricKeyParameter generatePublicKeyParameter( + PublicKey key) + throws InvalidKeyException + { + if (key instanceof BCRainbowPublicKey) + { + BCRainbowPublicKey k = (BCRainbowPublicKey)key; + + return new RainbowPublicKeyParameters(k.getDocLength(), k.getCoeffQuadratic(), + k.getCoeffSingular(), k.getCoeffScalar()); + } + + throw new InvalidKeyException("can't identify Rainbow public key: " + key.getClass().getName()); + } + + static public AsymmetricKeyParameter generatePrivateKeyParameter( + PrivateKey key) + throws InvalidKeyException + { + if (key instanceof BCRainbowPrivateKey) + { + BCRainbowPrivateKey k = (BCRainbowPrivateKey)key; + return new RainbowPrivateKeyParameters(k.getInvA1(), k.getB1(), + k.getInvA2(), k.getB2(), k.getVi(), k.getLayers()); + } + + throw new InvalidKeyException("can't identify Rainbow private key."); + } +} + + diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/rainbow/SignatureSpi.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/rainbow/SignatureSpi.java new file mode 100644 index 00000000..01b12699 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/rainbow/SignatureSpi.java @@ -0,0 +1,164 @@ +package org.spongycastle.pqc.jcajce.provider.rainbow; + +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.SignatureException; +import java.security.spec.AlgorithmParameterSpec; + +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.digests.SHA224Digest; +import org.spongycastle.crypto.digests.SHA256Digest; +import org.spongycastle.crypto.digests.SHA384Digest; +import org.spongycastle.crypto.digests.SHA512Digest; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.pqc.crypto.rainbow.RainbowSigner; + +/** + * Rainbow Signature class, extending the jce SignatureSpi. + */ +public class SignatureSpi + extends java.security.SignatureSpi +{ + private Digest digest; + private RainbowSigner signer; + private SecureRandom random; + + protected SignatureSpi(Digest digest, RainbowSigner signer) + { + this.digest = digest; + this.signer = signer; + } + + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException + { + CipherParameters param; + param = RainbowKeysToParams.generatePublicKeyParameter(publicKey); + + digest.reset(); + signer.init(false, param); + } + + protected void engineInitSign(PrivateKey privateKey, SecureRandom random) + throws InvalidKeyException + { + this.random = random; + engineInitSign(privateKey); + } + + protected void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException + { + CipherParameters param; + param = RainbowKeysToParams.generatePrivateKeyParameter(privateKey); + + if (random != null) + { + param = new ParametersWithRandom(param, random); + } + + digest.reset(); + signer.init(true, param); + + } + + protected void engineUpdate(byte b) + throws SignatureException + { + digest.update(b); + } + + protected void engineUpdate(byte[] b, int off, int len) + throws SignatureException + { + digest.update(b, off, len); + } + + protected byte[] engineSign() + throws SignatureException + { + byte[] hash = new byte[digest.getDigestSize()]; + digest.doFinal(hash, 0); + try + { + byte[] sig = signer.generateSignature(hash); + + return sig; + } + catch (Exception e) + { + throw new SignatureException(e.toString()); + } + } + + protected boolean engineVerify(byte[] sigBytes) + throws SignatureException + { + byte[] hash = new byte[digest.getDigestSize()]; + digest.doFinal(hash, 0); + return signer.verifySignature(hash, sigBytes); + } + + protected void engineSetParameter(AlgorithmParameterSpec params) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated replaced with <a href = + * "#engineSetParameter(java.security.spec.AlgorithmParameterSpec)" + * > + */ + protected void engineSetParameter(String param, Object value) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated + */ + protected Object engineGetParameter(String param) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + + static public class withSha224 + extends SignatureSpi + { + public withSha224() + { + super(new SHA224Digest(), new RainbowSigner()); + } + } + + static public class withSha256 + extends SignatureSpi + { + public withSha256() + { + super(new SHA256Digest(), new RainbowSigner()); + } + } + + static public class withSha384 + extends SignatureSpi + { + public withSha384() + { + super(new SHA384Digest(), new RainbowSigner()); + } + } + + static public class withSha512 + extends SignatureSpi + { + public withSha512() + { + super(new SHA512Digest(), new RainbowSigner()); + } + } +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/util/AsymmetricBlockCipher.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/util/AsymmetricBlockCipher.java new file mode 100644 index 00000000..2e5678ad --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/util/AsymmetricBlockCipher.java @@ -0,0 +1,522 @@ +package org.spongycastle.pqc.jcajce.provider.util; + +import java.io.ByteArrayOutputStream; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.Key; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.ShortBufferException; + + +/** + * The AsymmetricBlockCipher class extends CipherSpiExt. + * NOTE: Some Ciphers are using Padding. OneAndZeroesPadding is used as default + * padding. However padding can still be specified, but mode is not supported; + * if you try to instantiate the cipher with something else than "NONE" as mode + * NoSuchAlgorithmException is thrown. + */ +public abstract class AsymmetricBlockCipher + extends CipherSpiExt +{ + + /** + * ParameterSpec used with this cipher + */ + protected AlgorithmParameterSpec paramSpec; + + /** + * Internal buffer + */ + protected ByteArrayOutputStream buf; + + /** + * The maximum number of bytes the cipher can decrypt. + */ + protected int maxPlainTextSize; + + /** + * The maximum number of bytes the cipher can encrypt. + */ + protected int cipherTextSize; + + /** + * The AsymmetricBlockCipher() constructor + */ + public AsymmetricBlockCipher() + { + buf = new ByteArrayOutputStream(); + } + + /** + * Return the block size (in bytes). Note: although the ciphers extending + * this class are not block ciphers, the method was adopted to return the + * maximal plaintext and ciphertext sizes for non hybrid ciphers. If the + * cipher is hybrid, it returns 0. + * + * @return if the cipher is not a hybrid one the max plain/cipher text size + * is returned, otherwise 0 is returned + */ + public final int getBlockSize() + { + return opMode == ENCRYPT_MODE ? maxPlainTextSize : cipherTextSize; + } + + /** + * @return <tt>null</tt> since no initialization vector is used. + */ + public final byte[] getIV() + { + return null; + } + + /** + * Return the length in bytes that an output buffer would need to be in + * order to hold the result of the next update or doFinal operation, given + * the input length <tt>inLen</tt> (in bytes). This call takes into + * account any unprocessed (buffered) data from a previous update call, and + * padding. The actual output length of the next update() or doFinal() call + * may be smaller than the length returned by this method. + * <p/> + * If the input length plus the length of the buffered data exceeds the + * maximum length, <tt>0</tt> is returned. + * + * @param inLen the length of the input + * @return the length of the ciphertext or <tt>0</tt> if the input is too + * long. + */ + public final int getOutputSize(int inLen) + { + + int totalLen = inLen + buf.size(); + + int maxLen = getBlockSize(); + + if (totalLen > maxLen) + { + // the length of the input exceeds the maximal supported length + return 0; + } + + return maxLen; + } + + /** + * <p/> + * Returns the parameters used with this cipher. + * <p/> + * The returned parameters may be the same that were used to initialize this + * cipher, or may contain the default set of parameters or a set of randomly + * generated parameters used by the underlying cipher implementation + * (provided that the underlying cipher implementation uses a default set of + * parameters or creates new parameters if it needs parameters but was not + * initialized with any). + * <p/> + * + * @return the parameters used with this cipher, or null if this cipher does + * not use any parameters. + */ + public final AlgorithmParameterSpec getParameters() + { + return paramSpec; + } + + /** + * Initializes the cipher for encryption by forwarding it to + * initEncrypt(Key, FlexiSecureRandom). + * <p/> + * <p/> + * If this cipher requires any algorithm parameters that cannot be derived + * from the given key, the underlying cipher implementation is supposed to + * generate the required parameters itself (using provider-specific default + * or random values) if it is being initialized for encryption, and raise an + * InvalidKeyException if it is being initialized for decryption. The + * generated parameters can be retrieved using engineGetParameters or + * engineGetIV (if the parameter is an IV). + * + * @param key the encryption or decryption key. + * @throws InvalidKeyException if the given key is inappropriate for initializing this + * cipher. + */ + public final void initEncrypt(Key key) + throws InvalidKeyException + { + try + { + initEncrypt(key, null, new SecureRandom()); + } + catch (InvalidAlgorithmParameterException e) + { + throw new InvalidParameterException( + "This cipher needs algorithm parameters for initialization (cannot be null)."); + } + } + + /** + * Initialize this cipher for encryption by forwarding it to + * initEncrypt(Key, FlexiSecureRandom, AlgorithmParameterSpec). + * <p/> + * If this cipher requires any algorithm parameters that cannot be derived + * from the given key, the underlying cipher implementation is supposed to + * generate the required parameters itself (using provider-specific default + * or random values) if it is being initialized for encryption, and raise an + * InvalidKeyException if it is being initialized for decryption. The + * generated parameters can be retrieved using engineGetParameters or + * engineGetIV (if the parameter is an IV). + * + * @param key the encryption or decryption key. + * @param random the source of randomness. + * @throws InvalidKeyException if the given key is inappropriate for initializing this + * cipher. + */ + public final void initEncrypt(Key key, SecureRandom random) + throws InvalidKeyException + { + + try + { + initEncrypt(key, null, random); + } + catch (InvalidAlgorithmParameterException iape) + { + throw new InvalidParameterException( + "This cipher needs algorithm parameters for initialization (cannot be null)."); + } + } + + /** + * Initializes the cipher for encryption by forwarding it to + * initEncrypt(Key, FlexiSecureRandom, AlgorithmParameterSpec). + * + * @param key the encryption or decryption key. + * @param params the algorithm parameters. + * @throws InvalidKeyException if the given key is inappropriate for initializing this + * cipher. + * @throws InvalidAlgorithmParameterException if the given algortihm parameters are inappropriate for + * this cipher, or if this cipher is being initialized for + * decryption and requires algorithm parameters and params + * is null. + */ + public final void initEncrypt(Key key, AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + initEncrypt(key, params, new SecureRandom()); + } + + /** + * This method initializes the AsymmetricBlockCipher with a certain key for + * data encryption. + * <p/> + * If this cipher (including its underlying feedback or padding scheme) + * requires any random bytes (e.g., for parameter generation), it will get + * them from random. + * <p/> + * Note that when a Cipher object is initialized, it loses all + * previously-acquired state. In other words, initializing a Cipher is + * equivalent to creating a new instance of that Cipher and initializing it + * <p/> + * + * @param key the key which has to be used to encrypt data. + * @param secureRandom the source of randomness. + * @param params the algorithm parameters. + * @throws InvalidKeyException if the given key is inappropriate for initializing this + * cipher + * @throws InvalidAlgorithmParameterException if the given algorithm parameters are inappropriate for + * this cipher, or if this cipher is being initialized for + * decryption and requires algorithm parameters and params + * is null. + */ + public final void initEncrypt(Key key, AlgorithmParameterSpec params, + SecureRandom secureRandom) + throws InvalidKeyException, + InvalidAlgorithmParameterException + { + opMode = ENCRYPT_MODE; + initCipherEncrypt(key, params, secureRandom); + } + + /** + * Initialize the cipher for decryption by forwarding it to + * {@link #initDecrypt(Key, AlgorithmParameterSpec)}. + * <p/> + * If this cipher requires any algorithm parameters that cannot be derived + * from the given key, the underlying cipher implementation is supposed to + * generate the required parameters itself (using provider-specific default + * or random values) if it is being initialized for encryption, and raise an + * InvalidKeyException if it is being initialized for decryption. The + * generated parameters can be retrieved using engineGetParameters or + * engineGetIV (if the parameter is an IV). + * + * @param key the encryption or decryption key. + * @throws InvalidKeyException if the given key is inappropriate for initializing this + * cipher. + */ + public final void initDecrypt(Key key) + throws InvalidKeyException + { + try + { + initDecrypt(key, null); + } + catch (InvalidAlgorithmParameterException iape) + { + throw new InvalidParameterException( + "This cipher needs algorithm parameters for initialization (cannot be null)."); + } + } + + /** + * This method initializes the AsymmetricBlockCipher with a certain key for + * data decryption. + * <p/> + * If this cipher (including its underlying feedback or padding scheme) + * requires any random bytes (e.g., for parameter generation), it will get + * them from random. + * <p/> + * Note that when a Cipher object is initialized, it loses all + * previously-acquired state. In other words, initializing a Cipher is + * equivalent to creating a new instance of that Cipher and initializing it + * <p/> + * + * @param key the key which has to be used to decrypt data. + * @param params the algorithm parameters. + * @throws InvalidKeyException if the given key is inappropriate for initializing this + * cipher + * @throws InvalidAlgorithmParameterException if the given algorithm parameters are inappropriate for + * this cipher, or if this cipher is being initialized for + * decryption and requires algorithm parameters and params + * is null. + */ + public final void initDecrypt(Key key, AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + opMode = DECRYPT_MODE; + initCipherDecrypt(key, params); + } + + /** + * Continue a multiple-part encryption or decryption operation. This method + * just writes the input into an internal buffer. + * + * @param input byte array containing the next part of the input + * @param inOff index in the array where the input starts + * @param inLen length of the input + * @return a new buffer with the result (always empty) + */ + public final byte[] update(byte[] input, int inOff, int inLen) + { + if (inLen != 0) + { + buf.write(input, inOff, inLen); + } + return new byte[0]; + } + + /** + * Continue a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized), processing another data part. + * + * @param input the input buffer + * @param inOff the offset where the input starts + * @param inLen the input length + * @param output the output buffer + * @param outOff the offset where the result is stored + * @return the length of the output (always 0) + */ + public final int update(byte[] input, int inOff, int inLen, byte[] output, + int outOff) + { + update(input, inOff, inLen); + return 0; + } + + /** + * Finish a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized). + * + * @param input the input buffer + * @param inOff the offset where the input starts + * @param inLen the input length + * @return a new buffer with the result + * @throws IllegalBlockSizeException if the plaintext or ciphertext size is too large. + * @throws BadPaddingException if the ciphertext is invalid. + */ + public final byte[] doFinal(byte[] input, int inOff, int inLen) + throws IllegalBlockSizeException, BadPaddingException + { + + checkLength(inLen); + update(input, inOff, inLen); + byte[] mBytes = buf.toByteArray(); + buf.reset(); + + switch (opMode) + { + case ENCRYPT_MODE: + return messageEncrypt(mBytes); + + case DECRYPT_MODE: + return messageDecrypt(mBytes); + + default: + return null; + + } + } + + /** + * Finish a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized). + * + * @param input the input buffer + * @param inOff the offset where the input starts + * @param inLen the input length + * @param output the buffer for the result + * @param outOff the offset where the result is stored + * @return the output length + * @throws ShortBufferException if the output buffer is too small to hold the result. + * @throws IllegalBlockSizeException if the plaintext or ciphertext size is too large. + * @throws BadPaddingException if the ciphertext is invalid. + */ + public final int doFinal(byte[] input, int inOff, int inLen, byte[] output, + int outOff) + throws ShortBufferException, IllegalBlockSizeException, + BadPaddingException + { + + if (output.length < getOutputSize(inLen)) + { + throw new ShortBufferException("Output buffer too short."); + } + + byte[] out = doFinal(input, inOff, inLen); + System.arraycopy(out, 0, output, outOff, out.length); + return out.length; + } + + /** + * Since asymmetric block ciphers do not support modes, this method does + * nothing. + * + * @param modeName the cipher mode (unused) + */ + protected final void setMode(String modeName) + { + // empty + } + + /** + * Since asymmetric block ciphers do not support padding, this method does + * nothing. + * + * @param paddingName the name of the padding scheme (not used) + */ + protected final void setPadding(String paddingName) + { + // empty + } + + /** + * Check if the message length plus the length of the input length can be + * en/decrypted. This method uses the specific values + * {@link #maxPlainTextSize} and {@link #cipherTextSize} which are set by + * the implementations. If the input length plus the length of the internal + * buffer is greater than {@link #maxPlainTextSize} for encryption or not + * equal to {@link #cipherTextSize} for decryption, an + * {@link IllegalBlockSizeException} will be thrown. + * + * @param inLen length of the input to check + * @throws IllegalBlockSizeException if the input length is invalid. + */ + protected void checkLength(int inLen) + throws IllegalBlockSizeException + { + + int inLength = inLen + buf.size(); + + if (opMode == ENCRYPT_MODE) + { + if (inLength > maxPlainTextSize) + { + throw new IllegalBlockSizeException( + "The length of the plaintext (" + inLength + + " bytes) is not supported by " + + "the cipher (max. " + maxPlainTextSize + + " bytes)."); + } + } + else if (opMode == DECRYPT_MODE) + { + if (inLength != cipherTextSize) + { + throw new IllegalBlockSizeException( + "Illegal ciphertext length (expected " + cipherTextSize + + " bytes, was " + inLength + " bytes)."); + } + } + + } + + /** + * Initialize the AsymmetricBlockCipher with a certain key for data + * encryption. + * + * @param key the key which has to be used to encrypt data + * @param params the algorithm parameters + * @param sr the source of randomness + * @throws InvalidKeyException if the given key is inappropriate for initializing this + * cipher. + * @throws InvalidAlgorithmParameterException if the given parameters are inappropriate for + * initializing this cipher. + */ + protected abstract void initCipherEncrypt(Key key, + AlgorithmParameterSpec params, SecureRandom sr) + throws InvalidKeyException, InvalidAlgorithmParameterException; + + /** + * Initialize the AsymmetricBlockCipher with a certain key for data + * encryption. + * + * @param key the key which has to be used to decrypt data + * @param params the algorithm parameters + * @throws InvalidKeyException if the given key is inappropriate for initializing this + * cipher + * @throws InvalidAlgorithmParameterException if the given parameters are inappropriate for + * initializing this cipher. + */ + protected abstract void initCipherDecrypt(Key key, + AlgorithmParameterSpec params) + throws InvalidKeyException, + InvalidAlgorithmParameterException; + + /** + * Encrypt the message stored in input. The method should also perform an + * additional length check. + * + * @param input the message to be encrypted (usually the message length is + * less than or equal to maxPlainTextSize) + * @return the encrypted message (it has length equal to maxCipherTextSize_) + * @throws IllegalBlockSizeException if the input is inappropriate for this cipher. + * @throws BadPaddingException if the input format is invalid. + */ + protected abstract byte[] messageEncrypt(byte[] input) + throws IllegalBlockSizeException, BadPaddingException; + + /** + * Decrypt the ciphertext stored in input. The method should also perform an + * additional length check. + * + * @param input the ciphertext to be decrypted (the ciphertext length is + * less than or equal to maxCipherTextSize) + * @return the decrypted message + * @throws IllegalBlockSizeException if the input is inappropriate for this cipher. + * @throws BadPaddingException if the input format is invalid. + */ + protected abstract byte[] messageDecrypt(byte[] input) + throws IllegalBlockSizeException, BadPaddingException; + +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/util/AsymmetricHybridCipher.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/util/AsymmetricHybridCipher.java new file mode 100644 index 00000000..57d367b2 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/util/AsymmetricHybridCipher.java @@ -0,0 +1,397 @@ +package org.spongycastle.pqc.jcajce.provider.util; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.Key; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.BadPaddingException; +import javax.crypto.ShortBufferException; + +/** + * The AsymmetricHybridCipher class extends CipherSpiExt. + * NOTE: Some Ciphers are using Padding. OneAndZeroesPadding is used as default + * padding. However padding can still be specified, but mode is not supported; + * if you try to instantiate the cipher with something else than "NONE" as mode, + * NoSuchAlgorithmException is thrown. + */ +public abstract class AsymmetricHybridCipher + extends CipherSpiExt +{ + + /** + * ParameterSpec used with this cipher + */ + protected AlgorithmParameterSpec paramSpec; + + /** + * Since asymmetric hybrid ciphers do not support modes, this method does + * nothing. + * + * @param modeName the cipher mode (unused) + */ + protected final void setMode(String modeName) + { + // empty + } + + /** + * Since asymmetric hybrid ciphers do not support padding, this method does + * nothing. + * + * @param paddingName the name of the padding scheme (not used) + */ + protected final void setPadding(String paddingName) + { + // empty + } + + /** + * @return <tt>null</tt> since no initialization vector is used. + */ + public final byte[] getIV() + { + return null; + } + + /** + * @return 0 since the implementing algorithms are not block ciphers + */ + public final int getBlockSize() + { + return 0; + } + + /** + * Return the parameters used with this cipher. + * <p/> + * The returned parameters may be the same that were used to initialize this + * cipher, or may contain the default set of parameters or a set of randomly + * generated parameters used by the underlying cipher implementation + * (provided that the underlying cipher implementation uses a default set of + * parameters or creates new parameters if it needs parameters but was not + * initialized with any). + * + * @return the parameters used with this cipher, or <tt>null</tt> if this + * cipher does not use any parameters. + */ + public final AlgorithmParameterSpec getParameters() + { + return paramSpec; + } + + /** + * Return the length in bytes that an output buffer would need to be in + * order to hold the result of the next update or doFinal operation, given + * the input length <tt>inLen</tt> (in bytes). This call takes into + * account any unprocessed (buffered) data from a previous update call, and + * padding. The actual output length of the next update() or doFinal() call + * may be smaller than the length returned by this method. + * + * @param inLen the length of the input + * @return the length of the output of the next <tt>update()</tt> or + * <tt>doFinal()</tt> call + */ + public final int getOutputSize(int inLen) + { + return opMode == ENCRYPT_MODE ? encryptOutputSize(inLen) + : decryptOutputSize(inLen); + } + + /** + * Initialize the cipher for encryption by forwarding it to + * {@link #initEncrypt(Key, AlgorithmParameterSpec, SecureRandom)}. + * <p/> + * If this cipher requires any algorithm parameters that cannot be derived + * from the given key, the underlying cipher implementation is supposed to + * generate the required parameters itself (using provider-specific default + * or random values) if it is being initialized for encryption, and raise an + * InvalidKeyException if it is being initialized for decryption. The + * generated parameters can be retrieved using {@link #getParameters()}. + * + * @param key the encryption key + * @throws InvalidKeyException if the given key is inappropriate for initializing this + * cipher. + * @throws InvalidParameterException if this cipher needs algorithm parameters for + * initialization and cannot generate parameters itself. + */ + public final void initEncrypt(Key key) + throws InvalidKeyException + { + try + { + initEncrypt(key, null, new SecureRandom()); + } + catch (InvalidAlgorithmParameterException e) + { + throw new InvalidParameterException( + "This cipher needs algorithm parameters for initialization (cannot be null)."); + } + } + + /** + * Initialize this cipher for encryption by forwarding it to + * {@link #initEncrypt(Key, AlgorithmParameterSpec, SecureRandom)}. + * <p/> + * If this cipher requires any algorithm parameters that cannot be derived + * from the given key, the underlying cipher implementation is supposed to + * generate the required parameters itself (using provider-specific default + * or random values) if it is being initialized for encryption, and raise an + * InvalidKeyException if it is being initialized for decryption. The + * generated parameters can be retrieved using {@link #getParameters()}. + * + * @param key the encryption key + * @param random the source of randomness + * @throws InvalidKeyException if the given key is inappropriate for initializing this + * cipher. + * @throws InvalidParameterException if this cipher needs algorithm parameters for + * initialization and cannot generate parameters itself. + */ + public final void initEncrypt(Key key, SecureRandom random) + throws InvalidKeyException + { + try + { + initEncrypt(key, null, random); + } + catch (InvalidAlgorithmParameterException iape) + { + throw new InvalidParameterException( + "This cipher needs algorithm parameters for initialization (cannot be null)."); + } + } + + /** + * Initialize the cipher for encryption by forwarding it to initEncrypt(Key, + * FlexiSecureRandom, AlgorithmParameterSpec). + * + * @param key the encryption key + * @param params the algorithm parameters + * @throws InvalidKeyException if the given key is inappropriate for initializing this + * cipher. + * @throws InvalidAlgorithmParameterException if the given algorithm parameters are inappropriate for + * this cipher, or if this cipher is initialized with + * <tt>null</tt> parameters and cannot generate parameters + * itself. + */ + public final void initEncrypt(Key key, AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + initEncrypt(key, params, new SecureRandom()); + } + + /** + * Initialize the cipher with a certain key for data encryption. + * <p/> + * If this cipher requires any random bytes (e.g., for parameter + * generation), it will get them from <tt>random</tt>. + * <p/> + * Note that when a Cipher object is initialized, it loses all + * previously-acquired state. In other words, initializing a Cipher is + * equivalent to creating a new instance of that Cipher and initializing it. + * + * @param key the encryption key + * @param random the source of randomness + * @param params the algorithm parameters + * @throws InvalidKeyException if the given key is inappropriate for initializing this + * cipher + * @throws InvalidAlgorithmParameterException if the given algorithm parameters are inappropriate for + * this cipher, or if this cipher is initialized with + * <tt>null</tt> parameters and cannot generate parameters + * itself. + */ + public final void initEncrypt(Key key, AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException, + InvalidAlgorithmParameterException + { + opMode = ENCRYPT_MODE; + initCipherEncrypt(key, params, random); + } + + /** + * Initialize the cipher for decryption by forwarding it to initDecrypt(Key, + * FlexiSecureRandom). + * <p/> + * If this cipher requires any algorithm parameters that cannot be derived + * from the given key, the underlying cipher implementation is supposed to + * generate the required parameters itself (using provider-specific default + * or random values) if it is being initialized for encryption, and raise an + * InvalidKeyException if it is being initialized for decryption. The + * generated parameters can be retrieved using {@link #getParameters()}. + * + * @param key the decryption key + * @throws InvalidKeyException if the given key is inappropriate for initializing this + * cipher. + */ + public final void initDecrypt(Key key) + throws InvalidKeyException + { + try + { + initDecrypt(key, null); + } + catch (InvalidAlgorithmParameterException iape) + { + throw new InvalidParameterException( + "This cipher needs algorithm parameters for initialization (cannot be null)."); + } + } + + /** + * Initialize the cipher with a certain key for data decryption. + * <p/> + * If this cipher requires any random bytes (e.g., for parameter + * generation), it will get them from <tt>random</tt>. + * <p/> + * Note that when a Cipher object is initialized, it loses all + * previously-acquired state. In other words, initializing a Cipher is + * equivalent to creating a new instance of that Cipher and initializing it + * + * @param key the decryption key + * @param params the algorithm parameters + * @throws InvalidKeyException if the given key is inappropriate for initializing this + * cipher + * @throws InvalidAlgorithmParameterException if the given algorithm parameters are inappropriate for + * this cipher, or if this cipher is initialized with + * <tt>null</tt> parameters and cannot generate parameters + * itself. + */ + public final void initDecrypt(Key key, AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + opMode = DECRYPT_MODE; + initCipherDecrypt(key, params); + } + + /** + * Continue a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized), processing another data part. + * + * @param input the input buffer + * @param inOff the offset where the input starts + * @param inLen the input length + * @return a new buffer with the result (maybe an empty byte array) + */ + public abstract byte[] update(byte[] input, int inOff, int inLen); + + /** + * Continue a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized), processing another data part. + * + * @param input the input buffer + * @param inOff the offset where the input starts + * @param inLen the input length + * @param output the output buffer + * @param outOff the offset where the result is stored + * @return the length of the output + * @throws ShortBufferException if the output buffer is too small to hold the result. + */ + public final int update(byte[] input, int inOff, int inLen, byte[] output, + int outOff) + throws ShortBufferException + { + if (output.length < getOutputSize(inLen)) + { + throw new ShortBufferException("output"); + } + byte[] out = update(input, inOff, inLen); + System.arraycopy(out, 0, output, outOff, out.length); + return out.length; + } + + /** + * Finish a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized). + * + * @param input the input buffer + * @param inOff the offset where the input starts + * @param inLen the input length + * @return a new buffer with the result + * @throws BadPaddingException if the ciphertext is invalid. + */ + public abstract byte[] doFinal(byte[] input, int inOff, int inLen) + throws BadPaddingException; + + /** + * Finish a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized). + * + * @param input the input buffer + * @param inOff the offset where the input starts + * @param inLen the input length + * @param output the buffer for the result + * @param outOff the offset where the result is stored + * @return the output length + * @throws ShortBufferException if the output buffer is too small to hold the result. + * @throws BadPaddingException if the ciphertext is invalid. + */ + public final int doFinal(byte[] input, int inOff, int inLen, byte[] output, + int outOff) + throws ShortBufferException, BadPaddingException + { + + if (output.length < getOutputSize(inLen)) + { + throw new ShortBufferException("Output buffer too short."); + } + byte[] out = doFinal(input, inOff, inLen); + System.arraycopy(out, 0, output, outOff, out.length); + return out.length; + } + + /** + * Compute the output size of an update() or doFinal() operation of a hybrid + * asymmetric cipher in encryption mode when given input of the specified + * length. + * + * @param inLen the length of the input + * @return the output size + */ + protected abstract int encryptOutputSize(int inLen); + + /** + * Compute the output size of an update() or doFinal() operation of a hybrid + * asymmetric cipher in decryption mode when given input of the specified + * length. + * + * @param inLen the length of the input + * @return the output size + */ + protected abstract int decryptOutputSize(int inLen); + + /** + * Initialize the AsymmetricHybridCipher with a certain key for data + * encryption. + * + * @param key the key which has to be used to encrypt data + * @param params the algorithm parameters + * @param sr the source of randomness + * @throws InvalidKeyException if the given key is inappropriate for initializing this + * cipher. + * @throws InvalidAlgorithmParameterException if the given parameters are inappropriate for + * initializing this cipher. + */ + protected abstract void initCipherEncrypt(Key key, + AlgorithmParameterSpec params, SecureRandom sr) + throws InvalidKeyException, InvalidAlgorithmParameterException; + + /** + * Initialize the AsymmetricHybridCipher with a certain key for data + * encryption. + * + * @param key the key which has to be used to decrypt data + * @param params the algorithm parameters + * @throws InvalidKeyException if the given key is inappropriate for initializing this + * cipher + * @throws InvalidAlgorithmParameterException if the given parameters are inappropriate for + * initializing this cipher. + */ + protected abstract void initCipherDecrypt(Key key, + AlgorithmParameterSpec params) + throws InvalidKeyException, + InvalidAlgorithmParameterException; + +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/util/CipherSpiExt.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/util/CipherSpiExt.java new file mode 100644 index 00000000..7978e2ae --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/util/CipherSpiExt.java @@ -0,0 +1,635 @@ +package org.spongycastle.pqc.jcajce.provider.util; + + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.BadPaddingException; +import javax.crypto.CipherSpi; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.ShortBufferException; + +/** + * The CipherSpiExt class extends CipherSpi. + */ +public abstract class CipherSpiExt + extends CipherSpi +{ + + /** + * Constant specifying encrypt mode. + */ + public static final int ENCRYPT_MODE = javax.crypto.Cipher.ENCRYPT_MODE; + + /** + * Constant specifying decrypt mode. + */ + public static final int DECRYPT_MODE = javax.crypto.Cipher.DECRYPT_MODE; + + /** + * The operation mode for this cipher ({@link #ENCRYPT_MODE} or + * {@link #DECRYPT_MODE}). + */ + protected int opMode; + + // **************************************************** + // JCA adapter methods + // **************************************************** + + /** + * Initialize this cipher object with a proper key and some random seed. + * Before a cipher object is ready for data processing, it has to be + * initialized according to the desired cryptographic operation, which is + * specified by the <tt>opMode</tt> parameter. + * <p/> + * If this cipher (including its underlying mode or padding scheme) requires + * any random bytes, it will obtain them from <tt>random</tt>. + * <p/> + * Note: If the mode needs an initialization vector, a blank array is used + * in this case. + * + * @param opMode the operation mode ({@link #ENCRYPT_MODE} or + * {@link #DECRYPT_MODE}) + * @param key the key + * @param random the random seed + * @throws java.security.InvalidKeyException if the key is inappropriate for initializing this cipher. + */ + protected final void engineInit(int opMode, java.security.Key key, + java.security.SecureRandom random) + throws java.security.InvalidKeyException + { + + try + { + engineInit(opMode, key, + (java.security.spec.AlgorithmParameterSpec)null, random); + } + catch (java.security.InvalidAlgorithmParameterException e) + { + throw new InvalidParameterException(e.getMessage()); + } + } + + /** + * Initialize this cipher with a key, a set of algorithm parameters, and a + * source of randomness. The cipher is initialized for encryption or + * decryption, depending on the value of <tt>opMode</tt>. + * <p/> + * If this cipher (including its underlying mode or padding scheme) requires + * any random bytes, it will obtain them from <tt>random</tt>. Note that + * when a {@link BlockCipher} object is initialized, it loses all + * previously-acquired state. In other words, initializing a Cipher is + * equivalent to creating a new instance of that Cipher and initializing it. + * <p/> + * Note: If the mode needs an initialization vector, a try to retrieve it + * from the AlgorithmParametersSpec is made. + * + * @param opMode the operation mode ({@link #ENCRYPT_MODE} or + * {@link #DECRYPT_MODE}) + * @param key the key + * @param algParams the algorithm parameters + * @param random the random seed + * @throws java.security.InvalidKeyException if the key is inappropriate for initializing this block + * cipher. + * @throws java.security.InvalidAlgorithmParameterException if the parameters are inappropriate for initializing this + * block cipher. + */ + protected final void engineInit(int opMode, java.security.Key key, + java.security.AlgorithmParameters algParams, + java.security.SecureRandom random) + throws java.security.InvalidKeyException, + java.security.InvalidAlgorithmParameterException + { + + // if algParams are not specified, initialize without them + if (algParams == null) + { + engineInit(opMode, key, random); + return; + } + + AlgorithmParameterSpec paramSpec = null; + // XXX getting AlgorithmParameterSpec from AlgorithmParameters + + engineInit(opMode, key, paramSpec, random); + } + + /** + * Initialize this cipher with a key, a set of algorithm parameters, and a + * source of randomness. The cipher is initialized for one of the following + * four operations: encryption, decryption, key wrapping or key unwrapping, + * depending on the value of opMode. If this cipher (including its + * underlying feedback or padding scheme) requires any random bytes (e.g., + * for parameter generation), it will get them from random. Note that when a + * Cipher object is initialized, it loses all previously-acquired state. In + * other words, initializing a Cipher is equivalent to creating a new + * instance of that Cipher and initializing it. + * + * @param opMode the operation mode ({@link #ENCRYPT_MODE} or + * {@link #DECRYPT_MODE}) + * @param key the encryption key + * @param params the algorithm parameters + * @param javaRand the source of randomness + * @throws java.security.InvalidKeyException if the given key is inappropriate for initializing this + * cipher + * @throws java.security.InvalidAlgorithmParameterException if the given algorithm parameters are inappropriate for + * this cipher, or if this cipher is being initialized for + * decryption and requires algorithm parameters and the + * parameters are null. + */ + protected void engineInit(int opMode, java.security.Key key, + java.security.spec.AlgorithmParameterSpec params, + java.security.SecureRandom javaRand) + throws java.security.InvalidKeyException, + java.security.InvalidAlgorithmParameterException + { + + if ((params != null) && !(params instanceof AlgorithmParameterSpec)) + { + throw new java.security.InvalidAlgorithmParameterException(); + } + + if ((key == null) || !(key instanceof Key)) + { + throw new java.security.InvalidKeyException(); + } + + this.opMode = opMode; + + if (opMode == ENCRYPT_MODE) + { + SecureRandom flexiRand = javaRand; + initEncrypt((Key)key, (AlgorithmParameterSpec)params, flexiRand); + + } + else if (opMode == DECRYPT_MODE) + { + initDecrypt((Key)key, (AlgorithmParameterSpec)params); + + } + } + + /** + * Return the result of the last step of a multi-step en-/decryption + * operation or the result of a single-step en-/decryption operation by + * processing the given input data and any remaining buffered data. The data + * to be processed is given in an input byte array. Beginning at + * inputOffset, only the first inputLen bytes are en-/decrypted, including + * any buffered bytes of a previous update operation. If necessary, padding + * is performed. The result is returned as a output byte array. + * + * @param input the byte array holding the data to be processed + * @param inOff the offset indicating the start position within the input + * byte array + * @param inLen the number of bytes to be processed + * @return the byte array containing the en-/decrypted data + * @throws javax.crypto.IllegalBlockSizeException if the ciphertext length is not a multiple of the + * blocklength. + * @throws javax.crypto.BadPaddingException if unpadding is not possible. + */ + protected final byte[] engineDoFinal(byte[] input, int inOff, int inLen) + throws javax.crypto.IllegalBlockSizeException, + javax.crypto.BadPaddingException + { + return doFinal(input, inOff, inLen); + } + + /** + * Perform the last step of a multi-step en-/decryption operation or a + * single-step en-/decryption operation by processing the given input data + * and any remaining buffered data. The data to be processed is given in an + * input byte array. Beginning at inputOffset, only the first inputLen bytes + * are en-/decrypted, including any buffered bytes of a previous update + * operation. If necessary, padding is performed. The result is stored in + * the given output byte array, beginning at outputOffset. The number of + * bytes stored in this byte array are returned. + * + * @param input the byte array holding the data to be processed + * @param inOff the offset indicating the start position within the input + * byte array + * @param inLen the number of bytes to be processed + * @param output the byte array for holding the result + * @param outOff the offset indicating the start position within the output + * byte array to which the en/decrypted data is written + * @return the number of bytes stored in the output byte array + * @throws javax.crypto.ShortBufferException if the output buffer is too short to hold the output. + * @throws javax.crypto.IllegalBlockSizeException if the ciphertext length is not a multiple of the + * blocklength. + * @throws javax.crypto.BadPaddingException if unpadding is not possible. + */ + protected final int engineDoFinal(byte[] input, int inOff, int inLen, + byte[] output, int outOff) + throws javax.crypto.ShortBufferException, + javax.crypto.IllegalBlockSizeException, + javax.crypto.BadPaddingException + { + return doFinal(input, inOff, inLen, output, outOff); + } + + /** + * @return the block size (in bytes), or 0 if the underlying algorithm is + * not a block cipher + */ + protected final int engineGetBlockSize() + { + return getBlockSize(); + } + + /** + * Return the key size of the given key object in bits. + * + * @param key the key object + * @return the key size in bits of the given key object + * @throws java.security.InvalidKeyException if key is invalid. + */ + protected final int engineGetKeySize(java.security.Key key) + throws java.security.InvalidKeyException + { + if (!(key instanceof Key)) + { + throw new java.security.InvalidKeyException("Unsupported key."); + } + return getKeySize((Key)key); + } + + /** + * Return the initialization vector. This is useful in the context of + * password-based encryption or decryption, where the IV is derived from a + * user-provided passphrase. + * + * @return the initialization vector in a new buffer, or <tt>null</tt> if + * the underlying algorithm does not use an IV, or if the IV has not + * yet been set. + */ + protected final byte[] engineGetIV() + { + return getIV(); + } + + /** + * Return the length in bytes that an output buffer would need to be in + * order to hold the result of the next update or doFinal operation, given + * the input length inputLen (in bytes). + * <p/> + * This call takes into account any unprocessed (buffered) data from a + * previous update call, and padding. + * <p/> + * The actual output length of the next update or doFinal call may be + * smaller than the length returned by this method. + * + * @param inLen the input length (in bytes) + * @return the required output buffer size (in bytes) + */ + protected final int engineGetOutputSize(int inLen) + { + return getOutputSize(inLen); + } + + /** + * Returns the parameters used with this cipher. + * <p/> + * The returned parameters may be the same that were used to initialize this + * cipher, or may contain the default set of parameters or a set of randomly + * generated parameters used by the underlying cipher implementation + * (provided that the underlying cipher implementation uses a default set of + * parameters or creates new parameters if it needs parameters but was not + * initialized with any). + * + * @return the parameters used with this cipher, or null if this cipher does + * not use any parameters. + */ + protected final java.security.AlgorithmParameters engineGetParameters() + { + // TODO + return null; + } + + /** + * Set the mode of this cipher. + * + * @param modeName the cipher mode + * @throws java.security.NoSuchAlgorithmException if neither the mode with the given name nor the default + * mode can be found + */ + protected final void engineSetMode(String modeName) + throws java.security.NoSuchAlgorithmException + { + setMode(modeName); + } + + /** + * Set the padding scheme of this cipher. + * + * @param paddingName the padding scheme + * @throws javax.crypto.NoSuchPaddingException if the requested padding scheme cannot be found. + */ + protected final void engineSetPadding(String paddingName) + throws javax.crypto.NoSuchPaddingException + { + setPadding(paddingName); + } + + /** + * Return the result of the next step of a multi-step en-/decryption + * operation. The data to be processed is given in an input byte array. + * Beginning at inputOffset, only the first inputLen bytes are + * en-/decrypted. The result is returned as a byte array. + * + * @param input the byte array holding the data to be processed + * @param inOff the offset indicating the start position within the input + * byte array + * @param inLen the number of bytes to be processed + * @return the byte array containing the en-/decrypted data + */ + protected final byte[] engineUpdate(byte[] input, int inOff, int inLen) + { + return update(input, inOff, inLen); + } + + /** + * Perform the next step of a multi-step en-/decryption operation. The data + * to be processed is given in an input byte array. Beginning at + * inputOffset, only the first inputLen bytes are en-/decrypted. The result + * is stored in the given output byte array, beginning at outputOffset. The + * number of bytes stored in this output byte array are returned. + * + * @param input the byte array holding the data to be processed + * @param inOff the offset indicating the start position within the input + * byte array + * @param inLen the number of bytes to be processed + * @param output the byte array for holding the result + * @param outOff the offset indicating the start position within the output + * byte array to which the en-/decrypted data is written + * @return the number of bytes that are stored in the output byte array + * @throws javax.crypto.ShortBufferException if the output buffer is too short to hold the output. + */ + protected final int engineUpdate(final byte[] input, final int inOff, + final int inLen, byte[] output, final int outOff) + throws javax.crypto.ShortBufferException + { + return update(input, inOff, inLen, output, outOff); + } + + /** + * Initialize this cipher with a key, a set of algorithm parameters, and a + * source of randomness for encryption. + * <p/> + * If this cipher requires any algorithm parameters and paramSpec is null, + * the underlying cipher implementation is supposed to generate the required + * parameters itself (using provider-specific default or random values) if + * it is being initialized for encryption, and raise an + * InvalidAlgorithmParameterException if it is being initialized for + * decryption. The generated parameters can be retrieved using + * engineGetParameters or engineGetIV (if the parameter is an IV). + * <p/> + * If this cipher (including its underlying feedback or padding scheme) + * requires any random bytes (e.g., for parameter generation), it will get + * them from random. + * <p/> + * Note that when a {@link BlockCipher} object is initialized, it loses all + * previously-acquired state. In other words, initializing a Cipher is + * equivalent to creating a new instance of that Cipher and initializing it. + * + * @param key the encryption key + * @param cipherParams the cipher parameters + * @param random the source of randomness + * @throws InvalidKeyException if the given key is inappropriate for initializing this + * block cipher. + * @throws InvalidAlgorithmParameterException if the parameters are inappropriate for initializing this + * block cipher. + */ + public abstract void initEncrypt(Key key, + AlgorithmParameterSpec cipherParams, SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException; + + /** + * Initialize this cipher with a key, a set of algorithm parameters, and a + * source of randomness for decryption. + * <p/> + * If this cipher requires any algorithm parameters and paramSpec is null, + * the underlying cipher implementation is supposed to generate the required + * parameters itself (using provider-specific default or random values) if + * it is being initialized for encryption, and throw an + * {@link InvalidAlgorithmParameterException} if it is being initialized for + * decryption. The generated parameters can be retrieved using + * engineGetParameters or engineGetIV (if the parameter is an IV). + * <p/> + * If this cipher (including its underlying feedback or padding scheme) + * requires any random bytes (e.g., for parameter generation), it will get + * them from random. + * <p/> + * Note that when a {@link BlockCipher} object is initialized, it loses all + * previously-acquired state. In other words, initializing a Cipher is + * equivalent to creating a new instance of that Cipher and initializing it. + * + * @param key the encryption key + * @param cipherParams the cipher parameters + * @throws InvalidKeyException if the given key is inappropriate for initializing this + * block cipher. + * @throws InvalidAlgorithmParameterException if the parameters are inappropriate for initializing this + * block cipher. + */ + public abstract void initDecrypt(Key key, + AlgorithmParameterSpec cipherParams) + throws InvalidKeyException, + InvalidAlgorithmParameterException; + + /** + * @return the name of this cipher + */ + public abstract String getName(); + + /** + * @return the block size (in bytes), or 0 if the underlying algorithm is + * not a block cipher + */ + public abstract int getBlockSize(); + + /** + * Returns the length in bytes that an output buffer would need to be in + * order to hold the result of the next update or doFinal operation, given + * the input length inputLen (in bytes). + * <p/> + * This call takes into account any unprocessed (buffered) data from a + * previous update call, and padding. + * <p/> + * The actual output length of the next update or doFinal call may be + * smaller than the length returned by this method. + * + * @param inputLen the input length (in bytes) + * @return the required output buffer size (in bytes) + */ + public abstract int getOutputSize(int inputLen); + + /** + * Return the key size of the given key object in bits. + * + * @param key the key object + * @return the key size in bits of the given key object + * @throws InvalidKeyException if key is invalid. + */ + public abstract int getKeySize(Key key) + throws InvalidKeyException; + + /** + * Returns the parameters used with this cipher. + * <p/> + * The returned parameters may be the same that were used to initialize this + * cipher, or may contain the default set of parameters or a set of randomly + * generated parameters used by the underlying cipher implementation + * (provided that the underlying cipher implementation uses a default set of + * parameters or creates new parameters if it needs parameters but was not + * initialized with any). + * + * @return the parameters used with this cipher, or null if this cipher does + * not use any parameters. + */ + public abstract AlgorithmParameterSpec getParameters(); + + /** + * Return the initialization vector. This is useful in the context of + * password-based encryption or decryption, where the IV is derived from a + * user-provided passphrase. + * + * @return the initialization vector in a new buffer, or <tt>null</tt> if + * the underlying algorithm does not use an IV, or if the IV has not + * yet been set. + */ + public abstract byte[] getIV(); + + /** + * Set the mode of this cipher. + * + * @param mode the cipher mode + * @throws NoSuchModeException if the requested mode cannot be found. + */ + protected abstract void setMode(String mode) + throws NoSuchAlgorithmException; + + /** + * Set the padding mechanism of this cipher. + * + * @param padding the padding mechanism + * @throws NoSuchPaddingException if the requested padding scheme cannot be found. + */ + protected abstract void setPadding(String padding) + throws NoSuchPaddingException; + + /** + * Continue a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized), processing another data part. + * + * @param input the input buffer + * @return a new buffer with the result (maybe an empty byte array) + */ + public final byte[] update(byte[] input) + { + return update(input, 0, input.length); + } + + /** + * Continue a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized), processing another data part. + * + * @param input the input buffer + * @param inOff the offset where the input starts + * @param inLen the input length + * @return a new buffer with the result (maybe an empty byte array) + */ + public abstract byte[] update(byte[] input, int inOff, int inLen); + + /** + * Continue a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized), processing another data part. + * + * @param input the input buffer + * @param inOff the offset where the input starts + * @param inLen the input length + * @param output the output buffer + * @param outOff the offset where the result is stored + * @return the length of the output + * @throws ShortBufferException if the output buffer is too small to hold the result. + */ + public abstract int update(byte[] input, int inOff, int inLen, + byte[] output, int outOff) + throws ShortBufferException; + + /** + * Finish a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized). + * + * @return a new buffer with the result + * @throws IllegalBlockSizeException if this cipher is a block cipher and the total input + * length is not a multiple of the block size (for + * encryption when no padding is used or for decryption). + * @throws BadPaddingException if this cipher is a block cipher and unpadding fails. + */ + public final byte[] doFinal() + throws IllegalBlockSizeException, + BadPaddingException + { + return doFinal(null, 0, 0); + } + + /** + * Finish a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized). + * + * @param input the input buffer + * @return a new buffer with the result + * @throws IllegalBlockSizeException if this cipher is a block cipher and the total input + * length is not a multiple of the block size (for + * encryption when no padding is used or for decryption). + * @throws BadPaddingException if this cipher is a block cipher and unpadding fails. + */ + public final byte[] doFinal(byte[] input) + throws IllegalBlockSizeException, + BadPaddingException + { + return doFinal(input, 0, input.length); + } + + /** + * Finish a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized). + * + * @param input the input buffer + * @param inOff the offset where the input starts + * @param inLen the input length + * @return a new buffer with the result + * @throws IllegalBlockSizeException if this cipher is a block cipher and the total input + * length is not a multiple of the block size (for + * encryption when no padding is used or for decryption). + * @throws BadPaddingException if this cipher is a block cipher and unpadding fails. + */ + public abstract byte[] doFinal(byte[] input, int inOff, int inLen) + throws IllegalBlockSizeException, BadPaddingException; + + /** + * Finish a multiple-part encryption or decryption operation (depending on + * how this cipher was initialized). + * + * @param input the input buffer + * @param inOff the offset where the input starts + * @param inLen the input length + * @param output the buffer for the result + * @param outOff the offset where the result is stored + * @return the output length + * @throws ShortBufferException if the output buffer is too small to hold the result. + * @throws IllegalBlockSizeException if this cipher is a block cipher and the total input + * length is not a multiple of the block size (for + * encryption when no padding is used or for decryption). + * @throws BadPaddingException if this cipher is a block cipher and unpadding fails. + */ + public abstract int doFinal(byte[] input, int inOff, int inLen, + byte[] output, int outOff) + throws ShortBufferException, + IllegalBlockSizeException, BadPaddingException; + +} diff --git a/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/util/KeyUtil.java b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/util/KeyUtil.java new file mode 100644 index 00000000..ae780b6d --- /dev/null +++ b/prov/src/main/java/org/spongycastle/pqc/jcajce/provider/util/KeyUtil.java @@ -0,0 +1,72 @@ +package org.spongycastle.pqc.jcajce.provider.util; + +import org.spongycastle.asn1.ASN1Encodable; +import org.spongycastle.asn1.ASN1Encoding; +import org.spongycastle.asn1.pkcs.PrivateKeyInfo; +import org.spongycastle.asn1.x509.AlgorithmIdentifier; +import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; + +public class KeyUtil +{ + public static byte[] getEncodedSubjectPublicKeyInfo(AlgorithmIdentifier algId, ASN1Encodable keyData) + { + try + { + return getEncodedSubjectPublicKeyInfo(new SubjectPublicKeyInfo(algId, keyData)); + } + catch (Exception e) + { + return null; + } + } + + public static byte[] getEncodedSubjectPublicKeyInfo(AlgorithmIdentifier algId, byte[] keyData) + { + try + { + return getEncodedSubjectPublicKeyInfo(new SubjectPublicKeyInfo(algId, keyData)); + } + catch (Exception e) + { + return null; + } + } + + public static byte[] getEncodedSubjectPublicKeyInfo(SubjectPublicKeyInfo info) + { + try + { + return info.getEncoded(ASN1Encoding.DER); + } + catch (Exception e) + { + return null; + } + } + + public static byte[] getEncodedPrivateKeyInfo(AlgorithmIdentifier algId, ASN1Encodable privKey) + { + try + { + PrivateKeyInfo info = new PrivateKeyInfo(algId, privKey.toASN1Primitive()); + + return getEncodedPrivateKeyInfo(info); + } + catch (Exception e) + { + return null; + } + } + + public static byte[] getEncodedPrivateKeyInfo(PrivateKeyInfo info) + { + try + { + return info.getEncoded(ASN1Encoding.DER); + } + catch (Exception e) + { + return null; + } + } +} |