1 /* Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
3 /** \file crypt_common.c Crypto functions independent of openssl/libgcrypt. */
11 #include "crypt_backend.h"
12 #include "portable_io.h"
16 /** If the key begins with this text, we treat it as an ssh key. */
17 #define KEY_TYPE_TXT "ssh-rsa"
20 * Check if the given buffer starts with an ssh rsa key signature.
22 * Returns number of header bytes to be skipped on success, zero if no ssh rsa
23 * signature was found.
25 static size_t is_ssh_rsa_key(char *data
, size_t size
)
29 if (size
< strlen(KEY_TYPE_TXT
) + 2)
31 cp
= memchr(data
, ' ', size
);
34 if (strncmp(KEY_TYPE_TXT
, data
, strlen(KEY_TYPE_TXT
)))
37 if (cp
>= data
+ size
)
45 * Perform some sanity checks on the decoded ssh key.
47 * This function returns the size of the header. Usually, the header is 11
48 * bytes long: four bytes for the length field, and the string "ssh-rsa".
50 static int check_ssh_key_header(const unsigned char *blob
, int blen
)
52 const unsigned char *p
= blob
, *end
= blob
+ blen
;
56 return -E_SSH_KEY_HEADER
;
57 rlen
= read_u32_be(p
);
60 return -E_SSH_KEY_HEADER
;
62 return -E_SSH_KEY_HEADER
;
63 if (rlen
< strlen(KEY_TYPE_TXT
))
64 return -E_SSH_KEY_HEADER
;
65 PARA_DEBUG_LOG("type: %s, rlen: %u\n", p
, rlen
);
66 if (strncmp((char *)p
, KEY_TYPE_TXT
, strlen(KEY_TYPE_TXT
)))
67 return -E_SSH_KEY_HEADER
;
72 * Perform sanity checks and base64-decode an ssh-rsa key.
74 * \param filename The public key file (usually id_rsa.pub).
75 * \param blob Pointer to base64-decoded blob is returned here.
76 * \param decoded_size The size of the decoded blob.
78 * The memory pointed at by the returned blob pointer has to be freed by the
81 * \return On success, the offset in bytes of the start of the key values
82 * (modulus, exponent..). This is the number of bytes to skip from the blob
83 * until the start of the first encoded number. On failure, a negative error
86 * \sa \ref uudecode().
88 int decode_public_key(const char *filename
, unsigned char **blob
,
95 ret
= mmap_full_file(filename
, O_RDONLY
, &map
, &map_size
, NULL
);
98 ret
= is_ssh_rsa_key(map
, map_size
);
103 ret
= uudecode(map
+ ret
, map_size
- ret
, (char **)blob
, decoded_size
);
106 ret
= check_ssh_key_header(*blob
, *decoded_size
);
110 ret2
= para_munmap(map
, map_size
);
111 if (ret
>= 0 && ret2
< 0)
117 * Check existence and permissions of a private key file.
119 * \param file The path of the key file.
121 * This checks whether the file exists and its permissions are restrictive
122 * enough. It is considered an error if we own the file and it is readable for
127 int check_private_key_file(const char *file
)
131 if (stat(file
, &st
) != 0)
132 return -ERRNO_TO_PARA_ERROR(errno
);
133 if ((st
.st_uid
== getuid()) && (st
.st_mode
& 077) != 0)
138 void hash_to_asc(const unsigned char *hash
, char *asc
)
141 const char hexchar
[] = "0123456789abcdef";
143 for (i
= 0; i
< HASH_SIZE
; i
++) {
144 asc
[2 * i
] = hexchar
[hash
[i
] >> 4];
145 asc
[2 * i
+ 1] = hexchar
[hash
[i
] & 0xf];
147 asc
[2 * HASH_SIZE
] = '\0';
150 int hash_compare(const unsigned char *h1
, const unsigned char *h2
)
154 for (i
= 0; i
< HASH_SIZE
; i
++) {
164 * Check header of an openssh private key and compute bignum offset.
166 * \param data The base64-decoded key.
167 * \param len The size of the decoded key.
169 * Several assumptions are made about the key. Most notably, we only support
170 * single unencrypted keys without comments.
172 * \return The offset at which the first bignum of the private key (the public
173 * exponent n) starts. Negative error code on failure.
175 int find_openssh_bignum_offset(const unsigned char *data
, int len
)
178 * Unencrypted keys without comments always start with the below byte
179 * sequence. See PROTOCOL.key of the openssh package.
181 static const unsigned char valid_openssh_header
[] = {
182 /* string "openssh-key-v1" */
183 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73, 0x68, 0x2d, 0x6b, 0x65,
184 0x79, 0x2d, 0x76, 0x31,
185 /* length of the cipher name */
186 0x00, 0x00, 0x00, 0x00, 0x04,
187 /* cipher name: "none" */
188 0x6e, 0x6f, 0x6e, 0x65,
189 /* length of the kdfname (only used for encrypted keys) */
190 0x00, 0x00, 0x00, 0x04,
191 /* kdfname: "none" */
192 0x6e, 0x6f, 0x6e, 0x65,
193 /* length of kdfoptions */
194 0x00, 0x00, 0x00, 0x00,
196 0x00, 0x00, 0x00, 0x01,
199 const unsigned char *p
, *end
= data
+ len
;
201 if (len
<= sizeof(valid_openssh_header
) + 4)
202 return -E_OPENSSH_PARSE
;
203 if (memcmp(data
, valid_openssh_header
, sizeof(valid_openssh_header
)))
204 return -E_OPENSSH_PARSE
;
205 p
= data
+ sizeof(valid_openssh_header
);
206 /* length of public key */
207 val
= read_u32_be(p
);
208 if (val
> end
- p
- 4)
209 return -E_OPENSSH_PARSE
;
211 /* length of private key */
212 val
= read_u32_be(p
);
213 if (val
> end
- p
- 4)
214 return -E_OPENSSH_PARSE
;
216 /* two equal random integers ("checkint") */
218 return -E_OPENSSH_PARSE
;
219 if (read_u32_be(p
) != read_u32_be(p
+ 4))
220 return -E_OPENSSH_PARSE
;
222 /* length of name of key type "ssh-rsa" */
224 return -E_OPENSSH_PARSE
;
225 if (read_u32_be(p
) != 7)
226 return -E_OPENSSH_PARSE
;
227 if (memcmp(p
+ 4, "ssh-rsa", 7))
228 return -E_OPENSSH_PARSE
;
233 /** Private PEM keys (legacy format) start with this header. */
234 #define PRIVATE_PEM_KEY_HEADER "-----BEGIN RSA PRIVATE KEY-----"
235 /** Private OPENSSH keys (RFC4716) start with this header. */
236 #define PRIVATE_OPENSSH_KEY_HEADER "-----BEGIN OPENSSH PRIVATE KEY-----"
237 /** Private PEM keys (legacy format) end with this footer. */
238 #define PRIVATE_PEM_KEY_FOOTER "-----END RSA PRIVATE KEY-----"
239 /** Private OPENSSH keys (RFC4716) end with this footer. */
240 #define PRIVATE_OPENSSH_KEY_FOOTER "-----END OPENSSH PRIVATE KEY-----"
243 * Decode an openssh-v1 (aka RFC4716) or PEM (aka ASN.1) private key.
245 * \param key_file The private key file (usually id_rsa).
246 * \param result Pointer to base64-decoded blob is returned here.
247 * \param blob_size The size of the decoded blob.
249 * This only checks header and footer and base64-decodes the part in between.
250 * No attempt to read the decoded part is made.
252 * \return Negative on errors, PKT_PEM or PKT_OPENSSH on success, indicating
255 int decode_private_key(const char *key_file
, unsigned char **result
,
258 int ret
, ret2
, i
, j
, key_type
;
260 size_t map_size
, key_size
;
261 unsigned char *blob
= NULL
;
262 char *begin
, *footer
, *key
;
264 ret
= mmap_full_file(key_file
, O_RDONLY
, &map
, &map_size
, NULL
);
268 if (strncmp(map
, PRIVATE_PEM_KEY_HEADER
,
269 strlen(PRIVATE_PEM_KEY_HEADER
)) == 0) {
271 begin
= map
+ strlen(PRIVATE_PEM_KEY_HEADER
);
272 footer
= strstr(map
, PRIVATE_PEM_KEY_FOOTER
);
273 PARA_INFO_LOG("detected legacy PEM key %s\n", key_file
);
274 } else if (strncmp(map
, PRIVATE_OPENSSH_KEY_HEADER
,
275 strlen(PRIVATE_OPENSSH_KEY_HEADER
)) == 0) {
276 key_type
= PKT_OPENSSH
;
277 begin
= map
+ strlen(PRIVATE_OPENSSH_KEY_HEADER
);
278 footer
= strstr(map
, PRIVATE_OPENSSH_KEY_FOOTER
);
279 PARA_INFO_LOG("detected openssh key %s\n", key_file
);
284 /* skip whitespace at the beginning */
285 for (; begin
< footer
; begin
++) {
286 if (para_isspace(*begin
))
294 key_size
= footer
- begin
;
295 key
= para_malloc(key_size
+ 1);
296 for (i
= 0, j
= 0; begin
+ i
< footer
; i
++) {
297 if (para_isspace(begin
[i
]))
302 ret
= base64_decode(key
, j
, (char **)&blob
, blob_size
);
308 ret2
= para_munmap(map
, map_size
);
309 if (ret
>= 0 && ret2
< 0)