@@ -24,6 +24,13 @@ package certificates
24
24
#cgo LDFLAGS: -framework Cocoa
25
25
#import <Cocoa/Cocoa.h>
26
26
27
+
28
+ // Used to return error strings (as NSString) as a C-string to the Go code.
29
+ const char *toErrorString(NSString *errString) {
30
+ NSLog(@"%@", errString);
31
+ return [errString cStringUsingEncoding:[NSString defaultCStringEncoding]];
32
+ }
33
+
27
34
const char *installCert(const char *path) {
28
35
NSURL *url = [NSURL fileURLWithPath:@(path) isDirectory:NO];
29
36
NSData *rootCertData = [NSData dataWithContentsOfURL:url];
@@ -90,7 +97,9 @@ const char *uninstallCert() {
90
97
return "";
91
98
}
92
99
93
- const char *getExpirationDate(char *expirationDate){
100
+ // Returns the expiration date "kSecOIDX509V1ValidityNotAfter" of the Arduino certificate.
101
+ // The value is returned as a CFAbsoluteTime: a long number of seconds from the date of 1 Jan 2001 00:00:00 GMT.
102
+ const char *getExpirationDate(long *expirationDate) {
94
103
// Create a key-value dictionary used to query the Keychain and look for the "Arduino" root certificate.
95
104
NSDictionary *getquery = @{
96
105
(id)kSecClass: (id)kSecClassCertificate,
@@ -101,42 +110,32 @@ const char *getExpirationDate(char *expirationDate){
101
110
OSStatus err = noErr;
102
111
SecCertificateRef cert = NULL;
103
112
104
- // Use this function to check for errors
113
+ // Search the keychain for certificates matching the query above.
105
114
err = SecItemCopyMatching((CFDictionaryRef)getquery, (CFTypeRef *)&cert);
106
-
107
115
if (err != noErr){
108
116
NSString *errString = [@"Error: " stringByAppendingFormat:@"%d", err];
109
117
NSLog(@"%@", errString);
110
118
return [errString cStringUsingEncoding:[NSString defaultCStringEncoding]];
111
119
}
112
120
113
- // Get data from the certificate. We just need the "invalidity date" property.
114
- CFDictionaryRef valuesDict = SecCertificateCopyValues(cert, (__bridge CFArrayRef)@[(__bridge id)kSecOIDX509V1ValidityNotAfter], NULL);
115
-
116
- id expirationDateValue;
117
- if(valuesDict){
118
- CFDictionaryRef invalidityDateDictionaryRef = CFDictionaryGetValue(valuesDict, kSecOIDX509V1ValidityNotAfter);
119
- if(invalidityDateDictionaryRef){
120
- CFTypeRef invalidityRef = CFDictionaryGetValue(invalidityDateDictionaryRef, kSecPropertyKeyValue);
121
- if(invalidityRef){
122
- expirationDateValue = CFBridgingRelease(invalidityRef);
123
- }
124
- }
125
- CFRelease(valuesDict);
126
- }
121
+ // Get data from the certificate, as a dictionary of properties. We just need the "invalidity not after" property.
122
+ CFDictionaryRef certDict = SecCertificateCopyValues(cert,
123
+ (__bridge CFArrayRef)@[(__bridge id)kSecOIDX509V1ValidityNotAfter], NULL);
124
+ if (certDict == NULL) return toErrorString(@"SecCertificateCopyValues failed");
127
125
128
- NSString *outputString = [@"" stringByAppendingFormat:@"%@", expirationDateValue];
129
- if([outputString isEqualToString:@""]){
130
- NSString *errString = @"Error: the expiration date of the certificate could not be found";
131
- NSLog(@"%@", errString);
132
- return [errString cStringUsingEncoding:[NSString defaultCStringEncoding]];
133
- }
134
126
135
- // This workaround allows to obtain the expiration date alongside the error message
136
- strncpy(expirationDate, [outputString cStringUsingEncoding:[NSString defaultCStringEncoding]], 32 );
137
- expirationDate[32-1] = 0 ;
127
+ // Get the "validity not after" property as a dictionary, and get the "value" key (that is a number).
128
+ CFDictionaryRef validityNotAfterDict = CFDictionaryGetValue(certDict, kSecOIDX509V1ValidityNotAfter );
129
+ if (validityNotAfterDict == NULL) return toErrorString(@"CFDictionaryGetValue (validity) failed") ;
138
130
139
- return "";
131
+ CFNumberRef number = (CFNumberRef)CFDictionaryGetValue(validityNotAfterDict, kSecPropertyKeyValue);
132
+ if (number == NULL) return toErrorString(@"CFDictionaryGetValue (keyValue) failed");
133
+
134
+ CFNumberGetValue(number, kCFNumberSInt64Type, expirationDate);
135
+ // NSLog(@"Certificate validity not after: %ld", *expirationDate);
136
+
137
+ CFRelease(certDict);
138
+ return ""; // No error.
140
139
}
141
140
142
141
const char *getDefaultBrowserName() {
@@ -173,7 +172,6 @@ const char *certInKeychain() {
173
172
import "C"
174
173
import (
175
174
"errors"
176
- "strconv"
177
175
"time"
178
176
"unsafe"
179
177
@@ -215,16 +213,20 @@ func UninstallCertificates() error {
215
213
// GetExpirationDate returns the expiration date of a certificate stored in the keychain
216
214
func GetExpirationDate () (time.Time , error ) {
217
215
log .Infof ("Retrieving certificate's expiration date" )
218
- dateString := C .CString ("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" ) // 32 characters string
219
- defer C .free (unsafe .Pointer (dateString ))
220
- p := C .getExpirationDate (dateString )
221
- s := C .GoString (p )
222
- if len (s ) != 0 {
223
- utilities .UserPrompt (s , "\" OK\" " , "OK" , "Arduino Agent: Error retrieving expiration date" )
224
- return time.Time {}, errors .New (s )
216
+
217
+ expirationDateLong := C .long (0 )
218
+
219
+ err := C .getExpirationDate (& expirationDateLong )
220
+ errString := C .GoString (err )
221
+ if len (errString ) > 0 {
222
+ utilities .UserPrompt (errString , "\" OK\" " , "OK" , "Arduino Agent: Error retrieving expiration date" )
223
+ return time.Time {}, errors .New (errString )
225
224
}
226
- dateValue , _ := strconv .ParseInt (C .GoString (dateString ), 10 , 64 )
227
- return time .Unix (dateValue , 0 ).AddDate (31 , 0 , 0 ), nil
225
+
226
+ // The expirationDate is the number of seconds from the date of 1 Jan 2001 00:00:00 GMT.
227
+ // Add 31 years to convert it to Unix Epoch.
228
+ expirationDate := int64 (expirationDateLong )
229
+ return time .Unix (expirationDate , 0 ).AddDate (31 , 0 , 0 ), nil
228
230
}
229
231
230
232
// GetDefaultBrowserName returns the name of the default browser
0 commit comments