1 package org.bouncycastle.pqc.crypto.util; 2 3 import java.io.IOException; 4 5 import org.bouncycastle.asn1.ASN1Set; 6 import org.bouncycastle.asn1.DEROctetString; 7 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; 8 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; 9 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; 10 import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 11 import org.bouncycastle.crypto.params.AsymmetricKeyParameter; 12 import org.bouncycastle.pqc.asn1.McElieceCCA2PrivateKey; 13 import org.bouncycastle.pqc.asn1.McEliecePrivateKey; 14 import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers; 15 import org.bouncycastle.pqc.asn1.SPHINCS256KeyParams; 16 import org.bouncycastle.pqc.asn1.XMSSKeyParams; 17 import org.bouncycastle.pqc.asn1.XMSSMTKeyParams; 18 import org.bouncycastle.pqc.asn1.XMSSMTPrivateKey; 19 import org.bouncycastle.pqc.asn1.XMSSPrivateKey; 20 import org.bouncycastle.pqc.crypto.lms.Composer; 21 import org.bouncycastle.pqc.crypto.lms.HSSPrivateKeyParameters; 22 import org.bouncycastle.pqc.crypto.lms.LMSPrivateKeyParameters; 23 import org.bouncycastle.pqc.crypto.mceliece.McElieceCCA2PrivateKeyParameters; 24 import org.bouncycastle.pqc.crypto.mceliece.McEliecePrivateKeyParameters; 25 import org.bouncycastle.pqc.crypto.newhope.NHPrivateKeyParameters; 26 import org.bouncycastle.pqc.crypto.qtesla.QTESLAPrivateKeyParameters; 27 import org.bouncycastle.pqc.crypto.sphincs.SPHINCSPrivateKeyParameters; 28 import org.bouncycastle.pqc.crypto.xmss.BDS; 29 import org.bouncycastle.pqc.crypto.xmss.BDSStateMap; 30 import org.bouncycastle.pqc.crypto.xmss.XMSSMTPrivateKeyParameters; 31 import org.bouncycastle.pqc.crypto.xmss.XMSSPrivateKeyParameters; 32 import org.bouncycastle.pqc.crypto.xmss.XMSSUtil; 33 import org.bouncycastle.util.Pack; 34 35 /** 36 * Factory to create ASN.1 private key info objects from lightweight private keys. 37 */ 38 public class PrivateKeyInfoFactory 39 { PrivateKeyInfoFactory()40 private PrivateKeyInfoFactory() 41 { 42 43 } 44 45 /** 46 * Create a PrivateKeyInfo representation of a private key. 47 * 48 * @param privateKey the key to be encoded into the info object. 49 * @return the appropriate PrivateKeyInfo 50 * @throws java.io.IOException on an error encoding the key 51 */ createPrivateKeyInfo(AsymmetricKeyParameter privateKey)52 public static PrivateKeyInfo createPrivateKeyInfo(AsymmetricKeyParameter privateKey) throws IOException 53 { 54 return createPrivateKeyInfo(privateKey, null); 55 } 56 57 /** 58 * Create a PrivateKeyInfo representation of a private key with attributes. 59 * 60 * @param privateKey the key to be encoded into the info object. 61 * @param attributes the set of attributes to be included. 62 * @return the appropriate PrivateKeyInfo 63 * @throws java.io.IOException on an error encoding the key 64 */ createPrivateKeyInfo(AsymmetricKeyParameter privateKey, ASN1Set attributes)65 public static PrivateKeyInfo createPrivateKeyInfo(AsymmetricKeyParameter privateKey, ASN1Set attributes) throws IOException 66 { 67 if (privateKey instanceof QTESLAPrivateKeyParameters) 68 { 69 QTESLAPrivateKeyParameters keyParams = (QTESLAPrivateKeyParameters)privateKey; 70 71 AlgorithmIdentifier algorithmIdentifier = Utils.qTeslaLookupAlgID(keyParams.getSecurityCategory()); 72 73 return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(keyParams.getSecret()), attributes); 74 } 75 else if (privateKey instanceof SPHINCSPrivateKeyParameters) 76 { 77 SPHINCSPrivateKeyParameters params = (SPHINCSPrivateKeyParameters)privateKey; 78 AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.sphincs256, 79 new SPHINCS256KeyParams(Utils.sphincs256LookupTreeAlgID(params.getTreeDigest()))); 80 81 return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(params.getKeyData())); 82 } 83 else if (privateKey instanceof NHPrivateKeyParameters) 84 { 85 NHPrivateKeyParameters params = (NHPrivateKeyParameters)privateKey; 86 87 AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.newHope); 88 89 short[] privateKeyData = params.getSecData(); 90 91 byte[] octets = new byte[privateKeyData.length * 2]; 92 for (int i = 0; i != privateKeyData.length; i++) 93 { 94 Pack.shortToLittleEndian(privateKeyData[i], octets, i * 2); 95 } 96 97 return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(octets)); 98 } 99 else if (privateKey instanceof LMSPrivateKeyParameters) 100 { 101 LMSPrivateKeyParameters params = (LMSPrivateKeyParameters)privateKey; 102 103 byte[] encoding = Composer.compose().u32str(1).bytes(params).build(); 104 byte[] pubEncoding = Composer.compose().u32str(1).bytes(params.getPublicKey()).build(); 105 106 AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_alg_hss_lms_hashsig); 107 return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(encoding), attributes, pubEncoding); 108 } 109 else if (privateKey instanceof HSSPrivateKeyParameters) 110 { 111 HSSPrivateKeyParameters params = (HSSPrivateKeyParameters)privateKey; 112 113 byte[] encoding = Composer.compose().u32str(params.getL()).bytes(params).build(); 114 byte[] pubEncoding = Composer.compose().u32str(params.getL()).bytes(params.getPublicKey().getLMSPublicKey()).build(); 115 116 AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_alg_hss_lms_hashsig); 117 return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(encoding), attributes, pubEncoding); 118 } 119 else if (privateKey instanceof XMSSPrivateKeyParameters) 120 { 121 XMSSPrivateKeyParameters keyParams = (XMSSPrivateKeyParameters)privateKey; 122 AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.xmss, 123 new XMSSKeyParams(keyParams.getParameters().getHeight(), 124 Utils.xmssLookupTreeAlgID(keyParams.getTreeDigest()))); 125 126 return new PrivateKeyInfo(algorithmIdentifier, xmssCreateKeyStructure(keyParams), attributes); 127 } 128 else if (privateKey instanceof XMSSMTPrivateKeyParameters) 129 { 130 XMSSMTPrivateKeyParameters keyParams = (XMSSMTPrivateKeyParameters)privateKey; 131 AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.xmss_mt, 132 new XMSSMTKeyParams(keyParams.getParameters().getHeight(), keyParams.getParameters().getLayers(), 133 Utils.xmssLookupTreeAlgID(keyParams.getTreeDigest()))); 134 135 return new PrivateKeyInfo(algorithmIdentifier, xmssmtCreateKeyStructure(keyParams), attributes); 136 } 137 else if (privateKey instanceof McElieceCCA2PrivateKeyParameters) 138 { 139 McElieceCCA2PrivateKeyParameters priv = (McElieceCCA2PrivateKeyParameters)privateKey; 140 McElieceCCA2PrivateKey mcEliecePriv = new McElieceCCA2PrivateKey(priv.getN(), priv.getK(), priv.getField(), priv.getGoppaPoly(), priv.getP(), Utils.getAlgorithmIdentifier(priv.getDigest())); 141 AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PQCObjectIdentifiers.mcElieceCca2); 142 143 return new PrivateKeyInfo(algorithmIdentifier, mcEliecePriv); 144 } 145 else 146 { 147 throw new IOException("key parameters not recognized"); 148 } 149 } 150 xmssCreateKeyStructure(XMSSPrivateKeyParameters keyParams)151 private static XMSSPrivateKey xmssCreateKeyStructure(XMSSPrivateKeyParameters keyParams) 152 throws IOException 153 { 154 byte[] keyData = keyParams.getEncoded(); 155 156 int n = keyParams.getParameters().getTreeDigestSize(); 157 int totalHeight = keyParams.getParameters().getHeight(); 158 int indexSize = 4; 159 int secretKeySize = n; 160 int secretKeyPRFSize = n; 161 int publicSeedSize = n; 162 int rootSize = n; 163 164 int position = 0; 165 int index = (int)XMSSUtil.bytesToXBigEndian(keyData, position, indexSize); 166 if (!XMSSUtil.isIndexValid(totalHeight, index)) 167 { 168 throw new IllegalArgumentException("index out of bounds"); 169 } 170 position += indexSize; 171 byte[] secretKeySeed = XMSSUtil.extractBytesAtOffset(keyData, position, secretKeySize); 172 position += secretKeySize; 173 byte[] secretKeyPRF = XMSSUtil.extractBytesAtOffset(keyData, position, secretKeyPRFSize); 174 position += secretKeyPRFSize; 175 byte[] publicSeed = XMSSUtil.extractBytesAtOffset(keyData, position, publicSeedSize); 176 position += publicSeedSize; 177 byte[] root = XMSSUtil.extractBytesAtOffset(keyData, position, rootSize); 178 position += rootSize; 179 /* import BDS state */ 180 byte[] bdsStateBinary = XMSSUtil.extractBytesAtOffset(keyData, position, keyData.length - position); 181 BDS bds = null; 182 try 183 { 184 bds = (BDS)XMSSUtil.deserialize(bdsStateBinary, BDS.class); 185 } 186 catch (ClassNotFoundException e) 187 { 188 throw new IOException("cannot parse BDS: " + e.getMessage()); 189 } 190 191 if ((bds.getMaxIndex() != (1 << totalHeight) - 1)) 192 { 193 return new XMSSPrivateKey(index, secretKeySeed, secretKeyPRF, publicSeed, root, bdsStateBinary, bds.getMaxIndex()); 194 } 195 else 196 { 197 return new XMSSPrivateKey(index, secretKeySeed, secretKeyPRF, publicSeed, root, bdsStateBinary); 198 } 199 } 200 xmssmtCreateKeyStructure(XMSSMTPrivateKeyParameters keyParams)201 private static XMSSMTPrivateKey xmssmtCreateKeyStructure(XMSSMTPrivateKeyParameters keyParams) 202 throws IOException 203 { 204 byte[] keyData = keyParams.getEncoded(); 205 206 int n = keyParams.getParameters().getTreeDigestSize(); 207 int totalHeight = keyParams.getParameters().getHeight(); 208 int indexSize = (totalHeight + 7) / 8; 209 int secretKeySize = n; 210 int secretKeyPRFSize = n; 211 int publicSeedSize = n; 212 int rootSize = n; 213 214 int position = 0; 215 int index = (int)XMSSUtil.bytesToXBigEndian(keyData, position, indexSize); 216 if (!XMSSUtil.isIndexValid(totalHeight, index)) 217 { 218 throw new IllegalArgumentException("index out of bounds"); 219 } 220 position += indexSize; 221 byte[] secretKeySeed = XMSSUtil.extractBytesAtOffset(keyData, position, secretKeySize); 222 position += secretKeySize; 223 byte[] secretKeyPRF = XMSSUtil.extractBytesAtOffset(keyData, position, secretKeyPRFSize); 224 position += secretKeyPRFSize; 225 byte[] publicSeed = XMSSUtil.extractBytesAtOffset(keyData, position, publicSeedSize); 226 position += publicSeedSize; 227 byte[] root = XMSSUtil.extractBytesAtOffset(keyData, position, rootSize); 228 position += rootSize; 229 /* import BDS state */ 230 byte[] bdsStateBinary = XMSSUtil.extractBytesAtOffset(keyData, position, keyData.length - position); 231 BDSStateMap bds = null; 232 try 233 { 234 bds = (BDSStateMap)XMSSUtil.deserialize(bdsStateBinary, BDSStateMap.class); 235 } 236 catch (ClassNotFoundException e) 237 { 238 throw new IOException("cannot parse BDSStateMap: " + e.getMessage()); 239 } 240 241 if ((bds.getMaxIndex() != (1L << totalHeight) - 1)) 242 { 243 return new XMSSMTPrivateKey(index, secretKeySeed, secretKeyPRF, publicSeed, root, bdsStateBinary, bds.getMaxIndex()); 244 } 245 else 246 { 247 return new XMSSMTPrivateKey(index, secretKeySeed, secretKeyPRF, publicSeed, root, bdsStateBinary); 248 } 249 } 250 } 251