1 /*
2  * Copyright 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.conscrypt;
18 
19 import java.io.IOException;
20 import java.security.AlgorithmParametersSpi;
21 import java.security.spec.AlgorithmParameterSpec;
22 import java.security.spec.InvalidParameterSpecException;
23 import java.security.spec.MGF1ParameterSpec;
24 import java.security.spec.PSSParameterSpec;
25 
26 /**
27  * AlgorithmParameters implementation for PSS.  The only supported encoding format is ASN.1
28  * (with X.509 accepted as an alias), as specified in RFC 4055 section 3.1.
29  */
30 @Internal
31 public class PSSParameters extends AlgorithmParametersSpi {
32 
33     private PSSParameterSpec spec = PSSParameterSpec.DEFAULT;
34 
PSSParameters()35     public PSSParameters() {}
36 
37     @Override
engineInit(AlgorithmParameterSpec algorithmParameterSpec)38     protected void engineInit(AlgorithmParameterSpec algorithmParameterSpec)
39             throws InvalidParameterSpecException {
40         if (algorithmParameterSpec instanceof PSSParameterSpec) {
41             this.spec = (PSSParameterSpec) algorithmParameterSpec;
42         } else {
43             throw new InvalidParameterSpecException("Only PSSParameterSpec is supported");
44         }
45     }
46 
47     @Override
engineInit(byte[] bytes)48     protected void engineInit(byte[] bytes) throws IOException {
49         long readRef = 0;
50         long seqRef = 0;
51         try {
52             readRef = NativeCrypto.asn1_read_init(bytes);
53             seqRef = NativeCrypto.asn1_read_sequence(readRef);
54             int saltLength = 20;
55             String hash = OAEPParameters.readHash(seqRef);
56             String mgfHash = OAEPParameters.readMgfHash(seqRef);
57             if (NativeCrypto.asn1_read_next_tag_is(seqRef, 2)) {
58                 long saltRef = 0;
59                 try {
60                     saltRef = NativeCrypto.asn1_read_tagged(seqRef);
61                     saltLength = (int) NativeCrypto.asn1_read_uint64(saltRef);
62                 } finally {
63                     NativeCrypto.asn1_read_free(saltRef);
64                 }
65             }
66             if (NativeCrypto.asn1_read_next_tag_is(seqRef, 3)) {
67                 long trailerField;
68                 long trailerRef = 0;
69                 try {
70                     trailerRef = NativeCrypto.asn1_read_tagged(seqRef);
71                     trailerField = (int) NativeCrypto.asn1_read_uint64(trailerRef);
72                 } finally {
73                     NativeCrypto.asn1_read_free(trailerRef);
74                 }
75                 // 1 is the only legal value for trailerField
76                 if (trailerField != 1) {
77                     throw new IOException("Error reading ASN.1 encoding");
78                 }
79             }
80 
81             if (!NativeCrypto.asn1_read_is_empty(seqRef)
82                     || !NativeCrypto.asn1_read_is_empty(readRef)) {
83                 throw new IOException("Error reading ASN.1 encoding");
84             }
85             this.spec = new PSSParameterSpec(hash, "MGF1", new MGF1ParameterSpec(mgfHash),
86                     saltLength, 1);
87         } finally {
88             NativeCrypto.asn1_read_free(seqRef);
89             NativeCrypto.asn1_read_free(readRef);
90         }
91     }
92 
93     @Override
engineInit(byte[] bytes, String format)94     protected void engineInit(byte[] bytes, String format) throws IOException {
95         if ((format == null) || format.equals("ASN.1") || format.equals("X.509")) {
96             engineInit(bytes);
97         } else {
98             throw new IOException("Unsupported format: " + format);
99         }
100     }
101 
102     @Override
103     @SuppressWarnings("unchecked")
engineGetParameterSpec(Class<T> aClass)104     protected <T extends AlgorithmParameterSpec> T engineGetParameterSpec(Class<T> aClass)
105             throws InvalidParameterSpecException {
106         if ((aClass != null) && aClass == PSSParameterSpec.class) {
107             return (T) spec;
108         } else {
109             throw new InvalidParameterSpecException("Unsupported class: " + aClass);
110         }
111     }
112 
113     @Override
engineGetEncoded()114     protected byte[] engineGetEncoded() throws IOException {
115         long cbbRef = 0;
116         long seqRef = 0;
117         try {
118             cbbRef = NativeCrypto.asn1_write_init();
119             seqRef = NativeCrypto.asn1_write_sequence(cbbRef);
120             OAEPParameters.writeHashAndMgfHash(seqRef, spec.getDigestAlgorithm(),
121                     (MGF1ParameterSpec) spec.getMGFParameters());
122             // Implementations are prohibited from writing the default value for any of the fields
123             if (spec.getSaltLength() != 20) {
124                 long tagRef = 0;
125                 try {
126                     tagRef = NativeCrypto.asn1_write_tag(seqRef, 2);
127                     NativeCrypto.asn1_write_uint64(tagRef, spec.getSaltLength());
128                 } finally {
129                     NativeCrypto.asn1_write_flush(seqRef);
130                     NativeCrypto.asn1_write_free(tagRef);
131                 }
132             }
133             // 1 is the only legal value for trailerField and the default, so ignore it
134             return NativeCrypto.asn1_write_finish(cbbRef);
135         } catch (IOException e) {
136             NativeCrypto.asn1_write_cleanup(cbbRef);
137             throw e;
138         } finally {
139             NativeCrypto.asn1_write_free(seqRef);
140             NativeCrypto.asn1_write_free(cbbRef);
141         }
142     }
143 
144     @Override
engineGetEncoded(String format)145     protected byte[] engineGetEncoded(String format) throws IOException {
146         if ((format == null) || format.equals("ASN.1") || format.equals("X.509")) {
147             return engineGetEncoded();
148         }
149         throw new IOException("Unsupported format: " + format);
150     }
151 
152     @Override
engineToString()153     protected String engineToString() {
154         return "Conscrypt PSS AlgorithmParameters";
155     }
156 }
157