]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2006-2013 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | // | |
25 | // signerutils - utilities for signature generation | |
26 | // | |
90dc47c2 A |
27 | #include "csutilities.h" |
28 | #include "drmaker.h" | |
29 | #include "resources.h" | |
b1ab9ed8 A |
30 | #include "signerutils.h" |
31 | #include "signer.h" | |
90dc47c2 A |
32 | |
33 | #include <Security/SecCmsBase.h> | |
b1ab9ed8 A |
34 | #include <Security/SecIdentity.h> |
35 | #include <Security/CMSEncoder.h> | |
90dc47c2 A |
36 | |
37 | #include "SecCodeSigner.h" | |
38 | ||
b1ab9ed8 | 39 | #include <security_utilities/unix++.h> |
fa7225c8 | 40 | #include <security_utilities/logging.h> |
b1ab9ed8 | 41 | #include <security_utilities/unixchild.h> |
90dc47c2 | 42 | |
b1ab9ed8 A |
43 | #include <vector> |
44 | ||
45 | // for helper validation | |
46 | #include "Code.h" | |
47 | #include <security_utilities/cfmunge.h> | |
48 | #include <sys/codesign.h> | |
49 | ||
50 | ||
51 | namespace Security { | |
52 | namespace CodeSigning { | |
53 | ||
54 | ||
55 | // | |
56 | // About the Mach-O allocation helper | |
57 | // | |
58 | static const char helperName[] = "codesign_allocate"; | |
59 | static const char helperPath[] = "/usr/bin/codesign_allocate"; | |
60 | static const char helperOverride[] = "CODESIGN_ALLOCATE"; | |
61 | static const size_t csAlign = 16; | |
62 | ||
63 | ||
64 | // | |
65 | // BlobWriters | |
66 | // | |
67 | void BlobWriter::component(CodeDirectory::SpecialSlot slot, CFDataRef data) | |
68 | { | |
69 | return EmbeddedSignatureBlob::Maker::component(slot, data); | |
70 | } | |
71 | ||
72 | ||
73 | void DetachedBlobWriter::flush() | |
74 | { | |
75 | EmbeddedSignatureBlob *blob = this->make(); | |
427c49bc | 76 | signer.code->detachedSignature(CFTempData(*blob)); |
b1ab9ed8 A |
77 | signer.state.returnDetachedSignature(blob, signer); |
78 | ::free(blob); | |
79 | } | |
80 | ||
81 | ||
82 | // | |
83 | // ArchEditor | |
84 | // | |
e3d460c9 | 85 | ArchEditor::ArchEditor(Universal &code, CodeDirectory::HashAlgorithms hashTypes, uint32_t attrs) |
b1ab9ed8 A |
86 | : DiskRep::Writer(attrs) |
87 | { | |
88 | Universal::Architectures archList; | |
89 | code.architectures(archList); | |
90 | for (Universal::Architectures::const_iterator it = archList.begin(); | |
91 | it != archList.end(); ++it) | |
e3d460c9 | 92 | architecture[*it] = new Arch(*it, hashTypes); |
b1ab9ed8 A |
93 | } |
94 | ||
95 | ||
96 | ArchEditor::~ArchEditor() | |
97 | { | |
98 | for (ArchMap::iterator it = begin(); it != end(); ++it) | |
99 | delete it->second; | |
100 | } | |
e3d460c9 A |
101 | |
102 | ||
103 | ArchEditor::Arch::Arch(const Architecture &arch, CodeDirectory::HashAlgorithms hashTypes) | |
104 | : architecture(arch) | |
105 | { | |
79b9da22 | 106 | blobSize = 0; |
e3d460c9 A |
107 | for (auto type = hashTypes.begin(); type != hashTypes.end(); ++type) |
108 | cdBuilders.insert(make_pair(*type, new CodeDirectory::Builder(*type))); | |
109 | } | |
b1ab9ed8 A |
110 | |
111 | ||
112 | // | |
113 | // BlobEditor | |
114 | // | |
115 | BlobEditor::BlobEditor(Universal &fat, SecCodeSigner::Signer &s) | |
e3d460c9 | 116 | : ArchEditor(fat, s.digestAlgorithms(), 0), signer(s) |
b1ab9ed8 A |
117 | { } |
118 | ||
119 | ||
120 | void BlobEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data) | |
121 | { | |
122 | mGlobal.component(slot, data); | |
123 | } | |
124 | ||
125 | void BlobEditor::write(Arch &arch, EmbeddedSignatureBlob *blob) | |
126 | { | |
127 | mMaker.add(arch.architecture.cpuType(), blob); | |
128 | } | |
129 | ||
130 | ||
131 | void BlobEditor::commit() | |
132 | { | |
133 | // create the architecture-global blob and store it into the superblob | |
134 | mMaker.add(0, mGlobal.make()); // takes ownership of blob | |
135 | ||
136 | // finish up the superblob and deliver it | |
137 | DetachedSignatureBlob *blob = mMaker.make(); | |
138 | signer.state.returnDetachedSignature(blob, signer); | |
139 | ::free(blob); | |
140 | } | |
141 | ||
142 | ||
143 | // | |
144 | // MachOEditor's allocate() method spawns the codesign_allocate helper tool to | |
145 | // "drill up" the Mach-O binary for insertion of Code Signing signature data. | |
146 | // After the tool succeeds, we open the new file and are ready to write it. | |
147 | // | |
e3d460c9 A |
148 | MachOEditor::MachOEditor(DiskRep::Writer *w, Universal &code, CodeDirectory::HashAlgorithms hashTypes, std::string srcPath) |
149 | : ArchEditor(code, hashTypes, w->attributes()), | |
b1ab9ed8 A |
150 | writer(w), |
151 | sourcePath(srcPath), | |
152 | tempPath(srcPath + ".cstemp"), | |
e3d460c9 | 153 | mHashTypes(hashTypes), |
b1ab9ed8 A |
154 | mNewCode(NULL), |
155 | mTempMayExist(false) | |
156 | { | |
157 | if (const char *path = getenv(helperOverride)) { | |
158 | mHelperPath = path; | |
159 | mHelperOverridden = true; | |
160 | } else { | |
161 | mHelperPath = helperPath; | |
162 | mHelperOverridden = false; | |
163 | } | |
164 | } | |
165 | ||
166 | MachOEditor::~MachOEditor() | |
167 | { | |
168 | delete mNewCode; | |
169 | if (mTempMayExist) | |
170 | ::remove(tempPath.c_str()); // ignore error (can't do anything about it) | |
79b9da22 | 171 | |
b1ab9ed8 A |
172 | this->kill(); |
173 | } | |
174 | ||
175 | ||
176 | void MachOEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data) | |
177 | { | |
178 | writer->component(slot, data); | |
179 | } | |
180 | ||
181 | ||
182 | void MachOEditor::allocate() | |
183 | { | |
184 | // note that we may have a temporary file from now on (for cleanup in the error case) | |
185 | mTempMayExist = true; | |
186 | ||
187 | // run codesign_allocate to make room in the executable file | |
188 | fork(); | |
189 | wait(); | |
190 | if (!Child::succeeded()) | |
427c49bc | 191 | MacOSError::throwMe(errSecCSHelperFailed); |
b1ab9ed8 A |
192 | |
193 | // open the new (temporary) Universal file | |
194 | { | |
195 | UidGuard guard(0); | |
196 | mFd.open(tempPath, O_RDWR); | |
197 | } | |
198 | mNewCode = new Universal(mFd); | |
199 | } | |
200 | ||
201 | static const unsigned char appleReq[] = { | |
202 | // anchor apple and info["Application-Group"] = "com.apple.tool.codesign_allocate" | |
203 | 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, | |
204 | 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x11, 0x41, 0x70, 0x70, 0x6c, | |
205 | 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x00, 0x00, 0x00, | |
206 | 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, | |
207 | 0x65, 0x2e, 0x74, 0x6f, 0x6f, 0x6c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x5f, | |
208 | 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, | |
209 | }; | |
210 | ||
211 | void MachOEditor::parentAction() | |
212 | { | |
213 | if (mHelperOverridden) { | |
214 | CODESIGN_ALLOCATE_VALIDATE((char*)mHelperPath, this->pid()); | |
215 | // check code identity of an overridden allocation helper | |
216 | SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(mHelperPath)); | |
427c49bc | 217 | code->staticValidate(kSecCSDefaultFlags, NULL); |
b1ab9ed8 A |
218 | code->validateRequirement((const Requirement *)appleReq, errSecCSReqFailed); |
219 | } | |
220 | } | |
221 | ||
222 | void MachOEditor::childAction() | |
223 | { | |
224 | vector<const char *> arguments; | |
225 | arguments.push_back(helperName); | |
226 | arguments.push_back("-i"); | |
227 | arguments.push_back(sourcePath.c_str()); | |
228 | arguments.push_back("-o"); | |
229 | arguments.push_back(tempPath.c_str()); | |
230 | ||
231 | for (Iterator it = architecture.begin(); it != architecture.end(); ++it) { | |
232 | size_t size = LowLevelMemoryUtilities::alignUp(it->second->blobSize, csAlign); | |
233 | char *ssize; // we'll leak this (execv is coming soon) | |
234 | asprintf(&ssize, "%zd", size); | |
235 | ||
236 | if (const char *arch = it->first.name()) { | |
427c49bc | 237 | CODESIGN_ALLOCATE_ARCH((char*)arch, (unsigned int)size); |
b1ab9ed8 A |
238 | arguments.push_back("-a"); |
239 | arguments.push_back(arch); | |
240 | } else { | |
427c49bc | 241 | CODESIGN_ALLOCATE_ARCHN(it->first.cpuType(), it->first.cpuSubtype(), (unsigned int)size); |
b1ab9ed8 A |
242 | arguments.push_back("-A"); |
243 | char *anum; | |
244 | asprintf(&anum, "%d", it->first.cpuType()); | |
245 | arguments.push_back(anum); | |
246 | asprintf(&anum, "%d", it->first.cpuSubtype()); | |
247 | arguments.push_back(anum); | |
248 | } | |
249 | arguments.push_back(ssize); | |
250 | } | |
251 | arguments.push_back(NULL); | |
252 | ||
253 | if (mHelperOverridden) | |
5c19dc3a | 254 | ::csops(0, CS_OPS_MARKKILL, NULL, 0); // force code integrity |
fa7225c8 | 255 | (void)::seteuid(0); // activate privilege if caller has it; ignore error if not |
b1ab9ed8 A |
256 | execv(mHelperPath, (char * const *)&arguments[0]); |
257 | } | |
258 | ||
259 | void MachOEditor::reset(Arch &arch) | |
260 | { | |
261 | arch.source.reset(mNewCode->architecture(arch.architecture)); | |
e3d460c9 A |
262 | |
263 | for (auto type = mHashTypes.begin(); type != mHashTypes.end(); ++type) { | |
264 | arch.eachDigest(^(CodeDirectory::Builder& builder) { | |
dbe77505 A |
265 | /* Signature editing may have no need for cd builders, and not |
266 | * have opened them, so only reopen them conditionally. */ | |
267 | if (builder.opened()) { | |
268 | builder.reopen(tempPath, arch.source->offset(), arch.source->signingOffset()); | |
269 | } | |
e3d460c9 A |
270 | }); |
271 | } | |
b1ab9ed8 A |
272 | } |
273 | ||
274 | ||
275 | // | |
276 | // MachOEditor's write() method actually writes the blob into the CODESIGNING section | |
277 | // of the executable image file. | |
278 | // | |
279 | void MachOEditor::write(Arch &arch, EmbeddedSignatureBlob *blob) | |
280 | { | |
281 | if (size_t offset = arch.source->signingOffset()) { | |
282 | size_t signingLength = arch.source->signingLength(); | |
427c49bc | 283 | CODESIGN_ALLOCATE_WRITE((char*)arch.architecture.name(), offset, (unsigned)blob->length(), (unsigned)signingLength); |
b1ab9ed8 A |
284 | if (signingLength < blob->length()) |
285 | MacOSError::throwMe(errSecCSCMSTooLarge); | |
286 | arch.source->seek(offset); | |
287 | arch.source->writeAll(*blob); | |
288 | ::free(blob); // done with it | |
289 | } else { | |
fa7225c8 | 290 | secinfo("signer", "%p cannot find CODESIGNING data in Mach-O", this); |
b1ab9ed8 A |
291 | MacOSError::throwMe(errSecCSInternalError); |
292 | } | |
293 | } | |
294 | ||
295 | ||
296 | // | |
297 | // Commit the edit. | |
298 | // This moves the temporary editor copy over the source image file. | |
299 | // Note that the Universal object returned by allocate() is still open | |
300 | // and valid; the caller owns it. | |
301 | // | |
302 | void MachOEditor::commit() | |
303 | { | |
304 | // if the file's owned by someone else *and* we can become root... | |
305 | struct stat st; | |
306 | UnixError::check(::stat(sourcePath.c_str(), &st)); | |
307 | ||
308 | // copy over all the *other* stuff | |
309 | Copyfile copy; | |
310 | int fd = mFd; | |
311 | copy.set(COPYFILE_STATE_DST_FD, &fd); | |
312 | { | |
313 | // perform copy under root or file-owner privileges if available | |
314 | UidGuard guard; | |
315 | if (!guard.seteuid(0)) | |
fa7225c8 | 316 | (void)guard.seteuid(st.st_uid); |
b1ab9ed8 A |
317 | |
318 | // copy metadata from original file... | |
319 | copy(sourcePath.c_str(), NULL, COPYFILE_SECURITY | COPYFILE_METADATA); | |
79b9da22 A |
320 | |
321 | #if TARGET_OS_OSX | |
322 | // determine AFSC status if we are told to preserve compression | |
323 | bool conductCompression = false; | |
324 | cmpInfo cInfo; | |
325 | if (writer->getPreserveAFSC()) { | |
326 | if (queryCompressionInfo(sourcePath.c_str(), &cInfo) == 0) { | |
327 | if (cInfo.compressionType != 0 && cInfo.compressedSize > 0) | |
328 | conductCompression = true; | |
329 | } | |
330 | } | |
331 | #endif | |
332 | ||
b1ab9ed8 A |
333 | // ... but explicitly update the timestamps since we did change the file |
334 | char buf; | |
335 | mFd.read(&buf, sizeof(buf), 0); | |
336 | mFd.write(&buf, sizeof(buf), 0); | |
337 | ||
338 | // move the new file into place | |
339 | UnixError::check(::rename(tempPath.c_str(), sourcePath.c_str())); | |
340 | mTempMayExist = false; // we renamed it away | |
79b9da22 A |
341 | |
342 | #if TARGET_OS_OSX | |
343 | // if the original file was compressed, compress the new file after move | |
344 | if (conductCompression) { | |
345 | CFMutableDictionaryRef options = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
346 | CFStringRef val = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), cInfo.compressionType); | |
347 | CFDictionarySetValue(options, kAFSCCompressionTypes, val); | |
348 | CFRelease(val); | |
349 | ||
350 | CompressionQueueContext compressionQueue = CreateCompressionQueue(NULL, NULL, NULL, NULL, options); | |
351 | ||
352 | if (!CompressFile(compressionQueue, sourcePath.c_str(), NULL)) { | |
353 | secinfo("signer", "%p Failed to queue compression of file %s", this, sourcePath.c_str()); | |
354 | MacOSError::throwMe(errSecCSInternalError); | |
355 | } | |
356 | FinishCompressionAndCleanUp(compressionQueue); | |
357 | ||
358 | compressionQueue = NULL; | |
359 | CFRelease(options); | |
360 | } | |
361 | #endif | |
362 | ||
b1ab9ed8 | 363 | } |
fa7225c8 | 364 | this->writer->flush(); |
b1ab9ed8 A |
365 | } |
366 | ||
367 | ||
368 | // | |
369 | // InternalRequirements | |
370 | // | |
371 | void InternalRequirements::operator () (const Requirements *given, const Requirements *defaulted, const Requirement::Context &context) | |
372 | { | |
373 | // first add the default internal requirements | |
374 | if (defaulted) { | |
375 | this->add(defaulted); | |
376 | ::free((void *)defaulted); // was malloc(3)ed by DiskRep | |
377 | } | |
378 | ||
379 | // now override them with any requirements explicitly given by the signer | |
380 | if (given) | |
381 | this->add(given); | |
382 | ||
383 | // now add the Designated Requirement, if we can make it and it's not been provided | |
384 | if (!this->contains(kSecDesignatedRequirementType)) { | |
385 | DRMaker maker(context); | |
386 | if (Requirement *dr = maker.make()) { | |
387 | this->add(kSecDesignatedRequirementType, dr); // takes ownership of dr | |
388 | } | |
389 | } | |
390 | ||
391 | // return the result | |
392 | mReqs = this->make(); | |
393 | } | |
394 | ||
395 | ||
396 | // | |
397 | // Pre-Signing contexts | |
398 | // | |
399 | PreSigningContext::PreSigningContext(const SecCodeSigner::Signer &signer) | |
400 | { | |
401 | // construct a cert chain | |
402 | if (signer.signingIdentity() != SecIdentityRef(kCFNull)) { | |
403 | CFRef<SecCertificateRef> signingCert; | |
404 | MacOSError::check(SecIdentityCopyCertificate(signer.signingIdentity(), &signingCert.aref())); | |
405 | CFRef<SecPolicyRef> policy = SecPolicyCreateWithOID(kSecPolicyAppleCodeSigning); | |
406 | CFRef<SecTrustRef> trust; | |
407 | MacOSError::check(SecTrustCreateWithCertificates(CFArrayRef(signingCert.get()), policy, &trust.aref())); | |
408 | SecTrustResultType result; | |
409 | MacOSError::check(SecTrustEvaluate(trust, &result)); | |
410 | CSSM_TP_APPLE_EVIDENCE_INFO *info; | |
411 | MacOSError::check(SecTrustGetResult(trust, &result, &mCerts.aref(), &info)); | |
412 | this->certs = mCerts; | |
413 | } | |
414 | ||
415 | // other stuff | |
416 | this->identifier = signer.signingIdentifier(); | |
417 | } | |
e3d460c9 A |
418 | |
419 | ||
420 | // | |
421 | // A collector of CodeDirectories for hash-agile construction of signatures. | |
422 | // | |
423 | CodeDirectorySet::~CodeDirectorySet() | |
424 | { | |
425 | for (auto it = begin(); it != end(); ++it) | |
426 | ::free(const_cast<CodeDirectory*>(it->second)); | |
427 | } | |
428 | ||
429 | ||
430 | void CodeDirectorySet::add(const Security::CodeSigning::CodeDirectory *cd) | |
431 | { | |
432 | insert(make_pair(cd->hashType, cd)); | |
433 | if (cd->hashType == kSecCodeSignatureHashSHA1) | |
434 | mPrimary = cd; | |
435 | } | |
436 | ||
437 | ||
438 | void CodeDirectorySet::populate(DiskRep::Writer *writer) const | |
439 | { | |
440 | assert(!empty()); | |
441 | ||
442 | if (mPrimary == NULL) // didn't add SHA-1; pick another occupant for this slot | |
443 | mPrimary = begin()->second; | |
444 | ||
445 | // reserve slot zero for a SHA-1 digest if present; else pick something else | |
446 | CodeDirectory::SpecialSlot nextAlternate = cdAlternateCodeDirectorySlots; | |
447 | for (auto it = begin(); it != end(); ++it) { | |
448 | if (it->second == mPrimary) { | |
449 | writer->codeDirectory(it->second, cdCodeDirectorySlot); | |
450 | } else { | |
451 | writer->codeDirectory(it->second, nextAlternate++); | |
452 | } | |
453 | } | |
454 | } | |
455 | ||
456 | ||
457 | const CodeDirectory* CodeDirectorySet::primary() const | |
458 | { | |
459 | if (mPrimary == NULL) | |
460 | mPrimary = begin()->second; | |
461 | return mPrimary; | |
462 | } | |
463 | ||
90dc47c2 | 464 | CFArrayRef CodeDirectorySet::hashList() const |
e3d460c9 A |
465 | { |
466 | CFRef<CFMutableArrayRef> hashList = makeCFMutableArray(0); | |
467 | for (auto it = begin(); it != end(); ++it) { | |
90dc47c2 | 468 | CFRef<CFDataRef> cdhash = it->second->cdhash(true); |
e3d460c9 A |
469 | CFArrayAppendValue(hashList, cdhash); |
470 | } | |
471 | return hashList.yield(); | |
472 | } | |
b1ab9ed8 | 473 | |
90dc47c2 A |
474 | CFDictionaryRef CodeDirectorySet::hashDict() const |
475 | { | |
476 | CFRef<CFMutableDictionaryRef> hashDict = makeCFMutableDictionary(); | |
477 | ||
478 | for (auto it = begin(); it != end(); ++it) { | |
479 | SECOidTag tag = CodeDirectorySet::SECOidTagForAlgorithm(it->first); | |
480 | ||
481 | if (tag == SEC_OID_UNKNOWN) { | |
482 | MacOSError::throwMe(errSecCSUnsupportedDigestAlgorithm); | |
483 | } | |
484 | ||
485 | CFRef<CFNumberRef> hashType = makeCFNumber(int(tag)); | |
486 | CFRef<CFDataRef> fullCdhash = it->second->cdhash(false); // Full-length cdhash! | |
487 | CFDictionarySetValue(hashDict, hashType, fullCdhash); | |
488 | } | |
489 | ||
490 | return hashDict.yield(); | |
491 | } | |
492 | ||
493 | SECOidTag CodeDirectorySet::SECOidTagForAlgorithm(CodeDirectory::HashAlgorithm algorithm) { | |
494 | SECOidTag tag; | |
495 | ||
496 | switch (algorithm) { | |
497 | case kSecCodeSignatureHashSHA1: | |
498 | tag = SEC_OID_SHA1; | |
499 | break; | |
500 | case kSecCodeSignatureHashSHA256: | |
501 | case kSecCodeSignatureHashSHA256Truncated: // truncated *page* hashes, not cdhash | |
502 | tag = SEC_OID_SHA256; | |
503 | break; | |
504 | case kSecCodeSignatureHashSHA384: | |
505 | tag = SEC_OID_SHA384; | |
506 | break; | |
507 | default: | |
508 | tag = SEC_OID_UNKNOWN; | |
509 | } | |
510 | ||
511 | return tag; | |
512 | } | |
513 | ||
514 | ||
b1ab9ed8 A |
515 | |
516 | } // end namespace CodeSigning | |
517 | } // end namespace Security |