Skip to content

Commit 1fdca51

Browse files
Introduce signing guile-ssh functions
1 parent 72ce045 commit 1fdca51

File tree

3 files changed

+135
-1
lines changed

3 files changed

+135
-1
lines changed

libguile-ssh/key-func.c

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,111 @@ Return a fingerprint string on success, #f on error.\
486486
}
487487
#undef FUNC_NAME
488488

489+
static gssh_symbol_t sshsig_hash_types[] = {
490+
{ "sha256", SSHSIG_DIGEST_SHA2_256 },
491+
{ "sha512", SSHSIG_DIGEST_SHA2_512 },
492+
{ NULL, -1 }
493+
};
494+
495+
SCM_DEFINE (guile_ssh_sign, "%sshsig-sign", 4, 0, 0,
496+
(SCM data_bv, SCM private_key, SCM sig_namespace, SCM hash_alg),
497+
"\
498+
Sign binary DATA_BV (bytevector) using a PRIVATE_KEY with specified SIG_NAMESPACE and HASH_ALG.\n\
499+
HASH_ALG should be 'sha256 or 'sha512.\n\
500+
Return the armored signature string on success, #f on error.\
501+
")
502+
#define FUNC_NAME s_guile_ssh_sign
503+
{
504+
gssh_key_t *kd = gssh_key_from_scm (private_key);
505+
char *c_sig_namespace = NULL;
506+
char *armored_sig = NULL;
507+
const void *data;
508+
size_t data_len;
509+
const gssh_symbol_t *hash_type = NULL;
510+
int res;
511+
SCM ret;
512+
513+
SCM_ASSERT (scm_is_bytevector (data_bv), data_bv, SCM_ARG1, FUNC_NAME);
514+
SCM_ASSERT (_private_key_p (kd), private_key, SCM_ARG2, FUNC_NAME);
515+
SCM_ASSERT (scm_is_string (sig_namespace), sig_namespace, SCM_ARG3, FUNC_NAME);
516+
SCM_ASSERT (scm_is_symbol (hash_alg), hash_alg, SCM_ARG4, FUNC_NAME);
517+
518+
scm_dynwind_begin (0);
519+
520+
data_len = scm_c_bytevector_length (data_bv);
521+
data = SCM_BYTEVECTOR_CONTENTS (data_bv);
522+
523+
c_sig_namespace = scm_to_locale_string (sig_namespace);
524+
scm_dynwind_free (c_sig_namespace);
525+
526+
hash_type = gssh_symbol_from_scm (sshsig_hash_types, hash_alg);
527+
if (! hash_type)
528+
guile_ssh_error1 (FUNC_NAME, "Wrong hash type", hash_alg);
529+
530+
res = sshsig_sign (data, data_len, kd->ssh_key, c_sig_namespace,
531+
hash_type->value, &armored_sig);
532+
533+
if (res == SSH_OK)
534+
{
535+
ret = scm_take_locale_string (armored_sig);
536+
}
537+
else
538+
{
539+
ret = SCM_BOOL_F;
540+
}
541+
542+
scm_dynwind_end ();
543+
return ret;
544+
}
545+
#undef FUNC_NAME
546+
547+
SCM_DEFINE (guile_ssh_verify, "%sshsig-verify", 3, 0, 0,
548+
(SCM data_bv, SCM signature, SCM sig_namespace),
549+
"\
550+
Verify a SIGNATURE for binary DATA_BV (bytevector) with SIG_NAMESPACE.\n\
551+
Return the signing key on success, #f on error.\
552+
")
553+
#define FUNC_NAME s_guile_ssh_verify
554+
{
555+
char *c_signature = NULL;
556+
char *c_sig_namespace = NULL;
557+
const void *data;
558+
size_t data_len;
559+
ssh_key sign_key = NULL;
560+
int res;
561+
SCM ret;
562+
563+
SCM_ASSERT (scm_is_bytevector (data_bv), data_bv, SCM_ARG1, FUNC_NAME);
564+
SCM_ASSERT (scm_is_string (signature), signature, SCM_ARG2, FUNC_NAME);
565+
SCM_ASSERT (scm_is_string (sig_namespace), sig_namespace, SCM_ARG3, FUNC_NAME);
566+
567+
scm_dynwind_begin (0);
568+
569+
data_len = scm_c_bytevector_length (data_bv);
570+
data = SCM_BYTEVECTOR_CONTENTS (data_bv);
571+
572+
c_signature = scm_to_locale_string (signature);
573+
scm_dynwind_free (c_signature);
574+
575+
c_sig_namespace = scm_to_locale_string (sig_namespace);
576+
scm_dynwind_free (c_sig_namespace);
577+
578+
res = sshsig_verify (data, data_len, c_signature, c_sig_namespace, &sign_key);
579+
580+
if (res == SSH_OK)
581+
{
582+
ret = gssh_key_to_scm (sign_key, SCM_BOOL_F);
583+
}
584+
else
585+
{
586+
ret = SCM_BOOL_F;
587+
}
588+
589+
scm_dynwind_end ();
590+
return ret;
591+
}
592+
#undef FUNC_NAME
593+
489594

490595
/* Initialize Scheme procedures. */
491596
void

libguile-ssh/key-func.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ extern SCM guile_ssh_public_key_to_string (SCM arg1);
2626
extern SCM guile_ssh_private_key_from_file (SCM arg1, SCM arg2);
2727
extern SCM guile_ssh_public_key_from_file (SCM arg1, SCM arg2);
2828
extern SCM guile_ssh_get_public_key_fingerprint (SCM arg1, SCM arg2);
29+
extern SCM guile_ssh_sign (SCM arg1, SCM arg2, SCM arg3, SCM arg4);
30+
extern SCM guile_ssh_verify (SCM arg1, SCM arg2, SCM arg3);
2931

3032
extern void init_key_func (void);
3133

modules/ssh/key.scm

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,15 @@
3838
;; get-public-key-hash
3939
;; get-public-key-fingerprint
4040
;; bytevector->hex-string
41+
;; sign
42+
;; verify
4143

4244

4345
;;; Code:
4446

4547
(define-module (ssh key)
4648
#:use-module (ice-9 format)
49+
#:use-module (ice-9 match)
4750
#:use-module (rnrs bytevectors)
4851
#:use-module (ssh log)
4952
#:export (key
@@ -60,7 +63,9 @@
6063
private-key-to-file
6164
get-public-key-fingerprint
6265
get-public-key-hash
63-
bytevector->hex-string))
66+
bytevector->hex-string
67+
sign
68+
verify))
6469

6570
(define (bytevector->hex-string bv)
6671
"Convert bytevector BV to a colon separated hex string."
@@ -77,6 +82,28 @@
7782
(define* (get-public-key-fingerprint key #:optional (hash 'sha256))
7883
(%get-public-key-fingerprint key hash))
7984

85+
(define* (sign data key
86+
#:key
87+
(namespace "file")
88+
(hash 'sha512))
89+
(match data
90+
((? string?)
91+
(%sshsig-sign (string->utf8 data) key namespace hash))
92+
((? bytevector?)
93+
(%sshsig-sign data key namespace hash))
94+
(_
95+
(error "sign: DATA must be a string or bytevector"))))
96+
97+
(define* (verify data signature
98+
#:key (namespace "file"))
99+
(match data
100+
((? string?)
101+
(%sshsig-verify (string->utf8 data) signature namespace))
102+
((? bytevector?)
103+
(%sshsig-sign data key namespace hash))
104+
(_
105+
(error "verify: DATA must be a string or bytevector"))))
106+
80107
(unless (getenv "GUILE_SSH_CROSS_COMPILING")
81108
(load-extension "libguile-ssh" "init_key"))
82109

0 commit comments

Comments
 (0)