3 * Copyright (c) 2018-2019 Apple Computer, Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * DNS SIG(0) signature generation for DNSSD SRP using mbedtls.
19 * Functions required for loading, saving, and generating public/private keypairs, extracting the public key
20 * into KEY RR data, and computing signatures.
22 * This is the implementation for Mac OS X.
27 #include <arpa/inet.h>
28 #include <sys/random.h>
40 #define SRP_CRYPTO_MACOS_INTERNAL
41 #include "srp-crypto.h"
43 // Key is stored in an opaque data structure, for mbedtls this is an mbedtls_pk_context.
44 // Function to read a public key from a KEY record
45 // Function to validate a signature given some data and a public key (not required on client)
47 // Function to free a key
49 srp_keypair_free(srp_key_t
*key
)
57 return arc4random_uniform(65536);
61 srp_sec_error_print(const char *reason
, OSStatus status
)
63 const char *utf8
= NULL
;
64 CFStringRef err
= SecCopyErrorMessageString(status
, NULL
);
66 utf8
= CFStringGetCStringPtr(err
, kCFStringEncodingUTF8
);
69 ERROR(PUB_S_SRP
": " PUB_S_SRP
, reason
, utf8
);
71 ERROR(PUB_S_SRP
": %d", reason
, (int)status
);
78 // Function to generate a key
80 srp_get_key_internal(const char *key_name
, bool delete)
83 srp_key_t
*key
= NULL
;
86 CFMutableDictionaryRef key_parameters
= CFDictionaryCreateMutable(NULL
, 8,
87 &kCFTypeDictionaryKeyCallBacks
,
88 &kCFTypeDictionaryValueCallBacks
);
89 CFMutableDictionaryRef pubkey_parameters
;
91 if (key_parameters
!= NULL
) {
92 CFDictionaryAddValue(key_parameters
, kSecAttrIsPermanent
, kCFBooleanTrue
);
93 CFDictionaryAddValue(key_parameters
, kSecAttrKeyType
, kSecAttrKeyTypeECSECPrimeRandom
);
94 CFNumberRef num
= CFNumberCreate(NULL
, kCFNumberLongType
, &two56
);
95 CFDictionaryAddValue(key_parameters
, kSecAttrKeySizeInBits
, num
);
97 CFStringRef str
= CFStringCreateWithCString(NULL
, key_name
, kCFStringEncodingUTF8
);
98 CFDictionaryAddValue(key_parameters
, kSecAttrLabel
, str
);
100 CFDictionaryAddValue(key_parameters
, kSecReturnRef
, kCFBooleanTrue
);
101 CFDictionaryAddValue(key_parameters
, kSecMatchLimit
, kSecMatchLimitOne
);
102 CFDictionaryAddValue(key_parameters
, kSecClass
, kSecClassKey
);
103 pubkey_parameters
= CFDictionaryCreateMutableCopy(NULL
, 8, key_parameters
);
104 if (pubkey_parameters
!= NULL
) {
105 CFDictionaryAddValue(key_parameters
, kSecAttrKeyClass
, kSecAttrKeyClassPrivate
);
106 CFDictionaryAddValue(pubkey_parameters
, kSecAttrKeyClass
, kSecAttrKeyClassPublic
);
107 CFDictionaryAddValue(pubkey_parameters
, kSecAttrIsExtractable
, kCFBooleanTrue
);
108 CFDictionaryAddValue(key_parameters
, kSecAttrIsExtractable
, kCFBooleanTrue
);
109 #if !defined(OPEN_SOURCE) && TARGET_OS_TV
110 CFDictionaryAddValue(pubkey_parameters
, kSecAttrAccessible
,
111 kSecAttrAccessibleAlwaysThisDeviceOnlyPrivate
);
113 CFDictionaryAddValue(pubkey_parameters
, kSecAttrAccessible
,
114 kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
);
117 status
= SecItemDelete(key_parameters
);
118 if (status
== errSecSuccess
) {
119 status
= SecItemDelete(pubkey_parameters
);
123 key
= calloc(1, sizeof(*key
));
126 CFErrorRef error
= NULL
;
128 // See if the key is already on the keychain.
129 status
= SecItemCopyMatching(key_parameters
, (CFTypeRef
*)&key
->private);
130 if (status
== errSecSuccess
) {
131 status
= SecItemCopyMatching(pubkey_parameters
, (CFTypeRef
*)&key
->public);
133 key
->private = SecKeyCreateRandomKey(key_parameters
, &error
);
134 if (key
->private != NULL
) {
135 key
->public = SecKeyCopyPublicKey(key
->private);
138 if (key
->public == NULL
|| key
->private == NULL
) {
142 srp_sec_error_print("Failed to get key pair", status
);
149 CFRelease(key_parameters
);
150 CFRelease(pubkey_parameters
);
157 srp_get_key(const char *key_name
, void *__unused os_context
)
159 return srp_get_key_internal(key_name
, false);
162 // Remove an existing key
164 srp_reset_key(const char *key_name
, void *__unused os_context
)
166 srp_get_key_internal(key_name
, true);
167 return kDNSServiceErr_NoError
;
170 // void to get the length of the public key
172 srp_pubkey_length(srp_key_t
*key
)
175 return ECDSA_KEY_SIZE
;
179 srp_key_algorithm(srp_key_t
*key
)
182 return dnssec_keytype_ecdsa
;
186 srp_signature_length(srp_key_t
*key
)
189 return ECDSA_KEY_SIZE
;
192 // Function to copy out the public key as binary data
194 srp_pubkey_copy(uint8_t *buf
, size_t max
, srp_key_t
*key
)
196 CFErrorRef error
= NULL
;
198 CFDataRef pubkey
= SecKeyCopyExternalRepresentation(key
->public, &error
);
199 if (pubkey
== NULL
) {
203 ERROR("Unknown failure in SecKeyCopyExternalRepresentation");
206 const uint8_t *bytes
= CFDataGetBytePtr(pubkey
);
207 unsigned long len
= CFDataGetLength(pubkey
);
209 // Should be 04 | X | Y
211 ERROR("Unexpected preamble to public key: %d", bytes
[0]);
212 } else if (len
- 1 > max
) {
213 ERROR("Not enough room for public key in buffer: %ld > %zd", len
- 1, max
);
214 } else if (len
- 1 != ECDSA_KEY_SIZE
) {
215 ERROR("Unexpected key size %ld", len
- 1);
217 memcpy(buf
, bytes
+ 1, len
- 1);
218 ret
= ECDSA_KEY_SIZE
;
225 // Function to generate a signature given some data and a private key
227 srp_sign(uint8_t *output
, size_t max
, uint8_t *message
, size_t msglen
,
228 uint8_t *rr
, size_t rdlen
, srp_key_t
*key
)
230 CFMutableDataRef payload
= NULL
;
231 CFDataRef signature
= NULL
;
232 CFErrorRef error
= NULL
;
233 const uint8_t *bytes
;
239 } raw_signature_data_t
;
240 raw_signature_data_t raw_signature
;
242 ECDSA_SIG_TEMPLATE(sig_template
);
244 if (max
< ECDSA_SHA256_SIG_SIZE
) {
245 ERROR("srp_sign: not enough space in output buffer (%lu) for signature (%d).",
246 (unsigned long)max
, ECDSA_SHA256_SIG_SIZE
);
250 payload
= CFDataCreateMutable(NULL
, msglen
+ rdlen
);
251 if (payload
== NULL
) {
252 ERROR("srp_sign: CFDataCreateMutable failed on length %zd", msglen
+ rdlen
);
255 CFDataAppendBytes(payload
, rr
, rdlen
);
256 CFDataAppendBytes(payload
, message
, msglen
);
258 signature
= SecKeyCreateSignature(key
->private,
259 kSecKeyAlgorithmECDSASignatureMessageX962SHA256
, payload
, &error
);
262 CFRelease(signature
);
266 if (signature
== NULL
) {
267 ERROR("No error, but no signature.");
271 SecAsn1CoderRef decoder
;
272 OSStatus status
= SecAsn1CoderCreate(&decoder
);
273 if (status
== errSecSuccess
) {
274 len
= CFDataGetLength(signature
);
275 bytes
= CFDataGetBytePtr(signature
);
277 status
= SecAsn1Decode(decoder
, bytes
, len
, sig_template
, &raw_signature
);
278 if (status
== errSecSuccess
) {
279 if (raw_signature
.r
.Length
+ raw_signature
.s
.Length
> ECDSA_SHA256_SIG_SIZE
) {
280 ERROR("Unexpected length %zd + %zd is not %d", raw_signature
.r
.Length
,
281 raw_signature
.s
.Length
, ECDSA_SHA256_SIG_SIZE
);
283 unsigned long diff
= ECDSA_SHA256_SIG_PART_SIZE
- raw_signature
.r
.Length
;
285 memset(output
, 0, diff
);
287 memcpy(output
+ diff
, raw_signature
.r
.Data
, raw_signature
.r
.Length
);
288 diff
= ECDSA_SHA256_SIG_PART_SIZE
- raw_signature
.s
.Length
;
290 memset(output
+ ECDSA_SHA256_SIG_PART_SIZE
, 0, diff
);
292 memcpy(output
+ ECDSA_SHA256_SIG_PART_SIZE
+ diff
, raw_signature
.s
.Data
, raw_signature
.s
.Length
);
296 SecAsn1CoderRelease(decoder
);
298 if (status
!= errSecSuccess
) {
299 srp_sec_error_print("srp_sign", status
);
301 CFRelease(signature
);
308 // c-file-style: "bsd"
311 // indent-tabs-mode: nil