diff options
Diffstat (limited to 'regress/misc')
-rw-r--r-- | regress/misc/Makefile | 2 | ||||
-rw-r--r-- | regress/misc/fuzz-harness/Makefile | 31 | ||||
-rw-r--r-- | regress/misc/fuzz-harness/privkey_fuzz.cc | 21 | ||||
-rw-r--r-- | regress/misc/fuzz-harness/sig_fuzz.cc | 24 | ||||
-rw-r--r-- | regress/misc/fuzz-harness/ssh-sk-null.cc | 51 | ||||
-rw-r--r-- | regress/misc/fuzz-harness/sshsig_fuzz.cc | 4 | ||||
-rw-r--r-- | regress/misc/kexfuzz/Makefile | 6 | ||||
-rw-r--r-- | regress/misc/kexfuzz/kexfuzz.c | 8 | ||||
-rw-r--r-- | regress/misc/sk-dummy/Makefile | 66 | ||||
-rw-r--r-- | regress/misc/sk-dummy/fatal.c | 20 | ||||
-rw-r--r-- | regress/misc/sk-dummy/sk-dummy.c | 526 |
11 files changed, 731 insertions, 28 deletions
diff --git a/regress/misc/Makefile b/regress/misc/Makefile index 14c0c279f..cf95f265c 100644 --- a/regress/misc/Makefile +++ b/regress/misc/Makefile | |||
@@ -1,3 +1,3 @@ | |||
1 | SUBDIR= kexfuzz | 1 | SUBDIR= kexfuzz sk-dummy |
2 | 2 | ||
3 | .include <bsd.subdir.mk> | 3 | .include <bsd.subdir.mk> |
diff --git a/regress/misc/fuzz-harness/Makefile b/regress/misc/fuzz-harness/Makefile index 85179ac4e..64fbdbab1 100644 --- a/regress/misc/fuzz-harness/Makefile +++ b/regress/misc/fuzz-harness/Makefile | |||
@@ -3,31 +3,36 @@ CXX=clang++-6.0 | |||
3 | FUZZ_FLAGS=-fsanitize=address,undefined -fsanitize-coverage=edge,trace-pc | 3 | FUZZ_FLAGS=-fsanitize=address,undefined -fsanitize-coverage=edge,trace-pc |
4 | FUZZ_LIBS=-lFuzzer | 4 | FUZZ_LIBS=-lFuzzer |
5 | 5 | ||
6 | CXXFLAGS=-O2 -g -Wall -Wextra -I ../../.. $(FUZZ_FLAGS) | 6 | CXXFLAGS=-O2 -g -Wall -Wextra -Wno-unused-parameter -I ../../.. $(FUZZ_FLAGS) |
7 | LDFLAGS=-L ../../.. -L ../../../openbsd-compat -g $(FUZZ_FLAGS) | 7 | LDFLAGS=-L ../../.. -L ../../../openbsd-compat -g $(FUZZ_FLAGS) |
8 | LIBS=-lssh -lopenbsd-compat -lcrypto $(FUZZ_LIBS) | 8 | LIBS=-lssh -lopenbsd-compat -lcrypto -lfido2 -lcbor $(FUZZ_LIBS) |
9 | COMMON_OBJS=ssh-sk-null.o | ||
9 | 10 | ||
10 | TARGETS=pubkey_fuzz sig_fuzz authopt_fuzz sshsig_fuzz sshsigopt_fuzz | 11 | TARGETS=pubkey_fuzz sig_fuzz authopt_fuzz sshsig_fuzz \ |
12 | sshsigopt_fuzz privkey_fuzz | ||
11 | 13 | ||
12 | all: $(TARGETS) | 14 | all: $(TARGETS) |
13 | 15 | ||
14 | .cc.o: | 16 | .cc.o: |
15 | $(CXX) $(CXXFLAGS) -c $< -o $@ | 17 | $(CXX) $(CXXFLAGS) -c $< -o $@ |
16 | 18 | ||
17 | pubkey_fuzz: pubkey_fuzz.o | 19 | pubkey_fuzz: pubkey_fuzz.o $(COMMON_OBJS) |
18 | $(CXX) -o $@ pubkey_fuzz.o $(LDFLAGS) $(LIBS) | 20 | $(CXX) -o $@ pubkey_fuzz.o $(COMMON_OBJS) $(LDFLAGS) $(LIBS) |
19 | 21 | ||
20 | sig_fuzz: sig_fuzz.o | 22 | sig_fuzz: sig_fuzz.o $(COMMON_OBJS) |
21 | $(CXX) -o $@ sig_fuzz.o $(LDFLAGS) $(LIBS) | 23 | $(CXX) -o $@ sig_fuzz.o $(COMMON_OBJS) $(LDFLAGS) $(LIBS) |
22 | 24 | ||
23 | authopt_fuzz: authopt_fuzz.o | 25 | authopt_fuzz: authopt_fuzz.o $(COMMON_OBJS) |
24 | $(CXX) -o $@ authopt_fuzz.o ../../../auth-options.o $(LDFLAGS) $(LIBS) | 26 | $(CXX) -o $@ authopt_fuzz.o $(COMMON_OBJS) ../../../auth-options.o $(LDFLAGS) $(LIBS) |
25 | 27 | ||
26 | sshsig_fuzz: sshsig_fuzz.o | 28 | sshsig_fuzz: sshsig_fuzz.o $(COMMON_OBJS) |
27 | $(CXX) -o $@ sshsig_fuzz.o ../../../sshsig.o $(LDFLAGS) $(LIBS) | 29 | $(CXX) -o $@ sshsig_fuzz.o $(COMMON_OBJS) ../../../sshsig.o $(LDFLAGS) $(LIBS) |
28 | 30 | ||
29 | sshsigopt_fuzz: sshsigopt_fuzz.o | 31 | sshsigopt_fuzz: sshsigopt_fuzz.o $(COMMON_OBJS) |
30 | $(CXX) -o $@ sshsigopt_fuzz.o ../../../sshsig.o $(LDFLAGS) $(LIBS) | 32 | $(CXX) -o $@ sshsigopt_fuzz.o $(COMMON_OBJS) ../../../sshsig.o $(LDFLAGS) $(LIBS) |
33 | |||
34 | privkey_fuzz: privkey_fuzz.o $(COMMON_OBJS) | ||
35 | $(CXX) -o $@ privkey_fuzz.o $(COMMON_OBJS) $(LDFLAGS) $(LIBS) | ||
31 | 36 | ||
32 | clean: | 37 | clean: |
33 | -rm -f *.o $(TARGETS) | 38 | -rm -f *.o $(TARGETS) |
diff --git a/regress/misc/fuzz-harness/privkey_fuzz.cc b/regress/misc/fuzz-harness/privkey_fuzz.cc new file mode 100644 index 000000000..ff0b0f776 --- /dev/null +++ b/regress/misc/fuzz-harness/privkey_fuzz.cc | |||
@@ -0,0 +1,21 @@ | |||
1 | #include <stddef.h> | ||
2 | #include <stdio.h> | ||
3 | #include <stdint.h> | ||
4 | |||
5 | extern "C" { | ||
6 | |||
7 | #include "sshkey.h" | ||
8 | #include "sshbuf.h" | ||
9 | |||
10 | int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) | ||
11 | { | ||
12 | struct sshkey *k = NULL; | ||
13 | struct sshbuf *b = sshbuf_from(data, size); | ||
14 | int r = sshkey_private_deserialize(b, &k); | ||
15 | if (r == 0) sshkey_free(k); | ||
16 | sshbuf_free(b); | ||
17 | return 0; | ||
18 | } | ||
19 | |||
20 | } // extern | ||
21 | |||
diff --git a/regress/misc/fuzz-harness/sig_fuzz.cc b/regress/misc/fuzz-harness/sig_fuzz.cc index dd1fda091..b32502ba0 100644 --- a/regress/misc/fuzz-harness/sig_fuzz.cc +++ b/regress/misc/fuzz-harness/sig_fuzz.cc | |||
@@ -31,19 +31,31 @@ int LLVMFuzzerTestOneInput(const uint8_t* sig, size_t slen) | |||
31 | static struct sshkey *ecdsa384 = generate_or_die(KEY_ECDSA, 384); | 31 | static struct sshkey *ecdsa384 = generate_or_die(KEY_ECDSA, 384); |
32 | static struct sshkey *ecdsa521 = generate_or_die(KEY_ECDSA, 521); | 32 | static struct sshkey *ecdsa521 = generate_or_die(KEY_ECDSA, 521); |
33 | #endif | 33 | #endif |
34 | struct sshkey_sig_details *details = NULL; | ||
34 | static struct sshkey *ed25519 = generate_or_die(KEY_ED25519, 0); | 35 | static struct sshkey *ed25519 = generate_or_die(KEY_ED25519, 0); |
35 | static const char *data = "If everyone started announcing his nose had " | 36 | static const char *data = "If everyone started announcing his nose had " |
36 | "run away, I don’t know how it would all end"; | 37 | "run away, I don’t know how it would all end"; |
37 | static const size_t dlen = strlen(data); | 38 | static const size_t dlen = strlen(data); |
38 | 39 | ||
39 | #ifdef WITH_OPENSSL | 40 | #ifdef WITH_OPENSSL |
40 | sshkey_verify(rsa, sig, slen, (const u_char *)data, dlen, NULL, 0); | 41 | sshkey_verify(rsa, sig, slen, (const u_char *)data, dlen, NULL, 0, &details); |
41 | sshkey_verify(dsa, sig, slen, (const u_char *)data, dlen, NULL, 0); | 42 | sshkey_sig_details_free(details); |
42 | sshkey_verify(ecdsa256, sig, slen, (const u_char *)data, dlen, NULL, 0); | 43 | details = NULL; |
43 | sshkey_verify(ecdsa384, sig, slen, (const u_char *)data, dlen, NULL, 0); | 44 | sshkey_verify(dsa, sig, slen, (const u_char *)data, dlen, NULL, 0, &details); |
44 | sshkey_verify(ecdsa521, sig, slen, (const u_char *)data, dlen, NULL, 0); | 45 | sshkey_sig_details_free(details); |
46 | details = NULL; | ||
47 | sshkey_verify(ecdsa256, sig, slen, (const u_char *)data, dlen, NULL, 0, &details); | ||
48 | sshkey_sig_details_free(details); | ||
49 | details = NULL; | ||
50 | sshkey_verify(ecdsa384, sig, slen, (const u_char *)data, dlen, NULL, 0, &details); | ||
51 | sshkey_sig_details_free(details); | ||
52 | details = NULL; | ||
53 | sshkey_verify(ecdsa521, sig, slen, (const u_char *)data, dlen, NULL, 0, &details); | ||
54 | sshkey_sig_details_free(details); | ||
55 | details = NULL; | ||
45 | #endif | 56 | #endif |
46 | sshkey_verify(ed25519, sig, slen, (const u_char *)data, dlen, NULL, 0); | 57 | sshkey_verify(ed25519, sig, slen, (const u_char *)data, dlen, NULL, 0, &details); |
58 | sshkey_sig_details_free(details); | ||
47 | return 0; | 59 | return 0; |
48 | } | 60 | } |
49 | 61 | ||
diff --git a/regress/misc/fuzz-harness/ssh-sk-null.cc b/regress/misc/fuzz-harness/ssh-sk-null.cc new file mode 100644 index 000000000..199af1121 --- /dev/null +++ b/regress/misc/fuzz-harness/ssh-sk-null.cc | |||
@@ -0,0 +1,51 @@ | |||
1 | /* $OpenBSD$ */ | ||
2 | /* | ||
3 | * Copyright (c) 2019 Google LLC | ||
4 | * | ||
5 | * Permission to use, copy, modify, and distribute this software for any | ||
6 | * purpose with or without fee is hereby granted, provided that the above | ||
7 | * copyright notice and this permission notice appear in all copies. | ||
8 | * | ||
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
16 | */ | ||
17 | |||
18 | extern "C" { | ||
19 | |||
20 | #include "includes.h" | ||
21 | |||
22 | #include <sys/types.h> | ||
23 | |||
24 | #include "ssherr.h" | ||
25 | #include "ssh-sk.h" | ||
26 | |||
27 | int | ||
28 | sshsk_enroll(int type, const char *provider_path, const char *device, | ||
29 | const char *application, const char *userid, uint8_t flags, | ||
30 | const char *pin, struct sshbuf *challenge_buf, | ||
31 | struct sshkey **keyp, struct sshbuf *attest) | ||
32 | { | ||
33 | return SSH_ERR_FEATURE_UNSUPPORTED; | ||
34 | } | ||
35 | |||
36 | int | ||
37 | sshsk_sign(const char *provider_path, struct sshkey *key, | ||
38 | u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, | ||
39 | u_int compat, const char *pin) | ||
40 | { | ||
41 | return SSH_ERR_FEATURE_UNSUPPORTED; | ||
42 | } | ||
43 | |||
44 | int | ||
45 | sshsk_load_resident(const char *provider_path, const char *device, | ||
46 | const char *pin, struct sshkey ***keysp, size_t *nkeysp) | ||
47 | { | ||
48 | return SSH_ERR_FEATURE_UNSUPPORTED; | ||
49 | } | ||
50 | |||
51 | }; | ||
diff --git a/regress/misc/fuzz-harness/sshsig_fuzz.cc b/regress/misc/fuzz-harness/sshsig_fuzz.cc index fe09ccb87..02211a096 100644 --- a/regress/misc/fuzz-harness/sshsig_fuzz.cc +++ b/regress/misc/fuzz-harness/sshsig_fuzz.cc | |||
@@ -22,10 +22,12 @@ int LLVMFuzzerTestOneInput(const uint8_t* sig, size_t slen) | |||
22 | struct sshbuf *signature = sshbuf_from(sig, slen); | 22 | struct sshbuf *signature = sshbuf_from(sig, slen); |
23 | struct sshbuf *message = sshbuf_from(data, strlen(data)); | 23 | struct sshbuf *message = sshbuf_from(data, strlen(data)); |
24 | struct sshkey *k = NULL; | 24 | struct sshkey *k = NULL; |
25 | struct sshkey_sig_details *details = NULL; | ||
25 | extern char *__progname; | 26 | extern char *__progname; |
26 | 27 | ||
27 | log_init(__progname, SYSLOG_LEVEL_QUIET, SYSLOG_FACILITY_USER, 1); | 28 | log_init(__progname, SYSLOG_LEVEL_QUIET, SYSLOG_FACILITY_USER, 1); |
28 | sshsig_verifyb(signature, message, "castle", &k); | 29 | sshsig_verifyb(signature, message, "castle", &k, &details); |
30 | sshkey_sig_details_free(details); | ||
29 | sshkey_free(k); | 31 | sshkey_free(k); |
30 | sshbuf_free(signature); | 32 | sshbuf_free(signature); |
31 | sshbuf_free(message); | 33 | sshbuf_free(message); |
diff --git a/regress/misc/kexfuzz/Makefile b/regress/misc/kexfuzz/Makefile index 20802cb87..9eb86931c 100644 --- a/regress/misc/kexfuzz/Makefile +++ b/regress/misc/kexfuzz/Makefile | |||
@@ -1,4 +1,4 @@ | |||
1 | # $OpenBSD: Makefile,v 1.4 2019/01/21 12:50:12 djm Exp $ | 1 | # $OpenBSD: Makefile,v 1.7 2020/01/26 00:09:50 djm Exp $ |
2 | 2 | ||
3 | .include <bsd.own.mk> | 3 | .include <bsd.own.mk> |
4 | .include <bsd.obj.mk> | 4 | .include <bsd.obj.mk> |
@@ -20,6 +20,7 @@ SRCS+=ssherr.c uidswap.c cleanup.c xmalloc.c match.c krl.c fatal.c | |||
20 | SRCS+=addrmatch.c bitmap.c packet.c dispatch.c canohost.c ssh_api.c | 20 | SRCS+=addrmatch.c bitmap.c packet.c dispatch.c canohost.c ssh_api.c |
21 | SRCS+=compat.c ed25519.c hash.c ge25519.c fe25519.c sc25519.c verify.c | 21 | SRCS+=compat.c ed25519.c hash.c ge25519.c fe25519.c sc25519.c verify.c |
22 | SRCS+=cipher-chachapoly.c chacha.c poly1305.c | 22 | SRCS+=cipher-chachapoly.c chacha.c poly1305.c |
23 | SRCS+=sshbuf-io.c ssh-ecdsa-sk.c ssh-ed25519-sk.c msg.c ssh-sk-client.c | ||
23 | 24 | ||
24 | SRCS+= kex.c | 25 | SRCS+= kex.c |
25 | SRCS+= dh.c | 26 | SRCS+= dh.c |
@@ -50,6 +51,9 @@ SSH1= no | |||
50 | CFLAGS+= -DWITH_SSH1 | 51 | CFLAGS+= -DWITH_SSH1 |
51 | .endif | 52 | .endif |
52 | 53 | ||
54 | LDADD+= -lfido2 -lcbor -lusbhid | ||
55 | DPADD+= ${LIBFIDO2} ${LIBCBOR} ${LIBUSBHID} | ||
56 | |||
53 | # enable warnings | 57 | # enable warnings |
54 | WARNINGS=Yes | 58 | WARNINGS=Yes |
55 | 59 | ||
diff --git a/regress/misc/kexfuzz/kexfuzz.c b/regress/misc/kexfuzz/kexfuzz.c index 7051e87b1..56697c918 100644 --- a/regress/misc/kexfuzz/kexfuzz.c +++ b/regress/misc/kexfuzz/kexfuzz.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: kexfuzz.c,v 1.5 2019/01/21 12:50:12 djm Exp $ */ | 1 | /* $OpenBSD: kexfuzz.c,v 1.6 2020/01/26 00:09:50 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Fuzz harness for KEX code | 3 | * Fuzz harness for KEX code |
4 | * | 4 | * |
@@ -424,12 +424,8 @@ main(int argc, char **argv) | |||
424 | if (packet_index == -1 || direction == -1 || data_path == NULL) | 424 | if (packet_index == -1 || direction == -1 || data_path == NULL) |
425 | badusage("Replace (-r) mode must specify direction " | 425 | badusage("Replace (-r) mode must specify direction " |
426 | "(-D) packet index (-i) and data path (-f)"); | 426 | "(-D) packet index (-i) and data path (-f)"); |
427 | if ((fd = open(data_path, O_RDONLY)) == -1) | 427 | if ((r = sshbuf_load_file(data_path, &replace_data)) != 0) |
428 | err(1, "open %s", data_path); | ||
429 | replace_data = sshbuf_new(); | ||
430 | if ((r = sshkey_load_file(fd, replace_data)) != 0) | ||
431 | errx(1, "read %s: %s", data_path, ssh_err(r)); | 428 | errx(1, "read %s: %s", data_path, ssh_err(r)); |
432 | close(fd); | ||
433 | } | 429 | } |
434 | 430 | ||
435 | /* Dump mode */ | 431 | /* Dump mode */ |
diff --git a/regress/misc/sk-dummy/Makefile b/regress/misc/sk-dummy/Makefile new file mode 100644 index 000000000..29e313c82 --- /dev/null +++ b/regress/misc/sk-dummy/Makefile | |||
@@ -0,0 +1,66 @@ | |||
1 | # $OpenBSD: Makefile,v 1.2 2019/11/29 00:13:29 djm Exp $ | ||
2 | |||
3 | .include <bsd.own.mk> | ||
4 | .include <bsd.obj.mk> | ||
5 | |||
6 | PROG= sk-dummy.so | ||
7 | NOMAN= | ||
8 | |||
9 | SSHREL=../../../../../usr.bin/ssh | ||
10 | .PATH: ${.CURDIR}/${SSHREL} | ||
11 | |||
12 | SRCS=sk-dummy.c | ||
13 | # From usr.bin/ssh | ||
14 | SRCS+=ed25519.c hash.c ge25519.c fe25519.c sc25519.c verify.c | ||
15 | OPENSSL?= yes | ||
16 | |||
17 | CFLAGS+= -fPIC | ||
18 | |||
19 | .if (${OPENSSL:L} == "yes") | ||
20 | CFLAGS+= -DWITH_OPENSSL | ||
21 | .endif | ||
22 | |||
23 | # enable warnings | ||
24 | WARNINGS=Yes | ||
25 | |||
26 | DEBUG=-g | ||
27 | CFLAGS+= -fstack-protector-all | ||
28 | CDIAGFLAGS= -Wall | ||
29 | CDIAGFLAGS+= -Wextra | ||
30 | CDIAGFLAGS+= -Werror | ||
31 | CDIAGFLAGS+= -Wchar-subscripts | ||
32 | CDIAGFLAGS+= -Wcomment | ||
33 | CDIAGFLAGS+= -Wformat | ||
34 | CDIAGFLAGS+= -Wformat-security | ||
35 | CDIAGFLAGS+= -Wimplicit | ||
36 | CDIAGFLAGS+= -Winline | ||
37 | CDIAGFLAGS+= -Wmissing-declarations | ||
38 | CDIAGFLAGS+= -Wmissing-prototypes | ||
39 | CDIAGFLAGS+= -Wparentheses | ||
40 | CDIAGFLAGS+= -Wpointer-arith | ||
41 | CDIAGFLAGS+= -Wreturn-type | ||
42 | CDIAGFLAGS+= -Wshadow | ||
43 | CDIAGFLAGS+= -Wsign-compare | ||
44 | CDIAGFLAGS+= -Wstrict-aliasing | ||
45 | CDIAGFLAGS+= -Wstrict-prototypes | ||
46 | CDIAGFLAGS+= -Wswitch | ||
47 | CDIAGFLAGS+= -Wtrigraphs | ||
48 | CDIAGFLAGS+= -Wuninitialized | ||
49 | CDIAGFLAGS+= -Wunused | ||
50 | CDIAGFLAGS+= -Wno-unused-parameter | ||
51 | .if ${COMPILER_VERSION:L} != "gcc3" | ||
52 | CDIAGFLAGS+= -Wold-style-definition | ||
53 | .endif | ||
54 | |||
55 | CFLAGS+=-I${.CURDIR}/${SSHREL} | ||
56 | |||
57 | .if (${OPENSSL:L} == "yes") | ||
58 | LDADD+= -lcrypto | ||
59 | DPADD+= ${LIBCRYPTO} | ||
60 | .endif | ||
61 | |||
62 | $(PROG): $(OBJS) | ||
63 | $(CC) $(LDFLAGS) -shared -o $@ $(OBJS) $(LDADD) | ||
64 | |||
65 | .include <bsd.prog.mk> | ||
66 | |||
diff --git a/regress/misc/sk-dummy/fatal.c b/regress/misc/sk-dummy/fatal.c new file mode 100644 index 000000000..7cdc74b97 --- /dev/null +++ b/regress/misc/sk-dummy/fatal.c | |||
@@ -0,0 +1,20 @@ | |||
1 | /* public domain */ | ||
2 | |||
3 | #include <stdlib.h> | ||
4 | #include <stdio.h> | ||
5 | #include <stdarg.h> | ||
6 | #include <unistd.h> | ||
7 | |||
8 | void fatal(char *fmt, ...); | ||
9 | |||
10 | void | ||
11 | fatal(char *fmt, ...) | ||
12 | { | ||
13 | va_list ap; | ||
14 | |||
15 | va_start(ap, fmt); | ||
16 | vfprintf(stderr, fmt, ap); | ||
17 | va_end(ap); | ||
18 | fputc('\n', stderr); | ||
19 | _exit(1); | ||
20 | } | ||
diff --git a/regress/misc/sk-dummy/sk-dummy.c b/regress/misc/sk-dummy/sk-dummy.c new file mode 100644 index 000000000..dca158ded --- /dev/null +++ b/regress/misc/sk-dummy/sk-dummy.c | |||
@@ -0,0 +1,526 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2019 Markus Friedl | ||
3 | * | ||
4 | * Permission to use, copy, modify, and distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #include "includes.h" | ||
18 | |||
19 | #ifdef HAVE_STDINT_H | ||
20 | #include <stdint.h> | ||
21 | #endif | ||
22 | #include <stdlib.h> | ||
23 | #include <string.h> | ||
24 | #include <stdio.h> | ||
25 | #include <stddef.h> | ||
26 | #include <stdarg.h> | ||
27 | |||
28 | #include "crypto_api.h" | ||
29 | #include "sk-api.h" | ||
30 | |||
31 | #include <openssl/opensslv.h> | ||
32 | #include <openssl/crypto.h> | ||
33 | #include <openssl/evp.h> | ||
34 | #include <openssl/bn.h> | ||
35 | #include <openssl/ec.h> | ||
36 | #include <openssl/ecdsa.h> | ||
37 | #include <openssl/pem.h> | ||
38 | |||
39 | /* #define SK_DEBUG 1 */ | ||
40 | |||
41 | /* Compatibility with OpenSSH 1.0.x */ | ||
42 | #if (OPENSSL_VERSION_NUMBER < 0x10100000L) | ||
43 | #define ECDSA_SIG_get0(sig, pr, ps) \ | ||
44 | do { \ | ||
45 | (*pr) = sig->r; \ | ||
46 | (*ps) = sig->s; \ | ||
47 | } while (0) | ||
48 | #endif | ||
49 | |||
50 | #if SSH_SK_VERSION_MAJOR != 0x00040000 | ||
51 | # error SK API has changed, sk-dummy.c needs an update | ||
52 | #endif | ||
53 | |||
54 | static void skdebug(const char *func, const char *fmt, ...) | ||
55 | __attribute__((__format__ (printf, 2, 3))); | ||
56 | |||
57 | static void | ||
58 | skdebug(const char *func, const char *fmt, ...) | ||
59 | { | ||
60 | #if defined(SK_DEBUG) | ||
61 | va_list ap; | ||
62 | |||
63 | va_start(ap, fmt); | ||
64 | fprintf(stderr, "sk-dummy %s: ", func); | ||
65 | vfprintf(stderr, fmt, ap); | ||
66 | fputc('\n', stderr); | ||
67 | va_end(ap); | ||
68 | #else | ||
69 | (void)func; /* XXX */ | ||
70 | (void)fmt; /* XXX */ | ||
71 | #endif | ||
72 | } | ||
73 | |||
74 | uint32_t | ||
75 | sk_api_version(void) | ||
76 | { | ||
77 | return SSH_SK_VERSION_MAJOR; | ||
78 | } | ||
79 | |||
80 | static int | ||
81 | pack_key_ecdsa(struct sk_enroll_response *response) | ||
82 | { | ||
83 | #ifdef OPENSSL_HAS_ECC | ||
84 | EC_KEY *key = NULL; | ||
85 | const EC_GROUP *g; | ||
86 | const EC_POINT *q; | ||
87 | int ret = -1; | ||
88 | long privlen; | ||
89 | BIO *bio = NULL; | ||
90 | char *privptr; | ||
91 | |||
92 | response->public_key = NULL; | ||
93 | response->public_key_len = 0; | ||
94 | response->key_handle = NULL; | ||
95 | response->key_handle_len = 0; | ||
96 | |||
97 | if ((key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL) { | ||
98 | skdebug(__func__, "EC_KEY_new_by_curve_name"); | ||
99 | goto out; | ||
100 | } | ||
101 | if (EC_KEY_generate_key(key) != 1) { | ||
102 | skdebug(__func__, "EC_KEY_generate_key"); | ||
103 | goto out; | ||
104 | } | ||
105 | EC_KEY_set_asn1_flag(key, OPENSSL_EC_NAMED_CURVE); | ||
106 | if ((bio = BIO_new(BIO_s_mem())) == NULL || | ||
107 | (g = EC_KEY_get0_group(key)) == NULL || | ||
108 | (q = EC_KEY_get0_public_key(key)) == NULL) { | ||
109 | skdebug(__func__, "couldn't get key parameters"); | ||
110 | goto out; | ||
111 | } | ||
112 | response->public_key_len = EC_POINT_point2oct(g, q, | ||
113 | POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); | ||
114 | if (response->public_key_len == 0 || response->public_key_len > 2048) { | ||
115 | skdebug(__func__, "bad pubkey length %zu", | ||
116 | response->public_key_len); | ||
117 | goto out; | ||
118 | } | ||
119 | if ((response->public_key = malloc(response->public_key_len)) == NULL) { | ||
120 | skdebug(__func__, "malloc pubkey failed"); | ||
121 | goto out; | ||
122 | } | ||
123 | if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED, | ||
124 | response->public_key, response->public_key_len, NULL) == 0) { | ||
125 | skdebug(__func__, "EC_POINT_point2oct failed"); | ||
126 | goto out; | ||
127 | } | ||
128 | /* Key handle contains PEM encoded private key */ | ||
129 | if (!PEM_write_bio_ECPrivateKey(bio, key, NULL, NULL, 0, NULL, NULL)) { | ||
130 | skdebug(__func__, "PEM_write_bio_ECPrivateKey failed"); | ||
131 | goto out; | ||
132 | } | ||
133 | if ((privlen = BIO_get_mem_data(bio, &privptr)) <= 0) { | ||
134 | skdebug(__func__, "BIO_get_mem_data failed"); | ||
135 | goto out; | ||
136 | } | ||
137 | if ((response->key_handle = malloc(privlen)) == NULL) { | ||
138 | skdebug(__func__, "malloc key_handle failed"); | ||
139 | goto out; | ||
140 | } | ||
141 | response->key_handle_len = (size_t)privlen; | ||
142 | memcpy(response->key_handle, privptr, response->key_handle_len); | ||
143 | /* success */ | ||
144 | ret = 0; | ||
145 | out: | ||
146 | if (ret != 0) { | ||
147 | if (response->public_key != NULL) { | ||
148 | memset(response->public_key, 0, | ||
149 | response->public_key_len); | ||
150 | free(response->public_key); | ||
151 | response->public_key = NULL; | ||
152 | } | ||
153 | if (response->key_handle != NULL) { | ||
154 | memset(response->key_handle, 0, | ||
155 | response->key_handle_len); | ||
156 | free(response->key_handle); | ||
157 | response->key_handle = NULL; | ||
158 | } | ||
159 | } | ||
160 | BIO_free(bio); | ||
161 | EC_KEY_free(key); | ||
162 | return ret; | ||
163 | #else | ||
164 | return -1; | ||
165 | #endif | ||
166 | } | ||
167 | |||
168 | static int | ||
169 | pack_key_ed25519(struct sk_enroll_response *response) | ||
170 | { | ||
171 | int ret = -1; | ||
172 | u_char pk[crypto_sign_ed25519_PUBLICKEYBYTES]; | ||
173 | u_char sk[crypto_sign_ed25519_SECRETKEYBYTES]; | ||
174 | |||
175 | response->public_key = NULL; | ||
176 | response->public_key_len = 0; | ||
177 | response->key_handle = NULL; | ||
178 | response->key_handle_len = 0; | ||
179 | |||
180 | memset(pk, 0, sizeof(pk)); | ||
181 | memset(sk, 0, sizeof(sk)); | ||
182 | crypto_sign_ed25519_keypair(pk, sk); | ||
183 | |||
184 | response->public_key_len = sizeof(pk); | ||
185 | if ((response->public_key = malloc(response->public_key_len)) == NULL) { | ||
186 | skdebug(__func__, "malloc pubkey failed"); | ||
187 | goto out; | ||
188 | } | ||
189 | memcpy(response->public_key, pk, sizeof(pk)); | ||
190 | /* Key handle contains sk */ | ||
191 | response->key_handle_len = sizeof(sk); | ||
192 | if ((response->key_handle = malloc(response->key_handle_len)) == NULL) { | ||
193 | skdebug(__func__, "malloc key_handle failed"); | ||
194 | goto out; | ||
195 | } | ||
196 | memcpy(response->key_handle, sk, sizeof(sk)); | ||
197 | /* success */ | ||
198 | ret = 0; | ||
199 | out: | ||
200 | if (ret != 0) | ||
201 | free(response->public_key); | ||
202 | return ret; | ||
203 | } | ||
204 | |||
205 | static int | ||
206 | check_options(struct sk_option **options) | ||
207 | { | ||
208 | size_t i; | ||
209 | |||
210 | if (options == NULL) | ||
211 | return 0; | ||
212 | for (i = 0; options[i] != NULL; i++) { | ||
213 | skdebug(__func__, "requested unsupported option %s", | ||
214 | options[i]->name); | ||
215 | if (options[i]->required) { | ||
216 | skdebug(__func__, "unknown required option"); | ||
217 | return -1; | ||
218 | } | ||
219 | } | ||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | int | ||
224 | sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, | ||
225 | const char *application, uint8_t flags, const char *pin, | ||
226 | struct sk_option **options, struct sk_enroll_response **enroll_response) | ||
227 | { | ||
228 | struct sk_enroll_response *response = NULL; | ||
229 | int ret = SSH_SK_ERR_GENERAL; | ||
230 | |||
231 | (void)flags; /* XXX; unused */ | ||
232 | |||
233 | if (enroll_response == NULL) { | ||
234 | skdebug(__func__, "enroll_response == NULL"); | ||
235 | goto out; | ||
236 | } | ||
237 | *enroll_response = NULL; | ||
238 | if (check_options(options) != 0) | ||
239 | goto out; /* error already logged */ | ||
240 | if ((response = calloc(1, sizeof(*response))) == NULL) { | ||
241 | skdebug(__func__, "calloc response failed"); | ||
242 | goto out; | ||
243 | } | ||
244 | switch(alg) { | ||
245 | case SSH_SK_ECDSA: | ||
246 | if (pack_key_ecdsa(response) != 0) | ||
247 | goto out; | ||
248 | break; | ||
249 | case SSH_SK_ED25519: | ||
250 | if (pack_key_ed25519(response) != 0) | ||
251 | goto out; | ||
252 | break; | ||
253 | default: | ||
254 | skdebug(__func__, "unsupported key type %d", alg); | ||
255 | return -1; | ||
256 | } | ||
257 | /* Have to return something here */ | ||
258 | if ((response->signature = calloc(1, 1)) == NULL) { | ||
259 | skdebug(__func__, "calloc signature failed"); | ||
260 | goto out; | ||
261 | } | ||
262 | response->signature_len = 0; | ||
263 | |||
264 | *enroll_response = response; | ||
265 | response = NULL; | ||
266 | ret = 0; | ||
267 | out: | ||
268 | if (response != NULL) { | ||
269 | free(response->public_key); | ||
270 | free(response->key_handle); | ||
271 | free(response->signature); | ||
272 | free(response->attestation_cert); | ||
273 | free(response); | ||
274 | } | ||
275 | return ret; | ||
276 | } | ||
277 | |||
278 | static void | ||
279 | dump(const char *preamble, const void *sv, size_t l) | ||
280 | { | ||
281 | #ifdef SK_DEBUG | ||
282 | const u_char *s = (const u_char *)sv; | ||
283 | size_t i; | ||
284 | |||
285 | fprintf(stderr, "%s (len %zu):\n", preamble, l); | ||
286 | for (i = 0; i < l; i++) { | ||
287 | if (i % 16 == 0) | ||
288 | fprintf(stderr, "%04zu: ", i); | ||
289 | fprintf(stderr, "%02x", s[i]); | ||
290 | if (i % 16 == 15 || i == l - 1) | ||
291 | fprintf(stderr, "\n"); | ||
292 | } | ||
293 | #endif | ||
294 | } | ||
295 | |||
296 | static int | ||
297 | sig_ecdsa(const uint8_t *message, size_t message_len, | ||
298 | const char *application, uint32_t counter, uint8_t flags, | ||
299 | const uint8_t *key_handle, size_t key_handle_len, | ||
300 | struct sk_sign_response *response) | ||
301 | { | ||
302 | #ifdef OPENSSL_HAS_ECC | ||
303 | ECDSA_SIG *sig = NULL; | ||
304 | const BIGNUM *sig_r, *sig_s; | ||
305 | int ret = -1; | ||
306 | BIO *bio = NULL; | ||
307 | EVP_PKEY *pk = NULL; | ||
308 | EC_KEY *ec = NULL; | ||
309 | SHA256_CTX ctx; | ||
310 | uint8_t apphash[SHA256_DIGEST_LENGTH]; | ||
311 | uint8_t sighash[SHA256_DIGEST_LENGTH]; | ||
312 | uint8_t countbuf[4]; | ||
313 | |||
314 | /* Decode EC_KEY from key handle */ | ||
315 | if ((bio = BIO_new(BIO_s_mem())) == NULL || | ||
316 | BIO_write(bio, key_handle, key_handle_len) != (int)key_handle_len) { | ||
317 | skdebug(__func__, "BIO setup failed"); | ||
318 | goto out; | ||
319 | } | ||
320 | if ((pk = PEM_read_bio_PrivateKey(bio, NULL, NULL, "")) == NULL) { | ||
321 | skdebug(__func__, "PEM_read_bio_PrivateKey failed"); | ||
322 | goto out; | ||
323 | } | ||
324 | if (EVP_PKEY_base_id(pk) != EVP_PKEY_EC) { | ||
325 | skdebug(__func__, "Not an EC key: %d", EVP_PKEY_base_id(pk)); | ||
326 | goto out; | ||
327 | } | ||
328 | if ((ec = EVP_PKEY_get1_EC_KEY(pk)) == NULL) { | ||
329 | skdebug(__func__, "EVP_PKEY_get1_EC_KEY failed"); | ||
330 | goto out; | ||
331 | } | ||
332 | /* Expect message to be pre-hashed */ | ||
333 | if (message_len != SHA256_DIGEST_LENGTH) { | ||
334 | skdebug(__func__, "bad message len %zu", message_len); | ||
335 | goto out; | ||
336 | } | ||
337 | /* Prepare data to be signed */ | ||
338 | dump("message", message, message_len); | ||
339 | SHA256_Init(&ctx); | ||
340 | SHA256_Update(&ctx, application, strlen(application)); | ||
341 | SHA256_Final(apphash, &ctx); | ||
342 | dump("apphash", apphash, sizeof(apphash)); | ||
343 | countbuf[0] = (counter >> 24) & 0xff; | ||
344 | countbuf[1] = (counter >> 16) & 0xff; | ||
345 | countbuf[2] = (counter >> 8) & 0xff; | ||
346 | countbuf[3] = counter & 0xff; | ||
347 | dump("countbuf", countbuf, sizeof(countbuf)); | ||
348 | dump("flags", &flags, sizeof(flags)); | ||
349 | SHA256_Init(&ctx); | ||
350 | SHA256_Update(&ctx, apphash, sizeof(apphash)); | ||
351 | SHA256_Update(&ctx, &flags, sizeof(flags)); | ||
352 | SHA256_Update(&ctx, countbuf, sizeof(countbuf)); | ||
353 | SHA256_Update(&ctx, message, message_len); | ||
354 | SHA256_Final(sighash, &ctx); | ||
355 | dump("sighash", sighash, sizeof(sighash)); | ||
356 | /* create and encode signature */ | ||
357 | if ((sig = ECDSA_do_sign(sighash, sizeof(sighash), ec)) == NULL) { | ||
358 | skdebug(__func__, "ECDSA_do_sign failed"); | ||
359 | goto out; | ||
360 | } | ||
361 | ECDSA_SIG_get0(sig, &sig_r, &sig_s); | ||
362 | response->sig_r_len = BN_num_bytes(sig_r); | ||
363 | response->sig_s_len = BN_num_bytes(sig_s); | ||
364 | if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL || | ||
365 | (response->sig_s = calloc(1, response->sig_s_len)) == NULL) { | ||
366 | skdebug(__func__, "calloc signature failed"); | ||
367 | goto out; | ||
368 | } | ||
369 | BN_bn2bin(sig_r, response->sig_r); | ||
370 | BN_bn2bin(sig_s, response->sig_s); | ||
371 | ret = 0; | ||
372 | out: | ||
373 | explicit_bzero(&ctx, sizeof(ctx)); | ||
374 | explicit_bzero(&apphash, sizeof(apphash)); | ||
375 | explicit_bzero(&sighash, sizeof(sighash)); | ||
376 | ECDSA_SIG_free(sig); | ||
377 | if (ret != 0) { | ||
378 | free(response->sig_r); | ||
379 | free(response->sig_s); | ||
380 | response->sig_r = NULL; | ||
381 | response->sig_s = NULL; | ||
382 | } | ||
383 | BIO_free(bio); | ||
384 | EC_KEY_free(ec); | ||
385 | EVP_PKEY_free(pk); | ||
386 | return ret; | ||
387 | #else | ||
388 | return -1; | ||
389 | #endif | ||
390 | } | ||
391 | |||
392 | static int | ||
393 | sig_ed25519(const uint8_t *message, size_t message_len, | ||
394 | const char *application, uint32_t counter, uint8_t flags, | ||
395 | const uint8_t *key_handle, size_t key_handle_len, | ||
396 | struct sk_sign_response *response) | ||
397 | { | ||
398 | size_t o; | ||
399 | int ret = -1; | ||
400 | SHA256_CTX ctx; | ||
401 | uint8_t apphash[SHA256_DIGEST_LENGTH]; | ||
402 | uint8_t signbuf[sizeof(apphash) + sizeof(flags) + | ||
403 | sizeof(counter) + SHA256_DIGEST_LENGTH]; | ||
404 | uint8_t sig[crypto_sign_ed25519_BYTES + sizeof(signbuf)]; | ||
405 | unsigned long long smlen; | ||
406 | |||
407 | if (key_handle_len != crypto_sign_ed25519_SECRETKEYBYTES) { | ||
408 | skdebug(__func__, "bad key handle length %zu", key_handle_len); | ||
409 | goto out; | ||
410 | } | ||
411 | /* Expect message to be pre-hashed */ | ||
412 | if (message_len != SHA256_DIGEST_LENGTH) { | ||
413 | skdebug(__func__, "bad message len %zu", message_len); | ||
414 | goto out; | ||
415 | } | ||
416 | /* Prepare data to be signed */ | ||
417 | dump("message", message, message_len); | ||
418 | SHA256_Init(&ctx); | ||
419 | SHA256_Update(&ctx, application, strlen(application)); | ||
420 | SHA256_Final(apphash, &ctx); | ||
421 | dump("apphash", apphash, sizeof(apphash)); | ||
422 | |||
423 | memcpy(signbuf, apphash, sizeof(apphash)); | ||
424 | o = sizeof(apphash); | ||
425 | signbuf[o++] = flags; | ||
426 | signbuf[o++] = (counter >> 24) & 0xff; | ||
427 | signbuf[o++] = (counter >> 16) & 0xff; | ||
428 | signbuf[o++] = (counter >> 8) & 0xff; | ||
429 | signbuf[o++] = counter & 0xff; | ||
430 | memcpy(signbuf + o, message, message_len); | ||
431 | o += message_len; | ||
432 | if (o != sizeof(signbuf)) { | ||
433 | skdebug(__func__, "bad sign buf len %zu, expected %zu", | ||
434 | o, sizeof(signbuf)); | ||
435 | goto out; | ||
436 | } | ||
437 | dump("signbuf", signbuf, sizeof(signbuf)); | ||
438 | /* create and encode signature */ | ||
439 | smlen = sizeof(signbuf); | ||
440 | if (crypto_sign_ed25519(sig, &smlen, signbuf, sizeof(signbuf), | ||
441 | key_handle) != 0) { | ||
442 | skdebug(__func__, "crypto_sign_ed25519 failed"); | ||
443 | goto out; | ||
444 | } | ||
445 | if (smlen <= sizeof(signbuf)) { | ||
446 | skdebug(__func__, "bad sign smlen %llu, expected min %zu", | ||
447 | smlen, sizeof(signbuf) + 1); | ||
448 | goto out; | ||
449 | } | ||
450 | response->sig_r_len = (size_t)(smlen - sizeof(signbuf)); | ||
451 | if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) { | ||
452 | skdebug(__func__, "calloc signature failed"); | ||
453 | goto out; | ||
454 | } | ||
455 | memcpy(response->sig_r, sig, response->sig_r_len); | ||
456 | dump("sig_r", response->sig_r, response->sig_r_len); | ||
457 | ret = 0; | ||
458 | out: | ||
459 | explicit_bzero(&ctx, sizeof(ctx)); | ||
460 | explicit_bzero(&apphash, sizeof(apphash)); | ||
461 | explicit_bzero(&signbuf, sizeof(signbuf)); | ||
462 | explicit_bzero(&sig, sizeof(sig)); | ||
463 | if (ret != 0) { | ||
464 | free(response->sig_r); | ||
465 | response->sig_r = NULL; | ||
466 | } | ||
467 | return ret; | ||
468 | } | ||
469 | |||
470 | int | ||
471 | sk_sign(uint32_t alg, const uint8_t *message, size_t message_len, | ||
472 | const char *application, const uint8_t *key_handle, size_t key_handle_len, | ||
473 | uint8_t flags, const char *pin, struct sk_option **options, | ||
474 | struct sk_sign_response **sign_response) | ||
475 | { | ||
476 | struct sk_sign_response *response = NULL; | ||
477 | int ret = SSH_SK_ERR_GENERAL; | ||
478 | |||
479 | if (sign_response == NULL) { | ||
480 | skdebug(__func__, "sign_response == NULL"); | ||
481 | goto out; | ||
482 | } | ||
483 | *sign_response = NULL; | ||
484 | if (check_options(options) != 0) | ||
485 | goto out; /* error already logged */ | ||
486 | if ((response = calloc(1, sizeof(*response))) == NULL) { | ||
487 | skdebug(__func__, "calloc response failed"); | ||
488 | goto out; | ||
489 | } | ||
490 | response->flags = flags; | ||
491 | response->counter = 0x12345678; | ||
492 | switch(alg) { | ||
493 | case SSH_SK_ECDSA: | ||
494 | if (sig_ecdsa(message, message_len, application, | ||
495 | response->counter, flags, key_handle, key_handle_len, | ||
496 | response) != 0) | ||
497 | goto out; | ||
498 | break; | ||
499 | case SSH_SK_ED25519: | ||
500 | if (sig_ed25519(message, message_len, application, | ||
501 | response->counter, flags, key_handle, key_handle_len, | ||
502 | response) != 0) | ||
503 | goto out; | ||
504 | break; | ||
505 | default: | ||
506 | skdebug(__func__, "unsupported key type %d", alg); | ||
507 | return -1; | ||
508 | } | ||
509 | *sign_response = response; | ||
510 | response = NULL; | ||
511 | ret = 0; | ||
512 | out: | ||
513 | if (response != NULL) { | ||
514 | free(response->sig_r); | ||
515 | free(response->sig_s); | ||
516 | free(response); | ||
517 | } | ||
518 | return ret; | ||
519 | } | ||
520 | |||
521 | int | ||
522 | sk_load_resident_keys(const char *pin, struct sk_option **options, | ||
523 | struct sk_resident_key ***rks, size_t *nrks) | ||
524 | { | ||
525 | return SSH_SK_ERR_UNSUPPORTED; | ||
526 | } | ||