iOS: how to debug kSecTrustResultRecoverableTrustFailure - macos

I have read quite a few posts and sources now but couldn't find a definite answer.
I'm getting kSecTrustResultRecoverableTrustFailure on my SecTrustEvaluate() call and I would like to figure out why this is so (i.e. I want to figure out where exactly the trust chain validation fails and why). on OSX there seem to be some related function called SecTrustGetResult, but this is deprecated now even on OSX
How can I figure out where the validation fails? i'm fine with using private API's as I'm using this only during debugging to understand what exactly is going on inside.
thanks

Just use SecTrustCopyProperties() after calling SecTrustEvaluate():
SecTrustRef trust = ...;
SecTrustResultType trustResult = kSecTrustResultOtherError;
OSStatus status = SecTrustEvaluate(trust, &trustResult);
if (trustResult == kSecTrustResultRecoverableTrustFailure) {
NSArray * trustProperties = (__bridge_transfer id)
SecTrustCopyProperties(certTrust);
}
trustProperties is an array of dictionaries, one dictionary per cert in the cert chain evaluated. Every dictionary has an entry title, containing the name of the cert and if the cert didn't evaluate, it also contains an entry error containing the error. E.g. if the problem was that the cert has expired, the value of error will be CSSMERR_TP_CERT_EXPIRED.

Related

Problem with Objective-C marshalling an "optionals" property in Nativescript

I'm building a NativeScript plugin for iOS to integrate a card payment terminal as an external accessory. It is almost done, and working, but I have problem with passing one argument called "optionals". This is the whole code I'm trying to implement. It's the payworks framework for a Miura terminal. http://www.payworks.mpymnt.com/node/143
MPTransactionParameters *tp = [MPTransactionParameters chargeWithAmount:[NSDecimalNumber decimalNumberWithString:#"5.00"]
currency:MPCurrencyEUR
optionals:^(id<MPTransactionParametersOptionals> _Nonnull optionals) {
optionals.subject = #"Bouquet of Flowers";
optionals.customIdentifier = #"yourReferenceForTheTransaction";
}];
I cannot find a way of sending this "optionals" function.
In the generate typing metadata I see the MPTransactionParametersOptionals is a #protocol, but still don't know how to use it here as a parameter.
This is my current javascript code for the block
const tp = MPTransactionParameters.chargeWithAmountCurrencyOptionals(
amount,
MPCurrencyEUR,
function (optionals) {
console.log(optionals); //logs the newly created MPTransactionParameters instance, with set amount and currency properties, but cannot touch or set the optional properties.
}
);
The 3rd parameter of chargeWithAmountCurrencyOptionals() should be a function, but I'm doing it wrong, and searched everywhere in google how to do it but no success. I'm already trying for 2 days.
It is working, when the 3rd parameter is null, but I need the set the optional properties.
EDIT: adding the metadata. There are a lot of typings for MPtransactionParameters, so I decided to give you the whole file so you can search.
https://drive.google.com/open?id=1kvDoXtGbCoeCT20b9_t2stc2Qts3VyQx
EDIT2: Adding the typings:
https://drive.google.com/open?id=1lZ3ULYHbX7DXdUQMPoZeSfyEZrjItSOS

How to use S3 SSE C (Server Side Encryption with Client Provided Keys) with Ruby

I'm trying to upload a file to S3 and have it encrypted using the SSE-C encryption options. I can upload without the SSE-C options, but when I supply the sse_customer_key options I'm getting the following error:
ArgumentError: header x-amz-server-side-encryption-customer-key has field value "QkExM0JGRTNDMUUyRDRCQzA5NjAwNEQ2MjRBNkExMDYwQzBGQjcxODJDMjM0\nnMUE2MTNENDRCOTcxRjA2Qzk1Mg=", this cannot include CR/LF
I'm not sure if the problem is with the key I'm generating or with the encoding. I've played around with different options here, but the AWS documentation is not very clear. In the general SSE-C documentation it says you need to supply a x-amz-server-side​-encryption​-customer-key header, which is described as this:
Use this header to provide the 256-bit, base64-encoded encryption key
for Amazon S3 to use to encrypt or decrypt your data.
However, if I look at the Ruby SDK documentation for uploading a file the 3 options have a slightly different description
:sse_customer_algorithm (String) — Specifies the algorithm to use to when encrypting the object (e.g.,
:sse_customer_key (String) — Specifies the customer-provided encryption key for Amazon S3 to use in
:sse_customer_key_md5 (String) — Specifies the 128-bit MD5 digest of the encryption key according to RFC
(I didn't copy that wrong, the AWS documentation is literally half-written like that)
So the SDK documentation makes it seem like you supply the raw sse_customer_key and that it would base64-encode it on your behalf (which makes sense to me).
So right now I'm building the options like this:
sse_customer_algorithm: :AES256,
sse_customer_key: sse_customer_key,
sse_customer_key_md5: Digest::MD5.hexdigest(sse_customer_key)
I previously tried doing Base64.encode64(sse_customer_key) but that gave me a different error:
Aws::S3::Errors::InvalidArgument: The secret key was invalid for the
specified algorithm
I'm not sure if I'm generating the key incorrectly or if I'm supplying the key incorrectly (or if it's a different problem altogether).
This is how I'm generating the key:
require "openssl"
OpenSSL::Cipher.new("AES-256-CBC").random_key
Oh, did you notice that your key contains '\n'? That's most probably why you get the CR/LF error:
QkExM0JGRTNDMUUyRDRCQzA5NjAwNEQ2MjRBNkExMDYwQzBGQjcxODJDMjM0(\n)nMUE2MTNENDRCOTcxRjA2Qzk1Mg=
As mentioned by the colleague in the comments, strict_encode64 is an option, as it complies to RFC 2045.
By the way, I got this insight from here: https://bugs.ruby-lang.org/issues/14664
Hope it helps! :)
First of all, please make sure that you are using the latest version of the SDK (2.2.2.2) from here
So, As I understand while we generate the presigned URL, we have to specify the SSECustomerMethod and when consuming the URL, the "x-amz-server-side-encryption-customer-key" header is set with the customer key, you also need to set the "x-amz-server-side-encryption-customer-algorithm" header.
var getPresignedUrlRequest = new GetPreSignedUrlRequest
{
BucketName = bucketName,
Key = "EncryptedObject",
SSECustomerMethod= SSECustomerMethod.AES256,
Expires = DateTime.Now.AddMinutes(5)
};
var url = AWSClients.S3.GetPreSignedURL(getPresignedUrlRequest);
var webRequest = HttpWebRequest.Create(url);
webRequest.Headers.Add("x-amz-server-side-encryption-customer-algorithm", "AES256");
webRequest.Headers.Add("x-amz-server-side-encryption-customer-key", base64Key);
using (var response = webRequest.GetResponse())
using (var reader = new StreamReader(response.GetResponseStream()))
{
var contents = reader.ReadToEnd();
}

How to serialize a SecTrustRef object?

I have a SecTrustRef object from the system that I'd like to evaluate myself. Just calling SecTrustEvaluateAsync will be sufficient for this job. The problem is, I must evaluate it in a different process as only this other process has access to the keychains where the CA certificates are stored that may cause evaluation to succeed.
The two processes have an IPC link that allows me to exchange arbitrary byte data between them but I don't see any way to easily serialize a SecTrustRef into byte data and deserialize that data back to an object at the other process. There doesn't seem to be a persistent storage mechanism for SecTrustRef.
So am I overlooking something important here, or do I really have to get all the certs (SecTrustGetCertificateAtIndex) and all the policies (SecTrustCopyPolicies) and serialize these myself?
And if so, how would I serialize a policy?
For the certificate (SecCertificateRef) it's rather easy, I just call SecCertificateCopyData and later on SecCertificateCreateWithData.
But for policies I can only call SecPolicyCopyProperties on one side and later on SecPolicyCreateWithProperties, however the later one requires a 2nd parameter, a policyIdentifier and I see no way to get that value from an existing policy. What am I missing?
Reading through the source of the Security framework, I finally figured it out how to copy a SecPolicyRef:
SecPolicyCreateWithProperties wants what it calls a "policyIdentifier". It's a constant like kSecPolicyAppleIPsec.
This does not get stored directly by the function, it's comparing the value and calling dedicated internal "initializers" (like SecPolicyCreateIPsec).
These in turn call SecPolicyCreate (which is private). They end up passing the same identifier value that you passed to SecPolicyCreateWithProperties.
And this value then gets stored as-is in the _oid field!
The identifier is actually the OID. You can get it either via SecPolicyCopyProperties(policy) (stored in the dictionary with key kSecPolicyOid) or via SecPolicyGetOID (but that returns it as an inconvenient CSSM_OID). Some of those specialized initializers also use values from the properties dictionary passed to SecPolicyCreateWithProperties, those should be present in the copied properties dictionary already.
So this gives us:
Serialization:
CFDictionaryRef createSerializedPolicy(SecPolicyRef policy) {
// Already contains all the information needed.
return SecPolicyCopyProperties(policy);
}
Deserialization:
SecPolicyRef createDeserializedPolicy (CFDictionaryRef serialized) {
CFStringRef oid = CFDictionaryGetValue(serialized, kSecPolicyOid);
if (oid == NULL || CFGetTypeID(oid) != CFStringGetTypeID()) {
return NULL;
}
return SecPolicyCreateWithProperties(oid, serialized);
}
To reproduce the original SecTrustRef as closely as possible, the anchors need to be copied as well. There is an internal variable _anchorsOnly which is set to true once you set anchors. Unfortunately, there is no way to query this value and I've seen it being false in trusts passed by NSURLSession, for example. No idea yet on how to get this value in a public way.
Another problematic bit are the exceptions: if _exceptions is NULL but you query them via SecTrustCopyExceptions(trust), you do get data! And if you assign that to the deserialized trust via SecTrustSetExceptions(trust, exceptions) you suddenly end up with exceptions that were not there before and can change the evaluation result! (I've seen those suddenly appearing exceptions lead to an evaluation result of "proceed" instead of "recoverable trust failure").

NSUserDefaults and serialized data

I am having a problem with our approach to data persistence in our app. It was decided to use NSUserDefaults with NSCoding compliant data model, which I disagree with due to the scale of our app.
The problem I'm seeing is when the data model changes, any attempt to deserialized results in a crash. The app must be uninstalled and reinstalled in order to re-serialize.
Scenario:
User installs app
User does stuff.
Developer decides that a property should be added to one of the serialized objects and pushes an update.
User installs update.
App goes 'kaboom'.
This is happening because the data had been serialized with a different model than it is now attempting to be deserialized as.
Example:
class Contact: NSCoding {
var name
var address
var userId
}
... // NSCoding compliance happens next. This object gets serialized.
Someone decides that Contact needs more stuff:
class Contact: NSCoding {
var name
var address
var userId
var phoneNumber
var emailAddress
}
Attempting to deserialize the Contact object, even though the NSCoding compliance for encoding and decoding has been updated to load and deserialize, causes
fatal error: unexpectedly found nil while unwrapping an Optional value
CoreDataManager.unarchiveUser
Worker.init
So, my question is, how could we possibly avoid this crash from occurring when running an updated version of the app that has a different schema??
You're crashing because,
You are attempting to decodeObject(forKey:) on a key that doesn't exist (because it didn't exist on the class when the object was encoded). This method returns nil.
You are using ! to force-unwrap the result of #1.
As a rule of thumb, if your Swift code crashes on a line that contains a !, there's about a 95% chance that the ! is the direct cause of the crash. If the error message mentions unwrapping an optional, it's a 100% chance.
The documentation for the decodeObject(forKey:) method explains that it may return nil. In your case this is guaranteed to happen if you're upgrading from a previous version of the class and you're decoding a key that you just added.
Your code needs to recognize that there might not be a value for the new properties. The simplest fix is replacing as! with as?. Then you'll get a nil value for the new property. For properties that are not optional, you can add something like ?? "default value" to the end.
You could also use the containsValue(forKey:) method to check if a value exists before trying to decode the key.

Using Social.framework and SLRequest with Field Expansion

So far, OS X 10.8.2's Social.Framework works great:
NSURL *requestURL = [NSURL URLWithString:#"https://graph.facebook.com/me/friends"];
SLRequest *fbRequest = [SLRequest requestForServiceType:SLServiceTypeFacebook requestMethod:SLRequestMethodGET URL:requestURL parameters:params];
This properly delivers the list of friends of the me() object. Fine!
However, once I try to make use of Field Expansion, as follows, the request fails with the error:
An active access token must be used to query information about the current user:
[NSURL URLWithString:#".../friends?fields=cover,picture"];
This is surely reasoned by the fact, that the access_token will be appended internally in the Social.framework using st. similar to "?access_token=%#", which will fail in conjunction with the previous ?fields= usage by myself.
So I wonder whether this is actually a bug of the framework, or whether I'm using it wrongly? I'd really be thankful for any helpful information.
After contacting Apple directly, this is clearly the answer:
Add the field expansion tags using the GET parameter variable, instead of adding them to the actual URL:
SLRequest *fbRequest = [SLRequest requestForServiceType:SLServiceTypeFacebook
requestMethod:SLRequestMethodGET
URL:requestURL
parameters:#{#"fields":#"cover,picture"}];
Which is obvious, but I didn't think of that :). I'll let this thread as it is if someone might stumble upon the same problem in future!

Resources