krb5 commit: Add ASN.1 encoders and decoders for SPAKE types

Greg Hudson ghudson at mit.edu
Sun Mar 4 13:07:27 EST 2018


https://github.com/krb5/krb5/commit/78a09d95dff6915da4079bc611f4bb95f6a95f70
commit 78a09d95dff6915da4079bc611f4bb95f6a95f70
Author: Greg Hudson <ghudson at mit.edu>
Date:   Sat Jun 13 16:04:53 2015 -0400

    Add ASN.1 encoders and decoders for SPAKE types
    
    Add a new internal header k5-spake.h.  Add ASN.1 encoder and decoder
    functions and an internal free function for SPAKE types.  Add ASN.1
    tests and asn1c test vectors the new types.
    
    The additions to to make-vectors.c use C99 designated initializers in
    order to initialize unions.  This is okay since make-vectors.c is only
    compiled as part of "make test-vectors" and not as part of the regular
    build.

 src/include/k5-spake.h               |  107 ++++++++++++++++++++++++++++++++++
 src/lib/krb5/asn.1/asn1_k_encode.c   |   52 ++++++++++++++++-
 src/lib/krb5/krb/kfree.c             |   40 +++++++++++++
 src/lib/krb5/libkrb5.exports         |    6 ++
 src/tests/asn.1/Makefile.in          |    2 +-
 src/tests/asn.1/krb5_decode_test.c   |   37 ++++++++++++
 src/tests/asn.1/krb5_encode_test.c   |   29 +++++++++
 src/tests/asn.1/ktest.c              |   97 ++++++++++++++++++++++++++++++
 src/tests/asn.1/ktest.h              |    9 +++
 src/tests/asn.1/ktest_equal.c        |   49 +++++++++++++++
 src/tests/asn.1/ktest_equal.h        |    6 ++
 src/tests/asn.1/make-vectors.c       |   56 ++++++++++++++++++
 src/tests/asn.1/reference_encode.out |    6 ++
 src/tests/asn.1/spake.asn1           |   44 ++++++++++++++
 src/tests/asn.1/trval_reference.out  |   50 ++++++++++++++++
 15 files changed, 588 insertions(+), 2 deletions(-)

diff --git a/src/include/k5-spake.h b/src/include/k5-spake.h
new file mode 100644
index 0000000..ddb5d81
--- /dev/null
+++ b/src/include/k5-spake.h
@@ -0,0 +1,107 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* include/k5-spake.h - SPAKE preauth mech declarations */
+/*
+ * Copyright (C) 2015 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * The SPAKE preauth mechanism allows long-term client keys to be used for
+ * preauthentication without exposing them to offline dictionary attacks.  The
+ * negotiated key can also be used for second-factor authentication.  This
+ * header file declares structures and encoder/decoder functions for the
+ * mechanism's padata messages.
+ */
+
+#ifndef K5_SPAKE_H
+#define K5_SPAKE_H
+
+#include "k5-int.h"
+
+/* SPAKESecondFactor is contained within a SPAKEChallenge, SPAKEResponse, or
+ * EncryptedData message and contains a second-factor challenge or response. */
+typedef struct krb5_spake_factor_st {
+    int32_t type;
+    krb5_data *data;
+} krb5_spake_factor;
+
+/* SPAKESupport is sent from the client to the KDC to indicate which group the
+ * client supports. */
+typedef struct krb5_spake_support_st {
+    int32_t ngroups;
+    int32_t *groups;
+} krb5_spake_support;
+
+/* SPAKEChallenge is sent from the KDC to the client to communicate its group
+ * selection, public value, and second-factor challenge options. */
+typedef struct krb5_spake_challenge_st {
+    int32_t group;
+    krb5_data pubkey;
+    krb5_spake_factor **factors;
+} krb5_spake_challenge;
+
+/* SPAKEResponse is sent from the client to the KDC to communicate its public
+ * value and encrypted second-factor response. */
+typedef struct krb5_spake_response_st {
+    krb5_data pubkey;
+    krb5_enc_data factor;
+} krb5_spake_response;
+
+enum krb5_spake_msgtype {
+    SPAKE_MSGTYPE_UNKNOWN = -1,
+    SPAKE_MSGTYPE_SUPPORT = 0,
+    SPAKE_MSGTYPE_CHALLENGE = 1,
+    SPAKE_MSGTYPE_RESPONSE = 2,
+    SPAKE_MSGTYPE_ENCDATA = 3
+};
+
+/* PA-SPAKE is a choice among the message types which can appear in a PA-SPAKE
+ * padata element. */
+typedef struct krb5_pa_spake_st {
+    enum krb5_spake_msgtype choice;
+    union krb5_spake_message_choices {
+        krb5_spake_support support;
+        krb5_spake_challenge challenge;
+        krb5_spake_response response;
+        krb5_enc_data encdata;
+    } u;
+} krb5_pa_spake;
+
+krb5_error_code encode_krb5_spake_factor(const krb5_spake_factor *val,
+                                         krb5_data **code_out);
+krb5_error_code decode_krb5_spake_factor(const krb5_data *code,
+                                         krb5_spake_factor **val_out);
+void k5_free_spake_factor(krb5_context context, krb5_spake_factor *val);
+
+krb5_error_code encode_krb5_pa_spake(const krb5_pa_spake *val,
+                                     krb5_data **code_out);
+krb5_error_code decode_krb5_pa_spake(const krb5_data *code,
+                                     krb5_pa_spake **val_out);
+void k5_free_pa_spake(krb5_context context, krb5_pa_spake *val);
+
+#endif /* K5_SPAKE_H */
diff --git a/src/lib/krb5/asn.1/asn1_k_encode.c b/src/lib/krb5/asn.1/asn1_k_encode.c
index 8894609..476e2a5 100644
--- a/src/lib/krb5/asn.1/asn1_k_encode.c
+++ b/src/lib/krb5/asn.1/asn1_k_encode.c
@@ -25,7 +25,7 @@
  */
 
 #include "asn1_encode.h"
-#include <assert.h>
+#include "k5-spake.h"
 
 DEFINT_IMMEDIATE(krb5_version, KVNO, KRB5KDC_ERR_BAD_PVNO);
 
@@ -1814,3 +1814,53 @@ static const struct atype_info *secure_cookie_fields[] = {
 DEFSEQTYPE(secure_cookie, krb5_secure_cookie, secure_cookie_fields);
 MAKE_ENCODER(encode_krb5_secure_cookie, secure_cookie);
 MAKE_DECODER(decode_krb5_secure_cookie, secure_cookie);
+
+DEFFIELD(spake_factor_0, krb5_spake_factor, type, 0, int32);
+DEFFIELD(spake_factor_1, krb5_spake_factor, data, 1, opt_ostring_data_ptr);
+static const struct atype_info *spake_factor_fields[] = {
+    &k5_atype_spake_factor_0, &k5_atype_spake_factor_1
+};
+DEFSEQTYPE(spake_factor, krb5_spake_factor, spake_factor_fields);
+DEFPTRTYPE(spake_factor_ptr, spake_factor);
+DEFNULLTERMSEQOFTYPE(seqof_spake_factor, spake_factor_ptr);
+DEFPTRTYPE(ptr_seqof_spake_factor, seqof_spake_factor);
+MAKE_ENCODER(encode_krb5_spake_factor, spake_factor);
+MAKE_DECODER(decode_krb5_spake_factor, spake_factor);
+
+DEFCNFIELD(spake_support_0, krb5_spake_support, groups, ngroups, 0,
+           cseqof_int32);
+static const struct atype_info *spake_support_fields[] = {
+    &k5_atype_spake_support_0
+};
+DEFSEQTYPE(spake_support, krb5_spake_support, spake_support_fields);
+
+DEFFIELD(spake_challenge_0, krb5_spake_challenge, group, 0, int32);
+DEFFIELD(spake_challenge_1, krb5_spake_challenge, pubkey, 1, ostring_data);
+DEFFIELD(spake_challenge_2, krb5_spake_challenge, factors, 2,
+         ptr_seqof_spake_factor);
+static const struct atype_info *spake_challenge_fields[] = {
+    &k5_atype_spake_challenge_0, &k5_atype_spake_challenge_1,
+    &k5_atype_spake_challenge_2
+};
+DEFSEQTYPE(spake_challenge, krb5_spake_challenge, spake_challenge_fields);
+
+DEFFIELD(spake_response_0, krb5_spake_response, pubkey, 0, ostring_data);
+DEFFIELD(spake_response_1, krb5_spake_response, factor, 1, encrypted_data);
+static const struct atype_info *spake_response_fields[] = {
+    &k5_atype_spake_response_0, &k5_atype_spake_response_1,
+};
+DEFSEQTYPE(spake_response, krb5_spake_response, spake_response_fields);
+
+DEFCTAGGEDTYPE(pa_spake_0, 0, spake_support);
+DEFCTAGGEDTYPE(pa_spake_1, 1, spake_challenge);
+DEFCTAGGEDTYPE(pa_spake_2, 2, spake_response);
+DEFCTAGGEDTYPE(pa_spake_3, 3, encrypted_data);
+static const struct atype_info *pa_spake_alternatives[] = {
+    &k5_atype_pa_spake_0, &k5_atype_pa_spake_1, &k5_atype_pa_spake_2,
+    &k5_atype_pa_spake_3
+};
+DEFCHOICETYPE(pa_spake_choice, union krb5_spake_message_choices,
+              enum krb5_spake_msgtype, pa_spake_alternatives);
+DEFCOUNTEDTYPE_SIGNED(pa_spake, krb5_pa_spake, u, choice, pa_spake_choice);
+MAKE_ENCODER(encode_krb5_pa_spake, pa_spake);
+MAKE_DECODER(decode_krb5_pa_spake, pa_spake);
diff --git a/src/lib/krb5/krb/kfree.c b/src/lib/krb5/krb/kfree.c
index a631807..e1ea149 100644
--- a/src/lib/krb5/krb/kfree.c
+++ b/src/lib/krb5/krb/kfree.c
@@ -51,6 +51,7 @@
  */
 
 #include "k5-int.h"
+#include "k5-spake.h"
 #include <assert.h>
 
 void KRB5_CALLCONV
@@ -890,3 +891,42 @@ k5_free_secure_cookie(krb5_context context, krb5_secure_cookie *val)
     k5_zapfree_pa_data(val->data);
     free(val);
 }
+
+void
+k5_free_spake_factor(krb5_context context, krb5_spake_factor *val)
+{
+    if (val == NULL)
+        return;
+    krb5_free_data(context, val->data);
+    free(val);
+}
+
+void
+k5_free_pa_spake(krb5_context context, krb5_pa_spake *val)
+{
+    krb5_spake_factor **f;
+
+    if (val == NULL)
+        return;
+    switch (val->choice) {
+    case SPAKE_MSGTYPE_SUPPORT:
+        free(val->u.support.groups);
+        break;
+    case SPAKE_MSGTYPE_CHALLENGE:
+        krb5_free_data_contents(context, &val->u.challenge.pubkey);
+        for (f = val->u.challenge.factors; f != NULL && *f != NULL; f++)
+            k5_free_spake_factor(context, *f);
+        free(val->u.challenge.factors);
+        break;
+    case SPAKE_MSGTYPE_RESPONSE:
+        krb5_free_data_contents(context, &val->u.response.pubkey);
+        krb5_free_data_contents(context, &val->u.response.factor.ciphertext);
+        break;
+    case SPAKE_MSGTYPE_ENCDATA:
+        krb5_free_data_contents(context, &val->u.encdata.ciphertext);
+        break;
+    default:
+        break;
+    }
+    free(val);
+}
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index ed6cad6..622bc36 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -36,6 +36,7 @@ decode_krb5_pa_otp_req
 decode_krb5_pa_otp_enc_req
 decode_krb5_pa_pac_req
 decode_krb5_pa_s4u_x509_user
+decode_krb5_pa_spake
 decode_krb5_padata_sequence
 decode_krb5_priv
 decode_krb5_safe
@@ -44,6 +45,7 @@ decode_krb5_sam_challenge_2_body
 decode_krb5_sam_response_2
 decode_krb5_secure_cookie
 decode_krb5_setpw_req
+decode_krb5_spake_factor
 decode_krb5_tgs_rep
 decode_krb5_tgs_req
 decode_krb5_ticket
@@ -85,6 +87,7 @@ encode_krb5_pa_otp_challenge
 encode_krb5_pa_otp_req
 encode_krb5_pa_otp_enc_req
 encode_krb5_pa_s4u_x509_user
+encode_krb5_pa_spake
 encode_krb5_padata_sequence
 encode_krb5_pkinit_supp_pub_info
 encode_krb5_priv
@@ -95,6 +98,7 @@ encode_krb5_sam_challenge_2_body
 encode_krb5_sam_response_2
 encode_krb5_secure_cookie
 encode_krb5_sp80056a_other_info
+encode_krb5_spake_factor
 encode_krb5_tgs_rep
 encode_krb5_tgs_req
 encode_krb5_ticket
@@ -128,7 +132,9 @@ k5_free_kkdcp_message
 k5_free_pa_otp_challenge
 k5_free_pa_otp_req
 k5_free_secure_cookie
+k5_free_pa_spake
 k5_free_serverlist
+k5_free_spake_factor
 k5_hostrealm_free_context
 k5_init_trace
 k5_is_string_numeric
diff --git a/src/tests/asn.1/Makefile.in b/src/tests/asn.1/Makefile.in
index fec4e10..ec9c674 100644
--- a/src/tests/asn.1/Makefile.in
+++ b/src/tests/asn.1/Makefile.in
@@ -9,7 +9,7 @@ SRCS= $(srcdir)/krb5_encode_test.c $(srcdir)/krb5_decode_test.c \
 
 ASN1SRCS= $(srcdir)/krb5.asn1 $(srcdir)/pkix.asn1 $(srcdir)/otp.asn1 \
 	$(srcdir)/pkinit.asn1 $(srcdir)/pkinit-agility.asn1 \
-	$(srcdir)/cammac.asn1
+	$(srcdir)/cammac.asn1 $(srcdir)/spake.asn1
 
 all: krb5_encode_test krb5_decode_test krb5_decode_leak t_trval
 
diff --git a/src/tests/asn.1/krb5_decode_test.c b/src/tests/asn.1/krb5_decode_test.c
index f17f9b1..ee70fa4 100644
--- a/src/tests/asn.1/krb5_decode_test.c
+++ b/src/tests/asn.1/krb5_decode_test.c
@@ -25,6 +25,7 @@
  */
 
 #include "k5-int.h"
+#include "k5-spake.h"
 #include "ktest.h"
 #include "com_err.h"
 #include "utility.h"
@@ -1107,6 +1108,42 @@ int main(argc, argv)
         ktest_empty_secure_cookie(&ref);
     }
 
+    /****************************************************************/
+    /* decode_krb5_spake_factor */
+    {
+        setup(krb5_spake_factor,ktest_make_minimal_spake_factor);
+        decode_run("spake_factor","(optionals NULL)","30 05 A0 03 02 01 01",decode_krb5_spake_factor,ktest_equal_spake_factor,k5_free_spake_factor);
+        ktest_empty_spake_factor(&ref);
+    }
+    {
+        setup(krb5_spake_factor,ktest_make_maximal_spake_factor);
+        decode_run("spake_factor","","30 0E A0 03 02 01 02 A1 07 04 05 66 64 61 74 61",decode_krb5_spake_factor,ktest_equal_spake_factor,k5_free_spake_factor);
+        ktest_empty_spake_factor(&ref);
+    }
+
+    /****************************************************************/
+    /* decode_krb5_pa_spake */
+    {
+        setup(krb5_pa_spake,ktest_make_support_pa_spake);
+        decode_run("pa_spake","(support)","A0 0C 30 0A A0 08 30 06 02 01 01 02 01 02",decode_krb5_pa_spake,ktest_equal_pa_spake,k5_free_pa_spake);
+        ktest_empty_pa_spake(&ref);
+    }
+    {
+        setup(krb5_pa_spake,ktest_make_challenge_pa_spake);
+        decode_run("pa_spake","(challenge)","A1 2D 30 2B A0 03 02 01 01 A1 09 04 07 54 20 76 61 6C 75 65 A2 19 30 17 30 05 A0 03 02 01 01 30 0E A0 03 02 01 02 A1 07 04 05 66 64 61 74 61",decode_krb5_pa_spake,ktest_equal_pa_spake,k5_free_pa_spake);
+        ktest_empty_pa_spake(&ref);
+    }
+    {
+        setup(krb5_pa_spake,ktest_make_response_pa_spake);
+        decode_run("pa_spake","(response)","A2 34 30 32 A0 09 04 07 53 20 76 61 6C 75 65 A1 25 30 23 A0 03 02 01 00 A1 03 02 01 05 A2 17 04 15 6B 72 62 41 53 4E 2E 31 20 74 65 73 74 20 6D 65 73 73 61 67 65",decode_krb5_pa_spake,ktest_equal_pa_spake,k5_free_pa_spake);
+        ktest_empty_pa_spake(&ref);
+    }
+    {
+        setup(krb5_pa_spake,ktest_make_encdata_pa_spake);
+        decode_run("pa_spake","(encdata)","A3 25 30 23 A0 03 02 01 00 A1 03 02 01 05 A2 17 04 15 6B 72 62 41 53 4E 2E 31 20 74 65 73 74 20 6D 65 73 73 61 67 65",decode_krb5_pa_spake,ktest_equal_pa_spake,k5_free_pa_spake);
+        ktest_empty_pa_spake(&ref);
+    }
+
 #ifndef DISABLE_PKINIT
 
     /****************************************************************/
diff --git a/src/tests/asn.1/krb5_encode_test.c b/src/tests/asn.1/krb5_encode_test.c
index f5710b6..3efbfb4 100644
--- a/src/tests/asn.1/krb5_encode_test.c
+++ b/src/tests/asn.1/krb5_encode_test.c
@@ -759,6 +759,35 @@ main(argc, argv)
         encode_run(cookie, "secure_cookie", "", encode_krb5_secure_cookie);
         ktest_empty_secure_cookie(&cookie);
     }
+    /****************************************************************/
+    /* encode_krb5_spake_factor */
+    {
+        krb5_spake_factor factor;
+        ktest_make_minimal_spake_factor(&factor);
+        encode_run(factor, "spake_factor", "(optionals NULL)",
+                   encode_krb5_spake_factor);
+        ktest_empty_spake_factor(&factor);
+        ktest_make_maximal_spake_factor(&factor);
+        encode_run(factor, "spake_factor", "", encode_krb5_spake_factor);
+        ktest_empty_spake_factor(&factor);
+    }
+    /****************************************************************/
+    /* encode_krb5_pa_spake */
+    {
+        krb5_pa_spake pa_spake;
+        ktest_make_support_pa_spake(&pa_spake);
+        encode_run(pa_spake, "pa_spake", "(support)", encode_krb5_pa_spake);
+        ktest_empty_pa_spake(&pa_spake);
+        ktest_make_challenge_pa_spake(&pa_spake);
+        encode_run(pa_spake, "pa_spake", "(challenge)", encode_krb5_pa_spake);
+        ktest_empty_pa_spake(&pa_spake);
+        ktest_make_response_pa_spake(&pa_spake);
+        encode_run(pa_spake, "pa_spake", "(response)", encode_krb5_pa_spake);
+        ktest_empty_pa_spake(&pa_spake);
+        ktest_make_encdata_pa_spake(&pa_spake);
+        encode_run(pa_spake, "pa_spake", "(encdata)", encode_krb5_pa_spake);
+        ktest_empty_pa_spake(&pa_spake);
+    }
 #ifndef DISABLE_PKINIT
     /****************************************************************/
     /* encode_krb5_pa_pk_as_req */
diff --git a/src/tests/asn.1/ktest.c b/src/tests/asn.1/ktest.c
index 43084cb..8487955 100644
--- a/src/tests/asn.1/ktest.c
+++ b/src/tests/asn.1/ktest.c
@@ -1016,6 +1016,66 @@ ktest_make_sample_secure_cookie(krb5_secure_cookie *p)
     p->time = SAMPLE_TIME;
 }
 
+void
+ktest_make_minimal_spake_factor(krb5_spake_factor *p)
+{
+    p->type = 1;
+    p->data = NULL;
+}
+
+void
+ktest_make_maximal_spake_factor(krb5_spake_factor *p)
+{
+    p->type = 2;
+    p->data = ealloc(sizeof(*p->data));
+    krb5_data_parse(p->data, "fdata");
+}
+
+void
+ktest_make_support_pa_spake(krb5_pa_spake *p)
+{
+    krb5_spake_support *s = &p->u.support;
+
+    s->ngroups = 2;
+    s->groups = ealloc(s->ngroups * sizeof(*s->groups));
+    s->groups[0] = 1;
+    s->groups[1] = 2;
+    p->choice = SPAKE_MSGTYPE_SUPPORT;
+}
+
+void
+ktest_make_challenge_pa_spake(krb5_pa_spake *p)
+{
+    krb5_spake_challenge *c = &p->u.challenge;
+
+    c->group = 1;
+    krb5_data_parse(&c->pubkey, "T value");
+    c->factors = ealloc(3 * sizeof(*c->factors));
+    c->factors[0] = ealloc(sizeof(*c->factors[0]));
+    ktest_make_minimal_spake_factor(c->factors[0]);
+    c->factors[1] = ealloc(sizeof(*c->factors[1]));
+    ktest_make_maximal_spake_factor(c->factors[1]);
+    c->factors[2] = NULL;
+    p->choice = SPAKE_MSGTYPE_CHALLENGE;
+}
+
+void
+ktest_make_response_pa_spake(krb5_pa_spake *p)
+{
+    krb5_spake_response *r = &p->u.response;
+
+    krb5_data_parse(&r->pubkey, "S value");
+    ktest_make_sample_enc_data(&r->factor);
+    p->choice = SPAKE_MSGTYPE_RESPONSE;
+}
+
+void
+ktest_make_encdata_pa_spake(krb5_pa_spake *p)
+{
+    ktest_make_sample_enc_data(&p->u.encdata);
+    p->choice = SPAKE_MSGTYPE_ENCDATA;
+}
+
 /****************************************************************/
 /* destructors */
 
@@ -1854,3 +1914,40 @@ ktest_empty_secure_cookie(krb5_secure_cookie *p)
 {
     ktest_empty_pa_data_array(p->data);
 }
+
+void
+ktest_empty_spake_factor(krb5_spake_factor *p)
+{
+    krb5_free_data(NULL, p->data);
+    p->data = NULL;
+}
+
+void
+ktest_empty_pa_spake(krb5_pa_spake *p)
+{
+    krb5_spake_factor **f;
+
+    switch (p->choice) {
+    case SPAKE_MSGTYPE_SUPPORT:
+        free(p->u.support.groups);
+        break;
+    case SPAKE_MSGTYPE_CHALLENGE:
+        ktest_empty_data(&p->u.challenge.pubkey);
+        for (f = p->u.challenge.factors; *f != NULL; f++) {
+            ktest_empty_spake_factor(*f);
+            free(*f);
+        }
+        free(p->u.challenge.factors);
+        break;
+    case SPAKE_MSGTYPE_RESPONSE:
+        ktest_empty_data(&p->u.response.pubkey);
+        ktest_destroy_enc_data(&p->u.response.factor);
+        break;
+    case SPAKE_MSGTYPE_ENCDATA:
+        ktest_destroy_enc_data(&p->u.encdata);
+        break;
+    default:
+        break;
+    }
+    p->choice = SPAKE_MSGTYPE_UNKNOWN;
+}
diff --git a/src/tests/asn.1/ktest.h b/src/tests/asn.1/ktest.h
index 493303c..1413cfa 100644
--- a/src/tests/asn.1/ktest.h
+++ b/src/tests/asn.1/ktest.h
@@ -28,6 +28,7 @@
 #define __KTEST_H__
 
 #include "k5-int.h"
+#include "k5-spake.h"
 #include "kdb.h"
 
 #define SAMPLE_USEC 123456
@@ -124,6 +125,12 @@ void ktest_make_sample_kkdcp_message(krb5_kkdcp_message *p);
 void ktest_make_minimal_cammac(krb5_cammac *p);
 void ktest_make_maximal_cammac(krb5_cammac *p);
 void ktest_make_sample_secure_cookie(krb5_secure_cookie *p);
+void ktest_make_minimal_spake_factor(krb5_spake_factor *p);
+void ktest_make_maximal_spake_factor(krb5_spake_factor *p);
+void ktest_make_support_pa_spake(krb5_pa_spake *p);
+void ktest_make_challenge_pa_spake(krb5_pa_spake *p);
+void ktest_make_response_pa_spake(krb5_pa_spake *p);
+void ktest_make_encdata_pa_spake(krb5_pa_spake *p);
 
 /*----------------------------------------------------------------------*/
 
@@ -209,6 +216,8 @@ void ktest_empty_ldap_seqof_key_data(krb5_context, ldap_seqof_key_data *p);
 void ktest_empty_kkdcp_message(krb5_kkdcp_message *p);
 void ktest_empty_cammac(krb5_cammac *p);
 void ktest_empty_secure_cookie(krb5_secure_cookie *p);
+void ktest_empty_spake_factor(krb5_spake_factor *p);
+void ktest_empty_pa_spake(krb5_pa_spake *p);
 
 extern krb5_context test_context;
 extern char *sample_principal_name;
diff --git a/src/tests/asn.1/ktest_equal.c b/src/tests/asn.1/ktest_equal.c
index e8bb889..714cc43 100644
--- a/src/tests/asn.1/ktest_equal.c
+++ b/src/tests/asn.1/ktest_equal.c
@@ -853,6 +853,13 @@ ktest_equal_sequence_of_otp_tokeninfo(krb5_otp_tokeninfo **ref,
     array_compare(ktest_equal_otp_tokeninfo);
 }
 
+int
+ktest_equal_sequence_of_spake_factor(krb5_spake_factor **ref,
+                                     krb5_spake_factor **var)
+{
+    array_compare(ktest_equal_spake_factor);
+}
+
 #ifndef DISABLE_PKINIT
 
 static int
@@ -1094,3 +1101,45 @@ ktest_equal_secure_cookie(krb5_secure_cookie *ref, krb5_secure_cookie *var)
     p = p && ref->time == ref->time;
     return p;
 }
+
+int
+ktest_equal_spake_factor(krb5_spake_factor *ref, krb5_spake_factor *var)
+{
+    int p = TRUE;
+    if (ref == var) return TRUE;
+    else if (ref == NULL || var == NULL) return FALSE;
+    p = p && scalar_equal(type);
+    p = p && ptr_equal(data,ktest_equal_data);
+    return p;
+}
+
+int
+ktest_equal_pa_spake(krb5_pa_spake *ref, krb5_pa_spake *var)
+{
+    int p = TRUE;
+    if (ref == var) return TRUE;
+    else if (ref == NULL || var == NULL) return FALSE;
+    else if (ref->choice != var->choice) return FALSE;
+    switch (ref->choice) {
+    case SPAKE_MSGTYPE_SUPPORT:
+        p = p && scalar_equal(u.support.ngroups);
+        p = p && (memcmp(ref->u.support.groups,var->u.support.groups,
+                         ref->u.support.ngroups * sizeof(int32_t)) == 0);
+        break;
+    case SPAKE_MSGTYPE_CHALLENGE:
+        p = p && struct_equal(u.challenge.pubkey,ktest_equal_data);
+        p = p && ptr_equal(u.challenge.factors,
+                           ktest_equal_sequence_of_spake_factor);
+        break;
+    case SPAKE_MSGTYPE_RESPONSE:
+        p = p && struct_equal(u.response.pubkey,ktest_equal_data);
+        p = p && struct_equal(u.response.factor,ktest_equal_enc_data);
+        break;
+    case SPAKE_MSGTYPE_ENCDATA:
+        p = p && struct_equal(u.encdata,ktest_equal_enc_data);
+        break;
+    default:
+        break;
+    }
+    return p;
+}
diff --git a/src/tests/asn.1/ktest_equal.h b/src/tests/asn.1/ktest_equal.h
index c7b5d74..cfa82ac 100644
--- a/src/tests/asn.1/ktest_equal.h
+++ b/src/tests/asn.1/ktest_equal.h
@@ -28,6 +28,7 @@
 #define __KTEST_EQUAL_H__
 
 #include "k5-int.h"
+#include "k5-spake.h"
 #include "kdb.h"
 
 /* int ktest_equal_structure(krb5_structure *ref, *var) */
@@ -97,6 +98,8 @@ ktest_equal_sequence_of_algorithm_identifier(krb5_algorithm_identifier **ref,
                                              krb5_algorithm_identifier **var);
 int ktest_equal_sequence_of_otp_tokeninfo(krb5_otp_tokeninfo **ref,
                                           krb5_otp_tokeninfo **var);
+int ktest_equal_sequence_of_spake_factor(krb5_spake_factor **ref,
+                                         krb5_spake_factor **var);
 
 len_array(ktest_equal_array_of_enctype,krb5_enctype);
 len_array(ktest_equal_array_of_data,krb5_data);
@@ -152,4 +155,7 @@ int ktest_equal_cammac(krb5_cammac *ref, krb5_cammac *var);
 int ktest_equal_secure_cookie(krb5_secure_cookie *ref,
                               krb5_secure_cookie *var);
 
+generic(ktest_equal_spake_factor, krb5_spake_factor);
+generic(ktest_equal_pa_spake, krb5_pa_spake);
+
 #endif
diff --git a/src/tests/asn.1/make-vectors.c b/src/tests/asn.1/make-vectors.c
index 3cb8a45..2fc8546 100644
--- a/src/tests/asn.1/make-vectors.c
+++ b/src/tests/asn.1/make-vectors.c
@@ -40,6 +40,8 @@
 #include <PA-OTP-REQUEST.h>
 #include <PA-OTP-ENC-REQUEST.h>
 #include <AD-CAMMAC.h>
+#include <SPAKESecondFactor.h>
+#include <PA-SPAKE.h>
 
 static unsigned char buf[8192];
 static size_t buf_pos;
@@ -168,6 +170,36 @@ static struct other_verifiers overfs = { { verifiers, 2, 2 } };
 static AD_CAMMAC_t cammac_2 = { { { (void *)adlist_2, 2, 2 } },
                                 &vmac_1, &vmac_2, &overfs };
 
+/* SPAKESecondFactor */
+static SPAKESecondFactor_t factor_1 = { 1, NULL };
+static OCTET_STRING_t factor_data = { "fdata", 5 };
+static SPAKESecondFactor_t factor_2 = { 2, &factor_data };
+
+/* PA-SPAKE (support) */
+static Int32_t group_1 = 1, group_2 = 2, *groups[] = { &group_1, &group_2 };
+static PA_SPAKE_t pa_spake_1 = { PA_SPAKE_PR_support,
+                                 { .support = { { groups, 2, 2 } } } };
+
+/* PA-SPAKE (challenge) */
+static SPAKESecondFactor_t *factors[2] = { &factor_1, &factor_2 };
+static PA_SPAKE_t pa_spake_2 = { PA_SPAKE_PR_challenge,
+                                 { .challenge = { 1, { "T value", 7 },
+                                                  { factors, 2, 2 } } } };
+
+/* PA-SPAKE (response) */
+UInt32_t enctype_5 = 5;
+static PA_SPAKE_t pa_spake_3 = { PA_SPAKE_PR_response,
+                                 { .response = { { "S value", 7 },
+                                                 { 0, &enctype_5,
+                                                   { "krbASN.1 test message",
+                                                     21 } } } } };
+
+/* PA-SPAKE (encdata) */
+static PA_SPAKE_t pa_spake_4 = { PA_SPAKE_PR_encdata,
+                                 { .encdata = { 0, &enctype_5,
+                                                { "krbASN.1 test message",
+                                                  21 } } } };
+
 static int
 consume(const void *data, size_t size, void *dummy)
 {
@@ -272,6 +304,30 @@ main()
     der_encode(&asn_DEF_AD_CAMMAC, &cammac_2, consume, NULL);
     printbuf();
 
+    printf("\nMinimal SPAKESecondFactor:\n");
+    der_encode(&asn_DEF_SPAKESecondFactor, &factor_1, consume, NULL);
+    printbuf();
+
+    printf("\nMaximal SPAKESecondFactor:\n");
+    der_encode(&asn_DEF_SPAKESecondFactor, &factor_2, consume, NULL);
+    printbuf();
+
+    printf("\nPA-SPAKE (support):\n");
+    der_encode(&asn_DEF_PA_SPAKE, &pa_spake_1, consume, NULL);
+    printbuf();
+
+    printf("\nPA-SPAKE (challenge):\n");
+    der_encode(&asn_DEF_PA_SPAKE, &pa_spake_2, consume, NULL);
+    printbuf();
+
+    printf("\nPA-SPAKE (response):\n");
+    der_encode(&asn_DEF_PA_SPAKE, &pa_spake_3, consume, NULL);
+    printbuf();
+
+    printf("\nPA-SPAKE (encdata):\n");
+    der_encode(&asn_DEF_PA_SPAKE, &pa_spake_4, consume, NULL);
+    printbuf();
+
     printf("\n");
     return 0;
 }
diff --git a/src/tests/asn.1/reference_encode.out b/src/tests/asn.1/reference_encode.out
index 824e079..a76deea 100644
--- a/src/tests/asn.1/reference_encode.out
+++ b/src/tests/asn.1/reference_encode.out
@@ -72,3 +72,9 @@ encode_krb5_kkdcp_message: 30 82 01 FC A0 82 01 EC 04 82 01 E8 6A 82 01 E4 30 82
 encode_krb5_cammac(optionals NULL): 30 12 A0 10 30 0E 30 0C A0 03 02 01 01 A1 05 04 03 61 64 31
 encode_krb5_cammac: 30 81 F2 A0 1E 30 1C 30 0C A0 03 02 01 01 A1 05 04 03 61 64 31 30 0C A0 03 02 01 02 A1 05 04 03 61 64 32 A1 3D 30 3B A0 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A1 03 02 01 05 A2 03 02 01 10 A3 13 30 11 A0 03 02 01 01 A1 0A 04 08 63 6B 73 75 6D 6B 64 63 A2 3D 30 3B A0 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A1 03 02 01 05 A2 03 02 01 10 A3 13 30 11 A0 03 02 01 01 A1 0A 04 08 63 6B 73 75 6D 73 76 63 A3 52 30 50 30 13 A3 11 30 0F A0 03 02 01 01 A1 08 04 06 63 6B 73 75 6D 31 30 39 A0 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A1 03 02 01 05 A2 03 02 01 10 A3 11 30 0F A0 03 02 01 01 A1 08 04 06 63 6B 73 75 6D 32
 encode_krb5_secure_cookie: 30 2C 02 04 2D F8 02 25 30 24 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61
+encode_krb5_spake_factor(optionals NULL): 30 05 A0 03 02 01 01
+encode_krb5_spake_factor: 30 0E A0 03 02 01 02 A1 07 04 05 66 64 61 74 61
+encode_krb5_pa_spake(support): A0 0C 30 0A A0 08 30 06 02 01 01 02 01 02
+encode_krb5_pa_spake(challenge): A1 2D 30 2B A0 03 02 01 01 A1 09 04 07 54 20 76 61 6C 75 65 A2 19 30 17 30 05 A0 03 02 01 01 30 0E A0 03 02 01 02 A1 07 04 05 66 64 61 74 61
+encode_krb5_pa_spake(response): A2 34 30 32 A0 09 04 07 53 20 76 61 6C 75 65 A1 25 30 23 A0 03 02 01 00 A1 03 02 01 05 A2 17 04 15 6B 72 62 41 53 4E 2E 31 20 74 65 73 74 20 6D 65 73 73 61 67 65
+encode_krb5_pa_spake(encdata): A3 25 30 23 A0 03 02 01 00 A1 03 02 01 05 A2 17 04 15 6B 72 62 41 53 4E 2E 31 20 74 65 73 74 20 6D 65 73 73 61 67 65
diff --git a/src/tests/asn.1/spake.asn1 b/src/tests/asn.1/spake.asn1
new file mode 100644
index 0000000..50718d8
--- /dev/null
+++ b/src/tests/asn.1/spake.asn1
@@ -0,0 +1,44 @@
+KerberosV5SPAKE {
+        iso(1) identified-organization(3) dod(6) internet(1)
+        security(5) kerberosV5(2) modules(4) spake(8)
+} DEFINITIONS EXPLICIT TAGS ::= BEGIN
+
+IMPORTS
+    EncryptedData, Int32
+      FROM KerberosV5Spec2 { iso(1) identified-organization(3)
+        dod(6) internet(1) security(5) kerberosV5(2) modules(4)
+        krb5spec2(2) };
+        -- as defined in RFC 4120.
+
+SPAKESupport ::= SEQUENCE {
+    groups      [0] SEQUENCE (SIZE(1..MAX)) OF Int32,
+    ...
+}
+
+SPAKEChallenge ::= SEQUENCE {
+    group       [0] Int32,
+    pubkey      [1] OCTET STRING,
+    factors     [2] SEQUENCE (SIZE(1..MAX)) OF SPAKESecondFactor,
+    ...
+}
+
+SPAKESecondFactor ::= SEQUENCE {
+    type        [0] Int32,
+    data        [1] OCTET STRING OPTIONAL
+}
+
+SPAKEResponse ::= SEQUENCE {
+    pubkey      [0] OCTET STRING,
+    factor      [1] EncryptedData, -- SPAKESecondFactor
+    ...
+}
+
+PA-SPAKE ::= CHOICE {
+    support     [0] SPAKESupport,
+    challenge   [1] SPAKEChallenge,
+    response    [2] SPAKEResponse,
+    encdata     [3] EncryptedData,
+    ...
+}
+
+END
diff --git a/src/tests/asn.1/trval_reference.out b/src/tests/asn.1/trval_reference.out
index c27a042..e5c7159 100644
--- a/src/tests/asn.1/trval_reference.out
+++ b/src/tests/asn.1/trval_reference.out
@@ -1584,3 +1584,53 @@ encode_krb5_secure_cookie:
 .  .  [Sequence/Sequence Of]
 .  .  .  [1] [Integer] 13
 .  .  .  [2] [Octet String] "pa-data"
+
+encode_krb5_spake_factor(optionals NULL):
+
+[Sequence/Sequence Of]
+.  [0] [Integer] 1
+
+encode_krb5_spake_factor:
+
+[Sequence/Sequence Of]
+.  [0] [Integer] 2
+.  [1] [Octet String] "fdata"
+
+encode_krb5_pa_spake(support):
+
+[CONT 0]
+.  [Sequence/Sequence Of]
+.  .  [0] [Sequence/Sequence Of]
+.  .  .  [Integer] 1
+.  .  .  [Integer] 2
+
+encode_krb5_pa_spake(challenge):
+
+[CONT 1]
+.  [Sequence/Sequence Of]
+.  .  [0] [Integer] 1
+.  .  [1] [Octet String] "T value"
+.  .  [2] [Sequence/Sequence Of]
+.  .  .  [Sequence/Sequence Of]
+.  .  .  .  [0] [Integer] 1
+.  .  .  [Sequence/Sequence Of]
+.  .  .  .  [0] [Integer] 2
+.  .  .  .  [1] [Octet String] "fdata"
+
+encode_krb5_pa_spake(response):
+
+[CONT 2]
+.  [Sequence/Sequence Of]
+.  .  [0] [Octet String] "S value"
+.  .  [1] [Sequence/Sequence Of]
+.  .  .  [0] [Integer] 0
+.  .  .  [1] [Integer] 5
+.  .  .  [2] [Octet String] "krbASN.1 test message"
+
+encode_krb5_pa_spake(encdata):
+
+[CONT 3]
+.  [Sequence/Sequence Of]
+.  .  [0] [Integer] 0
+.  .  [1] [Integer] 5
+.  .  [2] [Octet String] "krbASN.1 test message"


More information about the cvs-krb5 mailing list