blob: a4484a39f85f54e5c45dd6c9f2f96f7467db8108 [file] [log] [blame]
/*
* Copyright 2017 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" BASIS,
* 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 org.conscrypt;
import java.io.IOException;
import java.security.AlgorithmParametersSpi;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.MGF1ParameterSpec;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
/**
* AlgorithmParameters implementation for OAEP. The only supported encoding format is ASN.1,
* as specified in RFC 4055 section 4.1.
*/
@Internal
public class OAEPParameters extends AlgorithmParametersSpi {
private static final Map<String, String> OID_TO_NAME = new HashMap<String, String>();
private static final Map<String, String> NAME_TO_OID = new HashMap<String, String>();
static {
OID_TO_NAME.put("1.3.14.3.2.26", "SHA-1");
OID_TO_NAME.put("2.16.840.1.101.3.4.2.4", "SHA-224");
OID_TO_NAME.put("2.16.840.1.101.3.4.2.1", "SHA-256");
OID_TO_NAME.put("2.16.840.1.101.3.4.2.2", "SHA-384");
OID_TO_NAME.put("2.16.840.1.101.3.4.2.3", "SHA-512");
for (Map.Entry<String, String> entry : OID_TO_NAME.entrySet()) {
NAME_TO_OID.put(entry.getValue(), entry.getKey());
}
}
private static final String MGF1_OID = "1.2.840.113549.1.1.8";
private static final String PSPECIFIED_OID = "1.2.840.113549.1.1.9";
private OAEPParameterSpec spec = OAEPParameterSpec.DEFAULT;
public OAEPParameters() {}
@Override
protected void engineInit(AlgorithmParameterSpec algorithmParameterSpec)
throws InvalidParameterSpecException {
if (algorithmParameterSpec instanceof OAEPParameterSpec) {
this.spec = (OAEPParameterSpec) algorithmParameterSpec;
} else {
throw new InvalidParameterSpecException("Only OAEPParameterSpec is supported");
}
}
@Override
protected void engineInit(byte[] bytes) throws IOException {
long readRef = 0;
long seqRef = 0;
try {
readRef = NativeCrypto.asn1_read_init(bytes);
seqRef = NativeCrypto.asn1_read_sequence(readRef);
PSource.PSpecified pSpecified = PSource.PSpecified.DEFAULT;
String hash = readHash(seqRef);
String mgfHash = readMgfHash(seqRef);
if (NativeCrypto.asn1_read_next_tag_is(seqRef, 2)) {
long pSourceRef = 0;
long pSourceSeqRef = 0;
try {
pSourceRef = NativeCrypto.asn1_read_tagged(seqRef);
pSourceSeqRef = NativeCrypto.asn1_read_sequence(pSourceRef);
String pSourceOid = NativeCrypto.asn1_read_oid(pSourceSeqRef);
if (!pSourceOid.equals(PSPECIFIED_OID)) {
throw new IOException("Error reading ASN.1 encoding");
}
pSpecified = new PSource.PSpecified(
NativeCrypto.asn1_read_octetstring(pSourceSeqRef));
if (!NativeCrypto.asn1_read_is_empty(pSourceSeqRef)) {
throw new IOException("Error reading ASN.1 encoding");
}
} finally {
NativeCrypto.asn1_read_free(pSourceSeqRef);
NativeCrypto.asn1_read_free(pSourceRef);
}
}
if (!NativeCrypto.asn1_read_is_empty(seqRef)
|| !NativeCrypto.asn1_read_is_empty(readRef)) {
throw new IOException("Error reading ASN.1 encoding");
}
this.spec = new OAEPParameterSpec(hash, "MGF1", new MGF1ParameterSpec(mgfHash),
pSpecified);
} finally {
NativeCrypto.asn1_read_free(seqRef);
NativeCrypto.asn1_read_free(readRef);
}
}
@Override
protected void engineInit(byte[] bytes, String format) throws IOException {
if ((format == null) || format.equals("ASN.1")) {
engineInit(bytes);
} else {
throw new IOException("Unsupported format: " + format);
}
}
// Shared with PSSParameters, since they share some of their encoded form
static String readHash(long seqRef) throws IOException {
if (NativeCrypto.asn1_read_next_tag_is(seqRef, 0)) {
long hashRef = 0;
try {
hashRef = NativeCrypto.asn1_read_tagged(seqRef);
return getHashName(hashRef);
} finally {
NativeCrypto.asn1_read_free(hashRef);
}
}
return "SHA-1";
}
// Shared with PSSParameters, since they share some of their encoded form
static String readMgfHash(long seqRef) throws IOException {
if (NativeCrypto.asn1_read_next_tag_is(seqRef, 1)) {
long mgfRef = 0;
long mgfSeqRef = 0;
try {
mgfRef = NativeCrypto.asn1_read_tagged(seqRef);
mgfSeqRef = NativeCrypto.asn1_read_sequence(mgfRef);
String mgfOid = NativeCrypto.asn1_read_oid(mgfSeqRef);
if (!mgfOid.equals(MGF1_OID)) {
throw new IOException("Error reading ASN.1 encoding");
}
String mgfHash = getHashName(mgfSeqRef);
if (!NativeCrypto.asn1_read_is_empty(mgfSeqRef)) {
throw new IOException("Error reading ASN.1 encoding");
}
return mgfHash;
} finally {
NativeCrypto.asn1_read_free(mgfSeqRef);
NativeCrypto.asn1_read_free(mgfRef);
}
}
return "SHA-1";
}
private static String getHashName(long hashRef) throws IOException {
long hashSeqRef = 0;
try {
hashSeqRef = NativeCrypto.asn1_read_sequence(hashRef);
String hashOid = NativeCrypto.asn1_read_oid(hashSeqRef);
if (!NativeCrypto.asn1_read_is_empty(hashSeqRef)) {
NativeCrypto.asn1_read_null(hashSeqRef);
}
if (!NativeCrypto.asn1_read_is_empty(hashSeqRef)
|| !OID_TO_NAME.containsKey(hashOid)) {
throw new IOException("Error reading ASN.1 encoding");
}
return OID_TO_NAME.get(hashOid);
} finally {
NativeCrypto.asn1_read_free(hashSeqRef);
}
}
@Override
@SuppressWarnings("unchecked")
protected <T extends AlgorithmParameterSpec> T engineGetParameterSpec(Class<T> aClass)
throws InvalidParameterSpecException {
if ((aClass != null) && aClass == OAEPParameterSpec.class) {
return (T) spec;
} else {
throw new InvalidParameterSpecException("Unsupported class: " + aClass);
}
}
@Override
protected byte[] engineGetEncoded() throws IOException {
long cbbRef = 0;
long seqRef = 0;
try {
cbbRef = NativeCrypto.asn1_write_init();
seqRef = NativeCrypto.asn1_write_sequence(cbbRef);
writeHashAndMgfHash(seqRef, spec.getDigestAlgorithm(),
(MGF1ParameterSpec) spec.getMGFParameters());
PSource.PSpecified pSource = (PSource.PSpecified) spec.getPSource();
// Implementations are prohibited from writing the default value for any of the fields
if (pSource.getValue().length != 0) {
long pSourceRef = 0;
long pSourceParamsRef = 0;
try {
pSourceRef = NativeCrypto.asn1_write_tag(seqRef, 2);
pSourceParamsRef = writeAlgorithmIdentifier(pSourceRef, PSPECIFIED_OID);
NativeCrypto.asn1_write_octetstring(pSourceParamsRef, pSource.getValue());
} finally {
NativeCrypto.asn1_write_flush(seqRef);
NativeCrypto.asn1_write_free(pSourceParamsRef);
NativeCrypto.asn1_write_free(pSourceRef);
}
}
return NativeCrypto.asn1_write_finish(cbbRef);
} catch (IOException e) {
NativeCrypto.asn1_write_cleanup(cbbRef);
throw e;
} finally {
NativeCrypto.asn1_write_free(seqRef);
NativeCrypto.asn1_write_free(cbbRef);
}
}
@Override
protected byte[] engineGetEncoded(String format) throws IOException {
if ((format == null) || format.equals("ASN.1")) {
return engineGetEncoded();
}
throw new IOException("Unsupported format: " + format);
}
// Shared with PSSParameters, since they share some of their encoded form
static void writeHashAndMgfHash(long seqRef, String hash, MGF1ParameterSpec mgfSpec) throws IOException {
// Implementations are prohibited from writing the default value for any of the fields
if (!hash.equals("SHA-1")) {
long hashRef = 0;
long hashParamsRef = 0;
try {
hashRef = NativeCrypto.asn1_write_tag(seqRef, 0);
hashParamsRef = writeAlgorithmIdentifier(
hashRef, NAME_TO_OID.get(hash));
NativeCrypto.asn1_write_null(hashParamsRef);
} finally {
NativeCrypto.asn1_write_flush(seqRef);
NativeCrypto.asn1_write_free(hashParamsRef);
NativeCrypto.asn1_write_free(hashRef);
}
}
if (!mgfSpec.getDigestAlgorithm().equals("SHA-1")) {
long mgfRef = 0;
long mgfParamsRef = 0;
long hashParamsRef = 0;
try {
mgfRef = NativeCrypto.asn1_write_tag(seqRef, 1);
mgfParamsRef = writeAlgorithmIdentifier(mgfRef, MGF1_OID);
hashParamsRef = writeAlgorithmIdentifier(
mgfParamsRef, NAME_TO_OID.get(mgfSpec.getDigestAlgorithm()));
NativeCrypto.asn1_write_null(hashParamsRef);
} finally {
NativeCrypto.asn1_write_flush(seqRef);
NativeCrypto.asn1_write_free(hashParamsRef);
NativeCrypto.asn1_write_free(mgfParamsRef);
NativeCrypto.asn1_write_free(mgfRef);
}
}
}
/**
* Writes an ASN.1 AlgorithmIdentifier structure into container, which looks like
* <pre>
* SEQUENCE
* OBJECT IDENTIFIER
* PARAMS (based on the particular algorithm)
* </pre>
* This method returns a reference to the sequence such that the params may be added to it.
* The reference needs to be freed with asn1_write_free once it's used.
*/
private static long writeAlgorithmIdentifier(long container, String oid) throws IOException {
long seqRef = 0;
try {
seqRef = NativeCrypto.asn1_write_sequence(container);
NativeCrypto.asn1_write_oid(seqRef, oid);
} catch (IOException e) {
NativeCrypto.asn1_write_free(seqRef);
throw e;
}
return seqRef;
}
@Override
protected String engineToString() {
return "Conscrypt OAEP AlgorithmParameters";
}
}