1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 #include "pal_signverify.h"
6 
7 static int32_t ExecuteSignTransform(SecTransformRef signer, CFDataRef* pSignatureOut, CFErrorRef* pErrorOut);
8 static int32_t ExecuteVerifyTransform(SecTransformRef verifier, CFErrorRef* pErrorOut);
9 
10 static int32_t ConfigureSignVerifyTransform(
11     SecTransformRef xform, CFDataRef cfDataHash, PAL_HashAlgorithm, bool useDigestAlgorithm, CFErrorRef* pErrorOut);
12 
GenerateSignature(SecKeyRef privateKey,uint8_t * pbDataHash,int32_t cbDataHash,PAL_HashAlgorithm hashAlgorithm,bool useHashAlgorithm,CFDataRef * pSignatureOut,CFErrorRef * pErrorOut)13 static int32_t GenerateSignature(SecKeyRef privateKey,
14                                  uint8_t* pbDataHash,
15                                  int32_t cbDataHash,
16                                  PAL_HashAlgorithm hashAlgorithm,
17                                  bool useHashAlgorithm,
18                                  CFDataRef* pSignatureOut,
19                                  CFErrorRef* pErrorOut)
20 {
21     if (pSignatureOut != nullptr)
22         *pSignatureOut = nullptr;
23     if (pErrorOut != nullptr)
24         *pErrorOut = nullptr;
25 
26     if (privateKey == nullptr || pbDataHash == nullptr || cbDataHash < 0 || pSignatureOut == nullptr ||
27         pErrorOut == nullptr)
28     {
29         return kErrorBadInput;
30     }
31 
32     CFDataRef dataHash = CFDataCreateWithBytesNoCopy(nullptr, pbDataHash, cbDataHash, kCFAllocatorNull);
33 
34     if (dataHash == nullptr)
35     {
36         return kErrorUnknownState;
37     }
38 
39     int32_t ret = kErrorSeeError;
40     SecTransformRef signer = SecSignTransformCreate(privateKey, pErrorOut);
41 
42     if (signer != nullptr)
43     {
44         if (*pErrorOut == nullptr)
45         {
46             if (ConfigureSignVerifyTransform(signer, dataHash, hashAlgorithm, useHashAlgorithm, pErrorOut))
47             {
48                 ret = ExecuteSignTransform(signer, pSignatureOut, pErrorOut);
49             }
50         }
51 
52         CFRelease(signer);
53     }
54 
55     CFRelease(dataHash);
56     return ret;
57 }
58 
AppleCryptoNative_GenerateSignature(SecKeyRef privateKey,uint8_t * pbDataHash,int32_t cbDataHash,CFDataRef * pSignatureOut,CFErrorRef * pErrorOut)59 extern "C" int32_t AppleCryptoNative_GenerateSignature(
60     SecKeyRef privateKey, uint8_t* pbDataHash, int32_t cbDataHash, CFDataRef* pSignatureOut, CFErrorRef* pErrorOut)
61 {
62     return GenerateSignature(privateKey, pbDataHash, cbDataHash, PAL_Unknown, false, pSignatureOut, pErrorOut);
63 }
64 
AppleCryptoNative_GenerateSignatureWithHashAlgorithm(SecKeyRef privateKey,uint8_t * pbDataHash,int32_t cbDataHash,PAL_HashAlgorithm hashAlgorithm,CFDataRef * pSignatureOut,CFErrorRef * pErrorOut)65 extern "C" int32_t AppleCryptoNative_GenerateSignatureWithHashAlgorithm(SecKeyRef privateKey,
66                                                                         uint8_t* pbDataHash,
67                                                                         int32_t cbDataHash,
68                                                                         PAL_HashAlgorithm hashAlgorithm,
69                                                                         CFDataRef* pSignatureOut,
70                                                                         CFErrorRef* pErrorOut)
71 {
72     return GenerateSignature(privateKey, pbDataHash, cbDataHash, hashAlgorithm, true, pSignatureOut, pErrorOut);
73 }
74 
VerifySignature(SecKeyRef publicKey,uint8_t * pbDataHash,int32_t cbDataHash,uint8_t * pbSignature,int32_t cbSignature,PAL_HashAlgorithm hashAlgorithm,bool useHashAlgorithm,CFErrorRef * pErrorOut)75 static int32_t VerifySignature(SecKeyRef publicKey,
76                                uint8_t* pbDataHash,
77                                int32_t cbDataHash,
78                                uint8_t* pbSignature,
79                                int32_t cbSignature,
80                                PAL_HashAlgorithm hashAlgorithm,
81                                bool useHashAlgorithm,
82                                CFErrorRef* pErrorOut)
83 {
84     if (pErrorOut != nullptr)
85         *pErrorOut = nullptr;
86 
87     if (publicKey == nullptr || pbDataHash == nullptr || cbDataHash < 0 || pbSignature == nullptr || cbSignature < 0 ||
88         pErrorOut == nullptr)
89         return kErrorBadInput;
90 
91     CFDataRef dataHash = CFDataCreateWithBytesNoCopy(nullptr, pbDataHash, cbDataHash, kCFAllocatorNull);
92 
93     if (dataHash == nullptr)
94     {
95         return kErrorUnknownState;
96     }
97 
98     CFDataRef signature = CFDataCreateWithBytesNoCopy(nullptr, pbSignature, cbSignature, kCFAllocatorNull);
99 
100     if (signature == nullptr)
101     {
102         CFRelease(dataHash);
103         return kErrorUnknownState;
104     }
105 
106     int32_t ret = kErrorSeeError;
107     SecTransformRef verifier = SecVerifyTransformCreate(publicKey, signature, pErrorOut);
108 
109     if (verifier != nullptr)
110     {
111         if (*pErrorOut == nullptr)
112         {
113             if (ConfigureSignVerifyTransform(verifier, dataHash, hashAlgorithm, useHashAlgorithm, pErrorOut))
114             {
115                 ret = ExecuteVerifyTransform(verifier, pErrorOut);
116             }
117         }
118 
119         CFRelease(verifier);
120     }
121 
122     CFRelease(dataHash);
123     CFRelease(signature);
124 
125     return ret;
126 }
127 
AppleCryptoNative_VerifySignatureWithHashAlgorithm(SecKeyRef publicKey,uint8_t * pbDataHash,int32_t cbDataHash,uint8_t * pbSignature,int32_t cbSignature,PAL_HashAlgorithm hashAlgorithm,CFErrorRef * pErrorOut)128 extern "C" int32_t AppleCryptoNative_VerifySignatureWithHashAlgorithm(SecKeyRef publicKey,
129                                                                       uint8_t* pbDataHash,
130                                                                       int32_t cbDataHash,
131                                                                       uint8_t* pbSignature,
132                                                                       int32_t cbSignature,
133                                                                       PAL_HashAlgorithm hashAlgorithm,
134                                                                       CFErrorRef* pErrorOut)
135 {
136     return VerifySignature(publicKey, pbDataHash, cbDataHash, pbSignature, cbSignature, hashAlgorithm, true, pErrorOut);
137 }
138 
AppleCryptoNative_VerifySignature(SecKeyRef publicKey,uint8_t * pbDataHash,int32_t cbDataHash,uint8_t * pbSignature,int32_t cbSignature,CFErrorRef * pErrorOut)139 extern "C" int32_t AppleCryptoNative_VerifySignature(SecKeyRef publicKey,
140                                                      uint8_t* pbDataHash,
141                                                      int32_t cbDataHash,
142                                                      uint8_t* pbSignature,
143                                                      int32_t cbSignature,
144                                                      CFErrorRef* pErrorOut)
145 {
146     return VerifySignature(publicKey, pbDataHash, cbDataHash, pbSignature, cbSignature, PAL_Unknown, false, pErrorOut);
147 }
148 
ExecuteSignTransform(SecTransformRef signer,CFDataRef * pSignatureOut,CFErrorRef * pErrorOut)149 static int32_t ExecuteSignTransform(SecTransformRef signer, CFDataRef* pSignatureOut, CFErrorRef* pErrorOut)
150 {
151     assert(signer != nullptr);
152     assert(pSignatureOut != nullptr);
153     assert(pErrorOut != nullptr);
154 
155     int32_t ret = INT_MIN;
156     CFTypeRef signerResponse = SecTransformExecute(signer, pErrorOut);
157     CFDataRef signature = nullptr;
158 
159     if (signerResponse == nullptr || *pErrorOut != nullptr)
160     {
161         ret = kErrorSeeError;
162         goto cleanup;
163     }
164 
165     if (CFGetTypeID(signerResponse) != CFDataGetTypeID())
166     {
167         ret = kErrorUnknownState;
168         goto cleanup;
169     }
170 
171     signature = reinterpret_cast<CFDataRef>(const_cast<void*>(signerResponse));
172 
173     if (CFDataGetLength(signature) > 0)
174     {
175         // We're going to call CFRelease in cleanup, so this keeps it alive
176         // to be interpreted by the managed code.
177         CFRetain(signature);
178         *pSignatureOut = signature;
179         ret = 1;
180     }
181     else
182     {
183         ret = kErrorUnknownState;
184         *pSignatureOut = nullptr;
185     }
186 
187 cleanup:
188     if (signerResponse != nullptr)
189     {
190         CFRelease(signerResponse);
191     }
192 
193     return ret;
194 }
195 
ExecuteVerifyTransform(SecTransformRef verifier,CFErrorRef * pErrorOut)196 static int32_t ExecuteVerifyTransform(SecTransformRef verifier, CFErrorRef* pErrorOut)
197 {
198     assert(verifier != nullptr);
199     assert(pErrorOut != nullptr);
200 
201     int32_t ret = kErrorSeeError;
202     CFTypeRef verifierResponse = SecTransformExecute(verifier, pErrorOut);
203 
204     if (verifierResponse != nullptr)
205     {
206         if (*pErrorOut == nullptr)
207         {
208             ret = (verifierResponse == kCFBooleanTrue);
209         }
210 
211         CFRelease(verifierResponse);
212     }
213 
214     return ret;
215 }
216 
ConfigureSignVerifyTransform(SecTransformRef xform,CFDataRef cfDataHash,PAL_HashAlgorithm hashAlgorithm,bool includeHashAlgorithm,CFErrorRef * pErrorOut)217 static int32_t ConfigureSignVerifyTransform(SecTransformRef xform,
218                                             CFDataRef cfDataHash,
219                                             PAL_HashAlgorithm hashAlgorithm,
220                                             bool includeHashAlgorithm,
221                                             CFErrorRef* pErrorOut)
222 {
223     if (!SecTransformSetAttribute(xform, kSecInputIsAttributeName, kSecInputIsDigest, pErrorOut))
224     {
225         return 0;
226     }
227 
228     if (!SecTransformSetAttribute(xform, kSecTransformInputAttributeName, cfDataHash, pErrorOut))
229     {
230         return 0;
231     }
232 
233     if (includeHashAlgorithm)
234     {
235         CFStringRef cfHashName = nullptr;
236         int32_t hashSize = 0;
237 
238         switch (hashAlgorithm)
239         {
240             case PAL_MD5:
241                 cfHashName = kSecDigestMD5;
242                 break;
243             case PAL_SHA1:
244                 cfHashName = kSecDigestSHA1;
245                 break;
246             case PAL_SHA256:
247                 cfHashName = kSecDigestSHA2;
248                 hashSize = 256;
249                 break;
250             case PAL_SHA384:
251                 cfHashName = kSecDigestSHA2;
252                 hashSize = 384;
253                 break;
254             case PAL_SHA512:
255                 cfHashName = kSecDigestSHA2;
256                 hashSize = 512;
257                 break;
258             default:
259                 return kErrorUnknownAlgorithm;
260         }
261 
262         if (!SecTransformSetAttribute(xform, kSecDigestTypeAttribute, cfHashName, pErrorOut))
263         {
264             return 0;
265         }
266 
267         if (hashSize != 0)
268         {
269             CFNumberRef cfHashSize = CFNumberCreate(nullptr, kCFNumberIntType, &hashSize);
270 
271             if (cfHashSize == nullptr)
272             {
273                 return 0;
274             }
275 
276             if (!SecTransformSetAttribute(xform, kSecDigestLengthAttribute, cfHashSize, pErrorOut))
277             {
278                 CFRelease(cfHashSize);
279                 return 0;
280             }
281 
282             CFRelease(cfHashSize);
283         }
284     }
285 
286     return 1;
287 }
288