diff options
Diffstat (limited to 'ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java')
-rw-r--r-- | ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java new file mode 100644 index 0000000..bf34ea1 --- /dev/null +++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java @@ -0,0 +1,289 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import javacard.framework.JCSystem; +import javacard.framework.Util; +import javacard.security.CryptoException; +import javacard.security.Key; +import javacard.security.MessageDigest; +import javacardx.crypto.Cipher; + +/** This class has the implementation for RSA_OAEP decoding algorithm. */ +public class KMRsaOAEPEncoding extends Cipher { + + public static final byte ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1 = (byte) 0x1E; + public static final byte ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256 = (byte) 0x1F; + + final short MGF1_BUF_SIZE = 256; + static byte[] mgf1Buf; + private Cipher cipher; + private byte hash; + private byte mgf1Hash; + private byte algorithm; + + public KMRsaOAEPEncoding(byte alg) { + setDigests(alg); + cipher = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false); + algorithm = alg; + if (null == mgf1Buf) { + mgf1Buf = + JCSystem.makeTransientByteArray(MGF1_BUF_SIZE, JCSystem.MEMORY_TYPE_TRANSIENT_DESELECT); + } + } + + private void setDigests(byte alg) { + switch (alg) { + case ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1: + hash = MessageDigest.ALG_SHA_256; + mgf1Hash = MessageDigest.ALG_SHA; + break; + case ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256: + hash = MessageDigest.ALG_SHA_256; + mgf1Hash = MessageDigest.ALG_SHA_256; + break; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + } + } + + private short getDigestLength() { + switch (hash) { + case MessageDigest.ALG_SHA: + return MessageDigest.LENGTH_SHA; + case MessageDigest.ALG_SHA_224: + return MessageDigest.LENGTH_SHA_224; + case MessageDigest.ALG_SHA_256: + return MessageDigest.LENGTH_SHA_256; + case MessageDigest.ALG_SHA_384: + return MessageDigest.LENGTH_SHA_384; + case MessageDigest.ALG_SHA_512: + return MessageDigest.LENGTH_SHA_512; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + } + return 0; + } + + @Override + public void init(Key theKey, byte theMode) throws CryptoException { + cipher.init(theKey, theMode); + } + + @Override + public void init(Key theKey, byte theMode, byte[] bArray, short bOff, short bLen) + throws CryptoException { + cipher.init(theKey, theMode, bArray, bOff, bLen); + } + + @Override + public byte getAlgorithm() { + return algorithm; + } + + @Override + public byte getCipherAlgorithm() { + return 0; + } + + @Override + public byte getPaddingAlgorithm() { + return 0; + } + + @Override + public short doFinal( + byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset) + throws CryptoException { + short len = cipher.doFinal(inBuff, inOffset, inLength, outBuff, outOffset); + + // https://tools.ietf.org/html/rfc8017#section-7.1 + // https://www.inf.pucrs.br/~calazans/graduate/TPVLSI_I/RSA-oaep_spec.pdf + // RSA OAEP Encoding and Decoding Mechanism for a 2048 bit RSA Key. + // Msg -> RSA-OAEP-ENCODE -> RSAEncryption -> RSADecryption -> + // RSA-OAEP-DECODE -> Msg + // RSA-OAEP-ENCODE generates an output length of 255, but RSAEncryption + // requires and input of length 256 so we pad 0 to the left of the input + // message and make the length equal to 256 and pass to RSAEncryption. + // RSADecryption takes input length equal to 256 and generates an + // output of length 256. After decryption the first byte of the output + // should be 0(left padding we did in encryption). + // RSA-OAEP-DECODE takes input of length 255 so remove the left padding of 1 + // byte. + if (len != 256 || outBuff[0] != 0) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + Util.arrayCopyNonAtomic( + outBuff, (short) (outOffset + 1), outBuff, (short) 0, (short) (len - 1)); + return rsaOAEPDecode(outBuff, (short) 0, (short) (len - 1)); + } + + @Override + public short update( + byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset) + throws CryptoException { + return cipher.update(inBuff, inOffset, inLength, outBuff, outOffset); + } + + private void maskGenerationFunction1( + byte[] input, + short inputOffset, + short inputLen, + short expectedOutLen, + byte[] outBuf, + short outOffset) { + short counter = 0; + MessageDigest.OneShot md = null; + try { + md = MessageDigest.OneShot.open(mgf1Hash); + short digestLen = md.getLength(); + + Util.arrayCopyNonAtomic(input, inputOffset, mgf1Buf, (short) 0, inputLen); + while (counter < (short) (expectedOutLen / digestLen)) { + I2OS(counter, mgf1Buf, (short) inputLen); + md.doFinal( + mgf1Buf, + (short) 0, + (short) (4 + inputLen), + outBuf, + (short) (outOffset + (counter * digestLen))); + counter++; + } + + if ((short) (counter * digestLen) < expectedOutLen) { + I2OS(counter, mgf1Buf, (short) inputLen); + md.doFinal( + mgf1Buf, + (short) 0, + (short) (4 + inputLen), + outBuf, + (short) (outOffset + (counter * digestLen))); + } + + } finally { + if (md != null) { + md.close(); + } + Util.arrayFillNonAtomic(mgf1Buf, (short) 0, (short) MGF1_BUF_SIZE, (byte) 0); + } + } + + // Integer to Octet String conversion. + private void I2OS(short i, byte[] out, short offset) { + Util.arrayFillNonAtomic(out, (short) offset, (short) 4, (byte) 0); + out[(short) (offset + 3)] = (byte) (i >>> 0); + out[(short) (offset + 2)] = (byte) (i >>> 8); + } + + private short rsaOAEPDecode(byte[] encodedMsg, short encodedMsgOff, short encodedMsgLen) { + MessageDigest.OneShot md = null; + byte[] tmpArray = KMAndroidSEProvider.getInstance().tmpArray; + + try { + short hLen = getDigestLength(); + + if (encodedMsgLen < (short) (2 * hLen + 1)) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + // encodedMsg will be in the format of maskedSeed||maskedDB. + // maskedSeed length is hLen and maskedDB length is (encodedMsgLen - hLen) + // Now retrieve the seedMask by calling MGF(maskedDB, hLen). The length + // of the seedMask is hLen. + // seedMask = MGF(maskedDB, hLen) + maskGenerationFunction1( + encodedMsg, + (short) (encodedMsgOff + hLen), + (short) (encodedMsgLen - hLen), + hLen, + tmpArray, + (short) 0); + + // Get the seed by doing XOR of (maskedSeed ^ seedMask). + // seed = (maskedSeed ^ seedMask) + for (short i = 0; i < hLen; i++) { + // Store the seed in encodeMsg itself. + encodedMsg[(short) (encodedMsgOff + i)] ^= tmpArray[i]; + } + + // Now get the dbMask by calling MGF(seed , (emLen-hLen)). + // dbMask = MGF(seed , (emLen-hLen)). + maskGenerationFunction1( + encodedMsg, + (short) encodedMsgOff, + hLen, + (short) (encodedMsgLen - hLen), + tmpArray, + (short) 0); + + // Get the DB value. DB = (maskedDB ^ dbMask) + // DB = Hash(P)||00||01||Msg, where P is encoding parameters. (P = NULL) + for (short i = 0; i < (short) (encodedMsgLen - hLen); i++) { + // Store the DB inside encodeMsg itself. + encodedMsg[(short) (encodedMsgOff + i + hLen)] ^= tmpArray[i]; + } + + // Verify Hash. + md = MessageDigest.OneShot.open(hash); + Util.arrayFillNonAtomic(tmpArray, (short) 0, (short) 256, (byte) 0); + md.doFinal(tmpArray, (short) 0, (short) 0, tmpArray, (short) 0); + if (0 + != Util.arrayCompare( + encodedMsg, (short) (encodedMsgOff + hLen), tmpArray, (short) 0, hLen)) { + // Verification failed. + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + + // Find the Message block in DB. + // DB = Hash(P)||00||01||Msg, where P is encoding parameters. (P = NULL) + // The message will be located at the end of the Data block (DB). + // The DB block is first constructed by keeping the message at the end and + // to the message 0x01 byte is prepended. The hash of the + // encoding parameters is calculated and then copied from the + // starting of the block and a variable length of 0's are + // appended to the end of the hash till the 0x01 byte. + short start = (short) (encodedMsgOff + encodedMsgLen); + for (short i = (short) (encodedMsgOff + 2 * hLen); + i < (short) (encodedMsgOff + encodedMsgLen); + i++) { + if ((encodedMsg[i] != 0)) { + start = i; + break; + } + } + if ((start >= (short) (encodedMsgOff + encodedMsgLen)) || (encodedMsg[start] != 0x01)) { + // Bad Padding. + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + start++; // Message starting pos. + if (start < (short) (encodedMsgOff + encodedMsgLen)) { + // Copy the message + Util.arrayCopyNonAtomic( + encodedMsg, + start, + encodedMsg, + encodedMsgOff, + (short) (encodedMsgLen - (start - encodedMsgOff))); + } + return (short) (encodedMsgLen - (start - encodedMsgOff)); + + } finally { + if (md != null) { + md.close(); + } + Util.arrayFillNonAtomic(tmpArray, (short) 0, KMAndroidSEProvider.TMP_ARRAY_SIZE, (byte) 0); + } + } +} |