1/*
2 * Copyright (C) 2014 Igalia S.L. All rights reserved.
3 * Copyright (C) 2016 Yusuke Suzuki <utatane.tea@gmail.com>.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24 * THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "CryptoAlgorithmRSASSA_PKCS1_v1_5.h"
29
30#if ENABLE(WEB_CRYPTO)
31
32#include "CryptoKeyRSA.h"
33#include "GCryptUtilities.h"
34#include "NotImplemented.h"
35
36namespace WebCore {
37
38static Optional<Vector<uint8_t>> gcryptSign(gcry_sexp_t keySexp, const Vector<uint8_t>& data, CryptoAlgorithmIdentifier hashAlgorithmIdentifier, size_t keySizeInBytes)
39{
40 // Perform digest operation with the specified algorithm on the given data.
41 Vector<uint8_t> dataHash;
42 {
43 auto digestAlgorithm = hashCryptoDigestAlgorithm(hashAlgorithmIdentifier);
44 if (!digestAlgorithm)
45 return WTF::nullopt;
46
47 auto digest = PAL::CryptoDigest::create(*digestAlgorithm);
48 if (!digest)
49 return WTF::nullopt;
50
51 digest->addBytes(data.data(), data.size());
52 dataHash = digest->computeHash();
53 }
54
55 // Construct the data s-expression that contains PKCS#1-padded hashed data.
56 PAL::GCrypt::Handle<gcry_sexp_t> dataSexp;
57 {
58 auto shaAlgorithm = hashAlgorithmName(hashAlgorithmIdentifier);
59 if (!shaAlgorithm)
60 return WTF::nullopt;
61
62 gcry_error_t error = gcry_sexp_build(&dataSexp, nullptr, "(data(flags pkcs1)(hash %s %b))",
63 *shaAlgorithm, dataHash.size(), dataHash.data());
64 if (error != GPG_ERR_NO_ERROR) {
65 PAL::GCrypt::logError(error);
66 return WTF::nullopt;
67 }
68 }
69
70 // Perform the PK signing, retrieving a sig-val s-expression of the following form:
71 // (sig-val
72 // (rsa
73 // (s s-mpi)))
74 PAL::GCrypt::Handle<gcry_sexp_t> signatureSexp;
75 gcry_error_t error = gcry_pk_sign(&signatureSexp, dataSexp, keySexp);
76 if (error != GPG_ERR_NO_ERROR) {
77 PAL::GCrypt::logError(error);
78 return WTF::nullopt;
79 }
80
81 // Return MPI data of the embedded s integer.
82 PAL::GCrypt::Handle<gcry_sexp_t> sSexp(gcry_sexp_find_token(signatureSexp, "s", 0));
83 if (!sSexp)
84 return WTF::nullopt;
85
86 return mpiZeroPrefixedData(sSexp, keySizeInBytes);
87}
88
89static Optional<bool> gcryptVerify(gcry_sexp_t keySexp, const Vector<uint8_t>& signature, const Vector<uint8_t>& data, CryptoAlgorithmIdentifier hashAlgorithmIdentifier)
90{
91 // Perform digest operation with the specified algorithm on the given data.
92 Vector<uint8_t> dataHash;
93 {
94 auto digestAlgorithm = hashCryptoDigestAlgorithm(hashAlgorithmIdentifier);
95 if (!digestAlgorithm)
96 return WTF::nullopt;
97
98 auto digest = PAL::CryptoDigest::create(*digestAlgorithm);
99 if (!digest)
100 return WTF::nullopt;
101
102 digest->addBytes(data.data(), data.size());
103 dataHash = digest->computeHash();
104 }
105
106 // Construct the sig-val s-expression that contains the signature data.
107 PAL::GCrypt::Handle<gcry_sexp_t> signatureSexp;
108 gcry_error_t error = gcry_sexp_build(&signatureSexp, nullptr, "(sig-val(rsa(s %b)))",
109 signature.size(), signature.data());
110 if (error != GPG_ERR_NO_ERROR) {
111 PAL::GCrypt::logError(error);
112 return WTF::nullopt;
113 }
114
115 // Construct the data s-expression that contains PKCS#1-padded hashed data.
116 PAL::GCrypt::Handle<gcry_sexp_t> dataSexp;
117 {
118 auto shaAlgorithm = hashAlgorithmName(hashAlgorithmIdentifier);
119 if (!shaAlgorithm)
120 return WTF::nullopt;
121
122 error = gcry_sexp_build(&dataSexp, nullptr, "(data(flags pkcs1)(hash %s %b))",
123 *shaAlgorithm, dataHash.size(), dataHash.data());
124 if (error != GPG_ERR_NO_ERROR) {
125 PAL::GCrypt::logError(error);
126 return WTF::nullopt;
127 }
128 }
129
130 // Perform the PK verification. We report success if there's no error returned, or
131 // a failure in any other case. OperationError should not be returned at this point,
132 // avoiding spilling information about the exact cause of verification failure.
133 error = gcry_pk_verify(signatureSexp, dataSexp, keySexp);
134 return { error == GPG_ERR_NO_ERROR };
135}
136
137ExceptionOr<Vector<uint8_t>> CryptoAlgorithmRSASSA_PKCS1_v1_5::platformSign(const CryptoKeyRSA& key, const Vector<uint8_t>& data)
138{
139 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!(key.keySizeInBits() % 8));
140 auto output = gcryptSign(key.platformKey(), data, key.hashAlgorithmIdentifier(), key.keySizeInBits() / 8);
141 if (!output)
142 return Exception { OperationError };
143 return WTFMove(*output);
144}
145
146ExceptionOr<bool> CryptoAlgorithmRSASSA_PKCS1_v1_5::platformVerify(const CryptoKeyRSA& key, const Vector<uint8_t>& signature, const Vector<uint8_t>& data)
147{
148 auto output = gcryptVerify(key.platformKey(), signature, data, key.hashAlgorithmIdentifier());
149 if (!output)
150 return Exception { OperationError };
151 return *output;
152}
153
154} // namespace WebCore
155
156#endif // ENABLE(WEB_CRYPTO)
157