I would like to move forward with this.
Is anyone willing to look at this?

On 9/2/19 9:06 AM, Martijn van Duren wrote:
> Hello tech@,
> 
> I've worked hard to get SNMPv3 support for snmp(1). Here's the end
> result. This mail contains the full diff for people just wanting to
> test, the follow up mails will contain the incremental diffs (still
> massive beasts) so they're easier to review.
> 
> I've implemented Net-SNMP's -A, -a, -E, -e, -3K (as -K), -3k (as -k),
> -l, -n, -u, -X, -x and -Z. I choose to leave out the -3 because the
> way to handle this is really ugly and -K and -k are unused by
> Net-SNMP.
> This is also the reason there's no support for master keys, because -m
> and -M are used by Net-SNMP and I don't know if I want to implement that
> at some point and I haven't seen a device that needs the master key. The
> scaffolding for adding master key support is there and if someone has
> an actual usecase for it and comes up with a good suggestion for a flag
> I'll be happy to implement it.
> 
> Tested with snmpd(8), netsnmpd and HP Laserjet 4730mfp by me and
> netgear GS724Tv4 ProSafe by semarie@ on previous iteration of diff.
> 
> Tests, feedback, OKs welcome.
> 
> martijn@
> 
> diff --git a/Makefile b/Makefile
> index 62bb556..102582b 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -1,9 +1,9 @@
>  #    $OpenBSD: Makefile,v 1.1 2019/08/09 06:17:59 martijn Exp $
>  
>  PROG=                snmp
> -SRCS=                mib.c smi.c snmp.c snmpc.c
> -LDADD+=              -lutil
> -DPADD+=              ${LIBUTIL}
> +SRCS=                mib.c smi.c snmp.c snmpc.c usm.c
> +LDADD+=              -lcrypto -lutil
> +DPADD+=              ${LIBCRYPTO} ${LIBUTIL}
>  
>  MAN=         snmp.1
>  
> diff --git a/snmp.1 b/snmp.1
> index e158ba0..fe283a5 100644
> --- a/snmp.1
> +++ b/snmp.1
> @@ -23,50 +23,110 @@
>  .Sh SYNOPSIS
>  .Nm
>  .Cm get | getnext
> +.Op Fl A Ar authpass
> +.Op Fl a Ar digest
>  .Op Fl c Ar community
> +.Op Fl E Ar ctxengineid
> +.Op Fl e Ar secengineid
> +.Op Fl K Ar localpriv
> +.Op Fl k Ar localauth
> +.Op Fl l Ar seclevel
> +.Op Fl n Ar ctxname
> +.Op Fl O Cm afnQqSvx
>  .Op Fl r Ar retries
>  .Op Fl t Ar timeout
> +.Op Fl u Ar user
>  .Op Fl v Ar version
> -.Op Fl O Cm afnQqSvx
> +.Op Fl X Ar privpass
> +.Op Fl x Ar cipher
> +.Op Fl Z Ar boots , Ns Ar time
>  .Ar agent
>  .Ar oid ...
>  .Nm
>  .Cm walk
> +.Op Fl A Ar authpass
> +.Op Fl a Ar digest
>  .Op Fl c Ar community
> +.Op Fl E Ar ctxengineid
> +.Op Fl e Ar secengineid
> +.Op Fl K Ar localpriv
> +.Op Fl k Ar localauth
> +.Op Fl l Ar seclevel
> +.Op Fl n Ar ctxname
> +.Op Fl O Cm afnQqSvx
>  .Op Fl r Ar retries
>  .Op Fl t Ar timeout
> +.Op Fl u Ar user
>  .Op Fl v Ar version
> -.Op Fl O Cm afnQqSvx
> +.Op Fl X Ar privpass
> +.Op Fl x Ar cipher
> +.Op Fl Z Ar boots , Ns Ar time
>  .Op Fl C Cm cIipt
>  .Op Fl C Cm E Ar endoid
>  .Ar agent
>  .Op Ar oid
>  .Nm
>  .Cm bulkget
> +.Op Fl A Ar authpass
> +.Op Fl a Ar digest
>  .Op Fl c Ar community
> +.Op Fl E Ar ctxengineid
> +.Op Fl e Ar secengineid
> +.Op Fl K Ar localpriv
> +.Op Fl k Ar localauth
> +.Op Fl l Ar seclevel
> +.Op Fl n Ar ctxname
> +.Op Fl O Cm afnQqSvx
>  .Op Fl r Ar retries
>  .Op Fl t Ar timeout
> +.Op Fl u Ar user
>  .Op Fl v Ar version
> -.Op Fl O Cm afnQqSvx
> +.Op Fl X Ar privpass
> +.Op Fl x Ar cipher
> +.Op Fl Z Ar boots , Ns Ar time
>  .Op Fl C Cm n Ns Ar nonrep Ns Cm r Ns Ar maxrep
>  .Ar agent
>  .Ar oid ...
>  .Nm
>  .Cm bulkwalk
> +.Op Fl A Ar authpass
> +.Op Fl a Ar digest
>  .Op Fl c Ar community
> +.Op Fl E Ar ctxengineid
> +.Op Fl e Ar secengineid
> +.Op Fl K Ar localpriv
> +.Op Fl k Ar localauth
> +.Op Fl l Ar seclevel
> +.Op Fl n Ar ctxname
> +.Op Fl O Cm afnQqSvx
>  .Op Fl r Ar retries
>  .Op Fl t Ar timeout
> +.Op Fl u Ar user
>  .Op Fl v Ar version
> -.Op Fl O Cm afnQqSvx
> +.Op Fl X Ar privpass
> +.Op Fl x Ar cipher
> +.Op Fl Z Ar boots , Ns Ar time
>  .Op Fl C Cm cipn Ns Ar nonrep Ns Cm r Ns Ar maxrep
>  .Ar agent
>  .Op Ar oid
>  .Nm
>  .Cm trap
> +.Op Fl A Ar authpass
> +.Op Fl a Ar digest
>  .Op Fl c Ar community
> +.Op Fl E Ar ctxengineid
> +.Op Fl e Ar secengineid
> +.Op Fl K Ar localpriv
> +.Op Fl k Ar localauth
> +.Op Fl l Ar seclevel
> +.Op Fl n Ar ctxname
>  .Op Fl r Ar retries
>  .Op Fl t Ar timeout
> +.Op Fl u Ar user
>  .Op Fl v Ar version
> +.Op Fl X Ar privpass
> +.Op Fl x Ar cipher
> +.Op Fl Z Ar boots , Ns Ar time
>  .Ar agent uptime trapoid
>  .Oo Ar varoid type value Oc ...
>  .Nm
> @@ -145,6 +205,28 @@ Dump the tree of compiled-in MIB objects.
>  .Pp
>  The options are as follows:
>  .Bl -tag -width Ds
> +.It Fl A Ar authpass
> +The authentication password for the user.
> +This will be transformed to
> +.Ar localauth .
> +This option is only used by
> +.Fl v Cm 3 .
> +.It Fl a Ar digest
> +Set the digest
> +.Pq authentication
> +protocol.
> +Options are
> +.Cm MD5 ,
> +.Cm SHA ,
> +.Cm SHA-224 ,
> +.Cm SHA-256 ,
> +.Cm SHA-384
> +or
> +.Cm SHA-512 .
> +This option defaults to
> +.Cm MD5 .
> +This option is only used by
> +.Fl v Cm 3 .
>  .It Fl C Ar appopt
>  Set the application specific
>  .Ar appopt
> @@ -220,6 +302,66 @@ Set the
>  string.
>  Defaults to
>  .Cm public .
> +This option is only used by
> +.Fl v Cm 1
> +and
> +.Fl v Cm 2c .
> +.It Fl e Ar secengineid
> +The USM security engine id.
> +Under normal circumstances this value is discovered via snmpv3 discovery and
> +does not need to be specified.
> +This option is only used by
> +.Fl v Cm 3 .
> +.It Fl E Ar ctxengineid
> +The snmpv3 context engine id.
> +Most of the time this value can be safely ignored.
> +This option is only used by
> +.Fl v Cm 3 .
> +.It Fl K Ar localpriv
> +The localized privacy password for the user in hexadecimal format
> +.Po
> +optionally prefixed with a
> +.Cm 0x
> +.Pc .
> +This option is only used by
> +.Fl v Cm 3 .
> +.It Fl k Ar localauth
> +The localized authentication password for the user in hexadecimal format
> +.Po
> +optionally prefixed with a
> +.Cm 0x
> +.Pc .
> +This option is only used by
> +.Fl v Cm 3 .
> +.It Fl l Ar seclevel
> +The security level.
> +Values can be
> +.Cm noAuthNoPriv Pq default ,
> +.Cm authNoPriv
> +.Po
> +requires either
> +.Fl A
> +or
> +.Fl k
> +.Pc
> +or
> +.Cm authPriv
> +.Po
> +requires either
> +.Fl X
> +or
> +.Fl K
> +in addition to the
> +.Cm authNoPriv
> +requirements
> +.Pc .
> +This option is only used by
> +.Fl v Cm 3 .
> +.It Fl n Ar ctxname
> +Sets the context name.
> +Defaults to an empty string.
> +This option is only used by
> +.Fl v Cm 3 .
>  .It Fl O Ar output
>  Set the
>  .Ar output
> @@ -256,15 +398,45 @@ Set the
>  .Ar timeout
>  to wait for a reply, in seconds.
>  Defaults to 1.
> +.It Fl u Ar user
> +Sets the username.
> +If
> +.Fl v Cm 3
> +is used this option is required.
> +This option is only used by
> +.Fl v Cm 3 .
>  .It Fl v Ar version
>  Set the snmp protocol
>  .Ar version
>  to either
> -.Cm 1
> +.Cm 1 ,
> +.Cm 2c
>  or
> -.Cm 2c .
> +.Cm 3 .
>  Currently defaults to
>  .Cm 2c .
> +.It Fl X Ar privpass
> +The privacy password for the user.
> +This will be tansformed to
> +.Ar localpriv .
> +This option is only used by
> +.Fl v Cm 3 .
> +.It Fl x Ar cipher
> +Sets the cipher
> +.Pq privacy
> +protocol.
> +Options are
> +.Cm DES
> +and
> +.Cm AES .
> +This option is only used by
> +.Fl v Cm 3 .
> +.It Fl Z Ar boots , Ns Ar time
> +Set the engine boots and engine time.
> +Under normal circumstances this value is discovered via snmpv3 discovery and
> +does not need to be specified.
> +This option is only used by
> +.Fl v Cm 3 .
>  .El
>  .Pp
>  The syntax for the
> diff --git a/snmp.c b/snmp.c
> index 7fac777..ba4dc3c 100644
> --- a/snmp.c
> +++ b/snmp.c
> @@ -32,6 +32,52 @@
>  
>  static struct ber_element *
>      snmp_resolve(struct snmp_agent *, struct ber_element *, int);
> +static char *
> +    snmp_package(struct snmp_agent *, struct ber_element *, size_t *);
> +static struct ber_element *
> +    snmp_unpackage(struct snmp_agent *, char *, size_t);
> +static void snmp_v3_free(struct snmp_v3 *);
> +static void snmp_v3_secparamsoffset(void *, size_t);
> +
> +struct snmp_v3 *
> +snmp_v3_init(int level, const char *ctxname, size_t ctxnamelen,
> +    struct snmp_sec *sec)
> +{
> +     struct snmp_v3 *v3;
> +
> +     if ((level & (SNMP_MSGFLAG_SECMASK | SNMP_MSGFLAG_REPORT)) != level ||
> +         sec == NULL) {
> +             errno = EINVAL;
> +             return NULL;
> +     }
> +     if ((v3 = calloc(1, sizeof(*v3))) == NULL)
> +             return NULL;
> +
> +     v3->level = level | SNMP_MSGFLAG_REPORT;
> +     v3->ctxnamelen = ctxnamelen;
> +     if (ctxnamelen != 0) {
> +             if ((v3->ctxname = malloc(ctxnamelen)) == NULL) {
> +                     free(v3);
> +                     return NULL;
> +             }
> +             memcpy(v3->ctxname, ctxname, ctxnamelen);
> +     }
> +     v3->sec = sec;
> +     return v3;
> +}
> +
> +int
> +snmp_v3_setengineid(struct snmp_v3 *v3, char *engineid, size_t engineidlen)
> +{
> +     if (v3->engineidset)
> +             free(v3->engineid);
> +     if ((v3->engineid = malloc(engineidlen)) == NULL)
> +             return -1;
> +     memcpy(v3->engineid, engineid, engineidlen);
> +     v3->engineidlen = engineidlen;
> +     v3->engineidset = 1;
> +     return 0;
> +}
>  
>  struct snmp_agent *
>  snmp_connect_v12(int fd, enum snmp_version version, const char *community)
> @@ -50,21 +96,54 @@ snmp_connect_v12(int fd, enum snmp_version version, const 
> char *community)
>               goto fail;
>       agent->timeout = 1;
>       agent->retries = 5;
> +     agent->v3 = NULL;
>       return agent;
>  
>  fail:
> -     free(agent->community);
>       free(agent);
>       return NULL;
>  }
>  
> +struct snmp_agent *
> +snmp_connect_v3(int fd, struct snmp_v3 *v3)
> +{
> +     struct snmp_agent *agent;
> +
> +     if ((agent = malloc(sizeof(*agent))) == NULL)
> +             return NULL;
> +     agent->fd = fd;
> +     agent->version = SNMP_V3;
> +     agent->v3 = v3;
> +     agent->timeout = 1;
> +     agent->retries = 5;
> +     agent->community = NULL;
> +
> +     if (v3->sec->init(agent) == -1) {
> +             snmp_free_agent(agent);
> +             return NULL;
> +     }
> +     return agent;
> +}
> +
>  void
>  snmp_free_agent(struct snmp_agent *agent)
>  {
>       free(agent->community);
> +     if (agent->v3 != NULL)
> +             snmp_v3_free(agent->v3);
>       free(agent);
>  }
>  
> +static void
> +snmp_v3_free(struct snmp_v3 *v3)
> +{
> +     v3->sec->free(v3->sec->data);
> +     free(v3->sec);
> +     free(v3->ctxname);
> +     free(v3->engineid);
> +     free(v3);
> +}
> +
>  struct ber_element *
>  snmp_get(struct snmp_agent *agent, struct ber_oid *oid, size_t len)
>  {
> @@ -171,19 +250,16 @@ fail:
>  static struct ber_element *
>  snmp_resolve(struct snmp_agent *agent, struct ber_element *pdu, int reply)
>  {
> -     struct ber_element *message, *varbind;
> +     struct ber_element *varbind;
>       struct ber_oid oid;
>       struct timespec start, now;
>       struct pollfd pfd;
> -     struct ber ber;
> +     char *message;
>       ssize_t len;
>       long long reqid, rreqid;
> -     long long version;
> -     char *community;
>       short direction;
>       int to, nfds, ret;
>       int tries;
> -     void *ptr;
>       char buf[READ_BUF_SIZE];
>  
>       if (ber_scanf_elements(pdu, "{i", &reqid) != 0) {
> @@ -192,23 +268,8 @@ snmp_resolve(struct snmp_agent *agent, struct 
> ber_element *pdu, int reply)
>               return NULL;
>       }
>  
> -     if ((message = ber_add_sequence(NULL)) == NULL) {
> -             ber_free_elements(pdu);
> +     if ((message = snmp_package(agent, pdu, &len)) == NULL)
>               return NULL;
> -     }
> -     if (ber_printf_elements(message, "dse", agent->version,
> -         agent->community, pdu) == NULL) {
> -             ber_free_elements(pdu);
> -             ber_free_elements(message);
> -             return NULL;
> -     }
> -     memset(&ber, 0, sizeof(ber));
> -     ber_set_application(&ber, smi_application);
> -     len = ber_write_elements(&ber, message);
> -     ber_free_elements(message);
> -     message = NULL;
> -     if (ber_get_writebuf(&ber, &ptr) < 1)
> -             goto fail;
>  
>       clock_gettime(CLOCK_MONOTONIC, &start);
>       memcpy(&now, &start, sizeof(now));
> @@ -236,7 +297,7 @@ snmp_resolve(struct snmp_agent *agent, struct ber_element 
> *pdu, int reply)
>                               goto fail;
>               }
>               if (direction == POLLOUT) {
> -                     ret = send(agent->fd, ptr, len, MSG_DONTWAIT);
> +                     ret = send(agent->fd, message, len, MSG_DONTWAIT);
>                       if (ret == -1)
>                               goto fail;
>                       if (ret < len) {
> @@ -253,25 +314,10 @@ snmp_resolve(struct snmp_agent *agent, struct 
> ber_element *pdu, int reply)
>                       errno = ECONNRESET;
>               if (ret <= 0)
>                       goto fail;
> -             ber_set_readbuf(&ber, buf, ret);
> -             if ((message = ber_read_elements(&ber, NULL)) == NULL) {
> -                     direction = POLLOUT;
> +             if ((pdu = snmp_unpackage(agent, buf, ret)) == NULL) {
>                       tries--;
> -                     continue;
> -             }
> -             if (ber_scanf_elements(message, "{ise", &version, &community,
> -                 &pdu) != 0) {
> -                     errno = EPROTO;
>                       direction = POLLOUT;
> -                     tries--;
> -                     continue;
> -             }
> -             /* Skip invalid packets; should not happen */
> -             if (version != agent->version ||
> -                 strcmp(community, agent->community) != 0) {
>                       errno = EPROTO;
> -                     direction = POLLOUT;
> -                     tries--;
>                       continue;
>               }
>               /* Validate pdu format and check request id */
> @@ -282,7 +328,7 @@ snmp_resolve(struct snmp_agent *agent, struct ber_element 
> *pdu, int reply)
>                       tries--;
>                       continue;
>               }
> -             if (rreqid != reqid) {
> +             if (rreqid != reqid && rreqid != 0) {
>                       errno = EPROTO;
>                       direction = POLLOUT;
>                       tries--;
> @@ -294,20 +340,202 @@ snmp_resolve(struct snmp_agent *agent, struct 
> ber_element *pdu, int reply)
>                               errno = EPROTO;
>                               direction = POLLOUT;
>                               tries--;
> -                             break;
> +                             continue;
>                       }
>               }
> -             if (varbind != NULL)
> -                     continue;
>  
> -             ber_unlink_elements(message->be_sub->be_next);
> -             ber_free_elements(message);
> -             ber_free(&ber);
> +             free(message);
>               return pdu;
>       }
>  
>  fail:
> +     free(message);
> +     return NULL;
> +}
> +
> +static char *
> +snmp_package(struct snmp_agent *agent, struct ber_element *pdu, size_t *len)
> +{
> +     struct ber ber;
> +     struct ber_element *message, *scopedpdu = NULL, *secparams, *encpdu;
> +     ssize_t securitysize, ret;
> +     size_t secparamsoffset;
> +     char *securityparams = NULL, *buf, *packet = NULL;
> +     long long msgid;
> +     void *cookie = NULL;
> +
> +     bzero(&ber, sizeof(ber));
> +     ber_set_application(&ber, smi_application);
> +
> +     if ((message = ber_add_sequence(NULL)) == NULL) {
> +             ber_free_elements(pdu);
> +             goto fail;
> +     }
> +
> +     switch (agent->version) {
> +     case SNMP_V1:
> +     case SNMP_V2C:
> +             if (ber_printf_elements(message, "dse", agent->version,
> +                 agent->community, pdu) == NULL) {
> +                     ber_free_elements(pdu);
> +                     goto fail;
> +             }
> +             break;
> +     case SNMP_V3:
> +             msgid = arc4random_uniform(2147483647);
> +             if ((scopedpdu = ber_add_sequence(NULL)) == NULL) {
> +                     ber_free_elements(pdu);
> +                     goto fail;
> +             }
> +             if (ber_printf_elements(scopedpdu, "xxe",
> +                 agent->v3->engineid, agent->v3->engineidlen,
> +                 agent->v3->ctxname, agent->v3->ctxnamelen, pdu) == NULL) {
> +                     ber_free_elements(pdu);
> +                     ber_free_elements(scopedpdu);
> +                     goto fail;
> +             }
> +             pdu = NULL;
> +             if ((securityparams = agent->v3->sec->genparams(agent,
> +                 &securitysize, &cookie)) == NULL) {
> +                     ber_free_elements(scopedpdu);
> +                     goto fail;
> +             }
> +             if (agent->v3->level & SNMP_MSGFLAG_PRIV) {
> +                     if ((encpdu = agent->v3->sec->encpdu(agent, scopedpdu,
> +                         cookie)) == NULL)
> +                             goto fail;
> +                     ber_free_elements(scopedpdu);
> +                     scopedpdu = encpdu;
> +             }
> +             if (ber_printf_elements(message, "d{idxd}xe",
> +                 agent->version, msgid, 1472, &(agent->v3->level),
> +                 (size_t) 1, agent->v3->sec->model, securityparams,
> +                 securitysize, scopedpdu) == NULL)
> +                     goto fail;
> +             if (ber_scanf_elements(message, "{SSe", &secparams) == -1)
> +                     goto fail;
> +             ber_set_writecallback(secparams, snmp_v3_secparamsoffset,
> +                 &secparamsoffset);
> +             break;
> +     }
> +
> +     if ((ret = ber_write_elements(&ber, message)) == -1)
> +             goto fail;
> +     *len = (size_t) ret;
> +     if (ber_get_writebuf(&ber, (void **)&buf) != -1 &&
> +         (packet = malloc(ret)) != NULL)
> +             memcpy(packet, buf, ret);
> +     ber_free(&ber);
> +
> +     if (agent->version == SNMP_V3 && packet != NULL) {
> +             if (agent->v3->sec->finalparams(agent, packet,
> +                 ret, secparamsoffset, cookie) == -1) {
> +                     free(packet);
> +                     packet = NULL;
> +             }
> +     }
> +
> +fail:
> +     if (agent->version == SNMP_V3)
> +             agent->v3->sec->freecookie(cookie);
>       ber_free_elements(message);
> +     free(securityparams);
> +     return packet;
> +}
> +
> +static struct ber_element *
> +snmp_unpackage(struct snmp_agent *agent, char *buf, size_t buflen)
> +{
> +     struct ber ber;
> +     enum snmp_version version;
> +     char *community;
> +     struct ber_element *pdu;
> +     long long msgid, model;
> +     int msgsz;
> +     char *msgflags, *secparams;
> +     size_t msgflagslen, secparamslen;
> +     struct ber_element *message = NULL, *payload, *scopedpdu, *ctxname;
> +     off_t secparamsoffset;
> +     char *encpdu, *engineid;
> +     size_t encpdulen, engineidlen;
> +     void *cookie = NULL;
> +
> +     bzero(&ber, sizeof(ber));
> +     ber_set_application(&ber, smi_application);
> +
> +     ber_set_readbuf(&ber, buf, buflen);
> +     if ((message = ber_read_elements(&ber, NULL)) == NULL)
> +             return NULL;
>       ber_free(&ber);
> +
> +     if (ber_scanf_elements(message, "{de", &version, &payload) != 0)
> +             goto fail;
> +
> +     if (version != agent->version)
> +             goto fail;
> +
> +     switch (version) {
> +     case SNMP_V1:
> +     case SNMP_V2C:
> +             if (ber_scanf_elements(payload, "se", &community, &pdu) == -1)
> +                     goto fail;
> +             ber_unlink_elements(payload);
> +             ber_free_elements(message);
> +             return pdu;
> +     case SNMP_V3:
> +             if (ber_scanf_elements(payload, "{idxi}pxe", &msgid, &msgsz,
> +                 &msgflags, &msgflagslen, &model, &secparamsoffset,
> +                 &secparams, &secparamslen, &scopedpdu) == -1)
> +                     goto fail;
> +             if (msgflagslen != 1)
> +                     goto fail;
> +             if (agent->v3->sec->parseparams(agent, buf, buflen,
> +                 secparamsoffset, secparams, secparamslen, msgflags[0],
> +                 &cookie) == -1) {
> +                     cookie = NULL;
> +                     goto fail;
> +             }
> +             if (msgflags[0] & SNMP_MSGFLAG_PRIV) {
> +                     if (ber_scanf_elements(scopedpdu, "x", &encpdu,
> +                         &encpdulen) == -1)
> +                             goto fail;
> +                     if ((scopedpdu = agent->v3->sec->decpdu(agent, encpdu,
> +                         encpdulen, cookie)) == NULL)
> +                             goto fail;
> +             }
> +             if (ber_scanf_elements(scopedpdu, "{xeS{", &engineid,
> +                 &engineidlen, &ctxname) == -1)
> +                     goto fail;
> +             if (!agent->v3->engineidset) {
> +                     if (snmp_v3_setengineid(agent->v3, engineid,
> +                         engineidlen) == -1)
> +                             goto fail;
> +             }
> +             pdu = ber_unlink_elements(ctxname);
> +             /* Accept reports, so we can continue if possible */
> +             if (pdu->be_type != SNMP_C_REPORT) {
> +                     if ((msgflags[0] & SNMP_MSGFLAG_SECMASK) !=
> +                         (agent->v3->level & SNMP_MSGFLAG_SECMASK))
> +                             goto fail;
> +             }
> +
> +             ber_free_elements(message);
> +             agent->v3->sec->freecookie(cookie);
> +             return pdu;
> +     }
> +     /* NOTREACHED */
> +
> +fail:
> +     if (version == SNMP_V3)
> +             agent->v3->sec->freecookie(cookie);
> +     ber_free_elements(message);
>       return NULL;
>  }
> +
> +static void
> +snmp_v3_secparamsoffset(void *cookie, size_t offset)
> +{
> +     size_t *spoffset = cookie;
> +
> +     *spoffset = offset;
> +}
> diff --git a/snmp.h b/snmp.h
> index 502aa75..ce62119 100644
> --- a/snmp.h
> +++ b/snmp.h
> @@ -108,12 +108,43 @@ enum snmp_security_model {
>       SNMP_SEC_TSM            = 4
>  };
>  
> +struct snmp_agent;
> +
> +struct snmp_sec {
> +     enum snmp_security_model model;
> +     int (*init)(struct snmp_agent *);
> +     char *(*genparams)(struct snmp_agent *, size_t *, void **);
> +     struct ber_element *(*encpdu)(struct snmp_agent *,
> +         struct ber_element *, void *);
> +     int (*finalparams)(struct snmp_agent *, char *, size_t, size_t, void *);
> +     int (*parseparams)(struct snmp_agent *, char *, size_t, off_t, char *,
> +         size_t, uint8_t, void **);
> +     struct ber_element *(*decpdu)(struct snmp_agent *, char *, size_t,
> +         void *);
> +     void (*free)(void *);
> +     void (*freecookie)(void *);
> +     void *data;
> +};
> +
> +struct snmp_v3 {
> +     uint8_t level;
> +     char *ctxname;
> +     size_t ctxnamelen;
> +     int engineidset;
> +     char *engineid;
> +     size_t engineidlen;
> +     struct snmp_sec *sec;
> +};
> +
>  struct snmp_agent {
>       int fd;
> -     enum snmp_version version;
> -     char *community;
>       int timeout;
>       int retries;
> +     enum snmp_version version;
> +/* SNMP_V1 & SNMP_V2C */
> +     char *community;
> +/* SNMP_V3 */
> +     struct snmp_v3 *v3;
>  };
>  
>  #define SNMP_MSGFLAG_AUTH    0x01
> @@ -123,7 +154,10 @@ struct snmp_agent {
>  
>  #define SNMP_MAX_TIMEWINDOW  150     /* RFC3414 */
>  
> +struct snmp_v3 *snmp_v3_init(int, const char *, size_t, struct snmp_sec *);
> +int snmp_v3_setengineid(struct snmp_v3 *, char *, size_t);
>  struct snmp_agent *snmp_connect_v12(int, enum snmp_version, const char *);
> +struct snmp_agent *snmp_connect_v3(int, struct snmp_v3 *);
>  void snmp_free_agent(struct snmp_agent *);
>  struct ber_element *
>      snmp_get(struct snmp_agent *agent, struct ber_oid *oid, size_t len);
> diff --git a/snmpc.c b/snmpc.c
> index dc48b10..658ca82 100644
> --- a/snmpc.c
> +++ b/snmpc.c
> @@ -23,8 +23,10 @@
>  #include <sys/un.h>
>  
>  #include <arpa/inet.h>
> +#include <openssl/evp.h>
>  
>  #include <ber.h>
> +#include <ctype.h>
>  #include <err.h>
>  #include <errno.h>
>  #include <netdb.h>
> @@ -38,16 +40,19 @@
>  
>  #include "smi.h"
>  #include "snmp.h"
> +#include "usm.h"
>  
> -#define GETOPT_COMMON                "c:r:t:v:O:"
> +#define GETOPT_COMMON                "A:a:c:E:e:K:k:l:n:O:r:t:u:v:X:x:Z:"
>  
>  int snmpc_get(int, char *[]);
>  int snmpc_walk(int, char *[]);
>  int snmpc_trap(int, char *[]);
>  int snmpc_mibtree(int, char *[]);
> +struct snmp_agent *snmpc_connect(char *, char *);
>  int snmpc_parseagent(char *, char *);
>  int snmpc_print(struct ber_element *);
>  __dead void snmpc_printerror(enum snmp_error, char *);
> +char *snmpc_hex2bin(char *, size_t *);
>  void usage(void);
>  
>  struct snmp_app {
> @@ -70,10 +75,11 @@ struct snmp_app snmp_apps[] = {
>  struct snmp_app *snmp_app = NULL;
>  
>  char *community = "public";
> +struct snmp_v3 *v3;
>  char *mib = "mib_2";
>  int retries = 5;
>  int timeout = 1;
> -int version = SNMP_V2C;
> +enum snmp_version version = SNMP_V2C;
>  int print_equals = 1;
>  int print_varbind_only = 0;
>  int print_summary = 0;
> @@ -91,6 +97,22 @@ enum smi_output_string output_string = smi_os_default;
>  int
>  main(int argc, char *argv[])
>  {
> +     const EVP_MD *md = NULL;
> +     const EVP_CIPHER *cipher = NULL;
> +     struct snmp_sec *sec;
> +     char *user = NULL;
> +     enum usm_key_level authkeylevel;
> +     char *authkey = NULL;
> +     size_t authkeylen = 0;
> +     enum usm_key_level privkeylevel;
> +     char *privkey = NULL;
> +     size_t privkeylen = 0;
> +     int seclevel = SNMP_MSGFLAG_REPORT;
> +     char *ctxname = NULL;
> +     char *ctxengineid = NULL, *secengineid = NULL;
> +     size_t ctxengineidlen, secengineidlen;
> +     int zflag = 0;
> +     long long boots, time;
>       char optstr[BUFSIZ];
>       const char *errstr;
>       char *strtolp;
> @@ -130,9 +152,86 @@ main(int argc, char *argv[])
>  
>       while ((ch = getopt(argc, argv, optstr)) != -1) {
>               switch (ch) {
> +             case 'A':
> +                     authkey = optarg;
> +                     authkeylen = strlen(authkey);
> +                     authkeylevel = USM_KEY_PASSWORD;
> +                     break;
> +             case 'a':
> +                     if (strcasecmp(optarg, "MD5") == 0)
> +                             md = EVP_md5();
> +                     else if (strcasecmp(optarg, "SHA") == 0)
> +                             md = EVP_sha1();
> +                     else if (strcasecmp(optarg, "SHA-224") == 0)
> +                             md = EVP_sha224();
> +                     else if (strcasecmp(optarg, "SHA-256") == 0)
> +                             md = EVP_sha256();
> +                     else if (strcasecmp(optarg, "SHA-384") == 0)
> +                             md = EVP_sha384();
> +                     else if (strcasecmp(optarg, "SHA-512") == 0)
> +                             md = EVP_sha512();
> +                     else
> +                             errx(1, "Invalid authentication protocol "
> +                                 "specified after -a flag: %s", optarg);
> +                     break;
>               case 'c':
>                       community = optarg;
>                       break;
> +             case 'E':
> +                     ctxengineid = snmpc_hex2bin(optarg,
> +                         &ctxengineidlen);
> +                     if (ctxengineid == NULL) {
> +                             if (errno == EINVAL)
> +                                     errx(1, "Bad engine ID value "
> +                                         "after -3E flag.");
> +                             err(1, "-3E");
> +                     }
> +                     break;
> +             case 'e':
> +                     secengineid = snmpc_hex2bin(optarg,
> +                         &secengineidlen);
> +                     if (secengineid == NULL) {
> +                             if (errno == EINVAL)
> +                                     errx(1, "Bad engine ID value "
> +                                         "after -3e flag.");
> +                             err(1, "-3e");
> +                     }
> +                     break;
> +             case 'K':
> +                     privkey = snmpc_hex2bin(optarg, &privkeylen);
> +                     if (privkey == NULL) {
> +                             if (errno == EINVAL)
> +                                     errx(1, "Bad key value after "
> +                                         "-3K flag.");
> +                             errx(1, "-3K");
> +                     }
> +                     privkeylevel = USM_KEY_LOCALIZED;
> +                             break;
> +             case 'k':
> +                     authkey = snmpc_hex2bin(optarg, &authkeylen);
> +                     if (authkey == NULL) {
> +                             if (errno == EINVAL)
> +                                     errx(1, "Bad key value after -k flag.");
> +                             err(1, "-k");
> +                     }
> +                     authkeylevel = USM_KEY_LOCALIZED;
> +                     break;
> +             case 'l':
> +                     if (strcmp(optarg, "noAuthNoPriv") == 0)
> +                             seclevel = SNMP_MSGFLAG_REPORT;
> +                     else if (strcmp(optarg, "authNoPriv") == 0)
> +                             seclevel = SNMP_MSGFLAG_AUTH |
> +                                 SNMP_MSGFLAG_REPORT;
> +                     else if (strcmp(optarg, "authPriv") == 0)
> +                             seclevel = SNMP_MSGFLAG_AUTH |
> +                                 SNMP_MSGFLAG_PRIV | SNMP_MSGFLAG_REPORT;
> +                     else
> +                             errx(1, "Invalid security level specified "
> +                                 "after -l flag: %s", optarg);
> +                     break;
> +             case 'n':
> +                     ctxname = optarg;
> +                     break;
>               case 'r':
>                       if ((retries = strtonum(optarg, 0, INT_MAX,
>                           &errstr)) == 0) {
> @@ -147,11 +246,16 @@ main(int argc, char *argv[])
>                                       errx(1, "-t: %s argument", errstr);
>                       }
>                       break;
> +             case 'u':
> +                     user = optarg;
> +                     break;
>               case 'v':
>                       if (strcmp(optarg, "1") == 0)
>                               version = SNMP_V1;
>                       else if (strcmp(optarg, "2c") == 0)
>                               version = SNMP_V2C;
> +                     else if (strcmp(optarg, "3") == 0)
> +                             version = SNMP_V3;
>                       else
>                               errc(1, EINVAL, "-v");
>                       break;
> @@ -282,6 +386,33 @@ main(int argc, char *argv[])
>                               }
>                       }
>                       break;
> +             case 'X':
> +                     privkey = optarg;
> +                     privkeylen = strlen(privkey);
> +                     privkeylevel = USM_KEY_PASSWORD;
> +                     break;
> +             case 'x':
> +                     if (strcasecmp(optarg, "DES") == 0)
> +                             cipher = EVP_des_cbc();
> +                     else if (strcasecmp(optarg, "AES") == 0)
> +                             cipher = EVP_aes_128_cfb128();
> +                     else
> +                             errx(1, "Invalid privacy protocol "
> +                                 "specified after -3x flag: %s",
> +                                 optarg);
> +                     break;
> +             case 'Z':
> +                     boots = strtoll(optarg, &strtolp, 10);
> +                     if (boots < 0 || strtolp == optarg || strtolp[0] != ',')
> +                             usage();
> +                     strtolp++;
> +                     while (strtolp[0] == ' ' && strtolp[0] == '\t')
> +                             strtolp++;
> +                     time = strtoll(strtolp, &strtolp, 10);
> +                     if (boots < 0 || strtolp == optarg)
> +                             usage();
> +                     zflag = 1;
> +                     break;
>               default:
>                       usage();
>               }
> @@ -289,6 +420,50 @@ main(int argc, char *argv[])
>       argc -= optind;
>       argv += optind;
>  
> +     if (version == SNMP_V3) {
> +             /* Setup USM */
> +             if (user == NULL || user[0] == '\0')
> +                     errx(1, "No securityName specified");
> +             if ((sec = usm_init(user, strlen(user))) == NULL)
> +                     err(1, "usm_init");
> +             if (seclevel & SNMP_MSGFLAG_AUTH) {
> +                     if (md == NULL)
> +                             md = EVP_md5();
> +                     if (authkey == NULL)
> +                             errx(1, "No authKey or authPassword specified");
> +                     if (usm_setauth(sec, md, authkey, authkeylen,
> +                         authkeylevel) == -1)
> +                             err(1, "Can't set authkey");
> +             }
> +             if (seclevel & SNMP_MSGFLAG_PRIV) {
> +                     if (cipher == NULL)
> +                             cipher = EVP_des_cbc();
> +                     if (privkey == NULL)
> +                             errx(1, "No privKey or privPassword specified");
> +                     if (usm_setpriv(sec, cipher, privkey, privkeylen,
> +                         privkeylevel) == -1)
> +                             err(1, "Can't set authkey");
> +             }
> +             if (secengineid != NULL) {
> +                     if (usm_setengineid(sec, secengineid,
> +                         secengineidlen) == -1)
> +                             err(1, "Can't set secengineid");
> +             }
> +             if (zflag)
> +                     if (usm_setbootstime(sec, boots, time) == -1)
> +                             err(1, "Can't set boots/time");
> +             v3 = snmp_v3_init(seclevel, ctxname, ctxname == NULL ? 0 :
> +                 strlen(ctxname), sec);
> +             if (v3 == NULL)
> +                     err(1, "snmp_v3_init");
> +             if (ctxengineid != NULL) {
> +                     if (snmp_v3_setengineid(v3, ctxengineid,
> +                         ctxengineidlen) == -1)
> +                             err(1, "Can't set ctxengineid");
> +             }
> +     }
> +
> +
>       return snmp_app->exec(argc, argv);
>  }
>  
> @@ -300,13 +475,13 @@ snmpc_get(int argc, char *argv[])
>       struct snmp_agent *agent;
>       int errorstatus, errorindex;
>       int i;
> +     int class;
> +     unsigned type;
>  
>       if (argc < 2)
>               usage();
>  
> -     agent = snmp_connect_v12(snmpc_parseagent(argv[0], "161"), version,
> -         community);
> -     if (agent == NULL)
> +     if ((agent = snmpc_connect(argv[0], "161")) == NULL)
>               err(1, "%s", snmp_app->name);
>       agent->timeout = timeout;
>       agent->retries = retries;
> @@ -339,12 +514,14 @@ snmpc_get(int argc, char *argv[])
>                       err(1, "get");
>       }
>  
> -     (void) ber_scanf_elements(pdu, "{Sdd{e", &errorstatus, &errorindex,
> -         &varbind);
> +     (void) ber_scanf_elements(pdu, "t{Sdd{e", &class, &type, &errorstatus,
> +         &errorindex, &varbind);
>       if (errorstatus != 0)
>               snmpc_printerror((enum snmp_error) errorstatus,
>                   argv[errorindex - 1]);
>  
> +     if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
> +             printf("Received report:\n");
>       for (; varbind != NULL; varbind = varbind->be_next) {
>               if (!snmpc_print(varbind))
>                       err(1, "Can't print response");
> @@ -365,6 +542,8 @@ snmpc_walk(int argc, char *argv[])
>       char oidstr[SNMP_MAX_OID_STRLEN];
>       int n = 0, prev_cmp;
>       int errorstatus, errorindex;
> +     int class;
> +     unsigned type;
>  
>       if (strcmp(snmp_app->name, "bulkwalk") == 0 && version < SNMP_V2C)
>               errx(1, "Cannot send V2 PDU on V1 session");
> @@ -372,8 +551,7 @@ snmpc_walk(int argc, char *argv[])
>               usage();
>       oids = argc == 1 ? mib : argv[1];
>  
> -     agent = snmp_connect_v12(snmpc_parseagent(argv[0], "161"), version, 
> community);
> -     if (agent == NULL)
> +     if ((agent = snmpc_connect(argv[0], "161"))== NULL)
>               err(1, "%s", snmp_app->name);
>       agent->timeout = timeout;
>       agent->retries = retries;
> @@ -390,15 +568,19 @@ snmpc_walk(int argc, char *argv[])
>               if ((pdu = snmp_get(agent, &oid, 1)) == NULL)
>                       err(1, "%s", snmp_app->name);
>  
> -             (void) ber_scanf_elements(pdu, "{Sdd{e", &errorstatus,
> -                 &errorindex, &varbind);
> +             (void) ber_scanf_elements(pdu, "t{Sdd{e", &class, &type,
> +                 &errorstatus, &errorindex, &varbind);
>               if (errorstatus != 0)
>                       snmpc_printerror((enum snmp_error) errorstatus,
>                           argv[errorindex - 1]);
>  
> +             if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
> +                     printf("Received report:\n");
>               if (!snmpc_print(varbind))
>                       err(1, "Can't print response");
>               ber_free_element(pdu);
> +             if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
> +                     return 1;
>               n++;
>       }
>       while (1) {
> @@ -412,14 +594,16 @@ snmpc_walk(int argc, char *argv[])
>                               err(1, "walk");
>               }
>  
> -             (void) ber_scanf_elements(pdu, "{Sdd{e", &errorstatus,
> -                 &errorindex, &varbind);
> +             (void) ber_scanf_elements(pdu, "t{Sdd{e", &class, &type,
> +                 &errorstatus, &errorindex, &varbind);
>               if (errorstatus != 0) {
>                       smi_oid2string(&noid, oidstr, sizeof(oidstr),
>                           oid_lookup);
>                       snmpc_printerror((enum snmp_error) errorstatus, oidstr);
>               }
>  
> +             if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
> +                     printf("Received report:\n");
>               for (; varbind != NULL; varbind = varbind->be_next) {
>                       (void) ber_scanf_elements(varbind, "{oe}", &noid,
>                           &value);
> @@ -440,6 +624,8 @@ snmpc_walk(int argc, char *argv[])
>                       n++;
>               }
>               ber_free_elements(pdu);
> +             if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
> +                     return 1;
>               if (varbind != NULL)
>                       break;
>       }
> @@ -447,15 +633,19 @@ snmpc_walk(int argc, char *argv[])
>               if ((pdu = snmp_get(agent, &oid, 1)) == NULL)
>                       err(1, "%s", snmp_app->name);
>  
> -             (void) ber_scanf_elements(pdu, "{Sdd{e", &errorstatus,
> -                 &errorindex, &varbind);
> +             (void) ber_scanf_elements(pdu, "t{Sdd{e", &class, &type,
> +                 &errorstatus, &errorindex, &varbind);
>               if (errorstatus != 0)
>                       snmpc_printerror((enum snmp_error) errorstatus,
>                           argv[errorindex - 1]);
>  
> +             if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
> +                     printf("Received report:\n");
>               if (!snmpc_print(varbind))
>                       err(1, "Can't print response");
>               ber_free_element(pdu);
> +             if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
> +                     return 1;
>               n++;
>       }
>       if (print_time)
> @@ -495,9 +685,7 @@ snmpc_trap(int argc, char *argv[])
>       if (version == SNMP_V1)
>               errx(1, "trap is not supported for snmp v1");
>  
> -     agent = snmp_connect_v12(snmpc_parseagent(argv[0], "162"),
> -         version, community);
> -     if (agent == NULL)
> +     if ((agent = snmpc_connect(argv[0], "162")) == NULL)
>               err(1, "%s", snmp_app->name);
>  
>       if (pledge("stdio", NULL) == -1)
> @@ -693,6 +881,20 @@ snmpc_mibtree(int argc, char *argv[])
>       return 0;
>  }
>  
> +struct snmp_agent *
> +snmpc_connect(char *host, char *port)
> +{
> +     switch (version) {
> +     case SNMP_V1:
> +     case SNMP_V2C:
> +             return snmp_connect_v12(snmpc_parseagent(host, port), version,
> +                 community);
> +     case SNMP_V3:
> +             return snmp_connect_v3(snmpc_parseagent(host, port), v3);
> +     }
> +     return NULL;
> +}
> +
>  int
>  snmpc_print(struct ber_element *elm)
>  {
> @@ -875,18 +1077,61 @@ snmpc_parseagent(char *agent, char *defaultport)
>       return s;
>  }
>  
> +char *
> +snmpc_hex2bin(char *hexstr, size_t *binlen)
> +{
> +     char *decstr;
> +
> +     if (hexstr[0] == '0' && hexstr[1] == 'x')
> +             hexstr += 2;
> +     while (hexstr[0] == ' ' || hexstr[0] == '\t')
> +             hexstr++;
> +
> +     if ((decstr = malloc((strlen(hexstr) / 2) + 1)) == NULL)
> +             return NULL;
> +
> +     for (*binlen = 0; hexstr[0] != '\0'; (*binlen)++) {
> +             hexstr[0] = toupper(hexstr[0]);
> +             hexstr[1] = toupper(hexstr[1]);
> +             if (hexstr[0] >= '0' && hexstr[0] <= '9')
> +                     decstr[*binlen] = (hexstr[0] - '0') << 4;
> +             else if (hexstr[0] >= 'A' && hexstr[0] <= 'F')
> +                     decstr[*binlen] = ((hexstr[0] - 'A') + 10) << 4;
> +             else
> +                     goto fail;
> +             if (hexstr[1] >= '0' && hexstr[1] <= '9')
> +                     decstr[*binlen] |= (hexstr[1] - '0');
> +             else if (hexstr[1] >= 'A' && hexstr[1] <= 'F')
> +                     decstr[*binlen] |= (hexstr[1] - 'A') + 10;
> +             else
> +                     goto fail;
> +
> +             hexstr += 2;
> +             while (hexstr[0] == ' ' || hexstr[0] == '\t')
> +                     hexstr++;
> +     }
> +
> +     return decstr;
> +fail:
> +     errno = EINVAL;
> +     free(decstr);
> +     return NULL;
> +}
> +
>  __dead void
>  usage(void)
>  {
>       size_t i;
>  
>       if (snmp_app != NULL) {
> -             fprintf(stderr, "usage: snmp %s%s%s%s\n",
> +             fprintf(stderr, "usage: snmp %s%s%s\n",
>                   snmp_app->name,
>                   snmp_app->usecommonopt ?
> -                 " [-c community] [-r retries] [-t timeout] [-v version]\n"
> -                 "            [-O afnqvxSQ]" : "",
> -                 snmp_app->usage == NULL ? "" : " ",
> +                 " [-A authpass] [-a digest] [-c community] [-e 
> secengineid]\n"
> +                 "            [-E ctxengineid] [-K localpriv] [-k localauth] 
> [-l seclevel]\n"
> +                 "            [-n ctxname] [-O afnqvxSQ] [-r retries] [-t 
> timeout] [-u user]\n"
> +                 "            [-v version] [-X privpass] [-x cipher] [-Z 
> boots,time]\n"
> +                 "            " : "",
>                   snmp_app->usage == NULL ? "" : snmp_app->usage);
>               exit(1);
>       }
> @@ -898,8 +1143,7 @@ usage(void)
>               fprintf(stderr, "snmp %s%s %s\n",
>                   snmp_apps[i].name,
>                   snmp_apps[i].usecommonopt ?
> -                 " [-c community] [-r retries] [-t timeout] [-v version]\n"
> -                 "            [-O afnqvxSQ]" : "",
> +                 " [common options]" : "",
>                   snmp_apps[i].usage ? snmp_apps[i].usage : "");
>       }
>       exit(1);
> diff --git a/usm.c b/usm.c
> new file mode 100644
> index 0000000..f34a6c9
> --- /dev/null
> +++ b/usm.c
> @@ -0,0 +1,685 @@
> +/*   $OpenBSD: usm.c,v 1.16 2019/06/11 05:36:32 martijn Exp $        */
> +
> +/*
> + * Copyright (c) 2019 Martijn van Duren <mart...@openbsd.org>
> + *
> + * Permission to use, copy, modify, and distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +#include <sys/time.h>
> +
> +#include <openssl/evp.h>
> +#include <openssl/hmac.h>
> +
> +#include <ber.h>
> +#include <errno.h>
> +#include <string.h>
> +#include <time.h>
> +
> +#include "smi.h"
> +#include "snmp.h"
> +#include "usm.h"
> +
> +#define USM_MAX_DIGESTLEN 48
> +#define USM_MAX_TIMEWINDOW 150
> +#define USM_SALTOFFSET 8
> +
> +struct usm_sec {
> +     struct snmp_sec snmp;
> +     char *user;
> +     size_t userlen;
> +     int engineidset;
> +     char *engineid;
> +     size_t engineidlen;
> +     enum usm_key_level authlevel;
> +     const EVP_MD *digest;
> +     char *authkey;
> +     enum usm_key_level privlevel;
> +     const EVP_CIPHER *cipher;
> +     char *privkey;
> +     int bootsset;
> +     uint32_t boots;
> +     int timeset;
> +     uint32_t time;
> +     struct timespec timecheck;
> +};
> +
> +struct usm_cookie {
> +     size_t digestoffset;
> +     long long salt;
> +     uint32_t boots;
> +     uint32_t time;
> +};
> +
> +static int usm_doinit(struct snmp_agent *);
> +static char *usm_genparams(struct snmp_agent *, size_t *, void **);
> +static int usm_finalparams(struct snmp_agent *, char *, size_t, size_t, void 
> *);
> +static struct ber_element *usm_encpdu(struct snmp_agent *agent,
> +    struct ber_element *pdu, void *cookie);
> +static char *usm_crypt(const EVP_CIPHER *, int, char *, struct usm_cookie *,
> +    char *, size_t, size_t *);
> +static int usm_parseparams(struct snmp_agent *, char *, size_t, off_t, char 
> *,
> +    size_t, uint8_t, void **);
> +struct ber_element *usm_decpdu(struct snmp_agent *, char *, size_t, void *);
> +static void usm_digest_pos(void *, size_t);
> +static void usm_free(void *);
> +static char *usm_passwd2mkey(const EVP_MD *, const char *);
> +static char *usm_mkey2lkey(struct usm_sec *, const EVP_MD *, const char *);
> +static size_t usm_digestlen(const EVP_MD *);
> +
> +struct snmp_sec *
> +usm_init(const char *user, size_t userlen)
> +{
> +     struct snmp_sec *sec;
> +     struct usm_sec *usm;
> +
> +     if (user == NULL || user[0] == '\0') {
> +             errno = EINVAL;
> +             return NULL;
> +     }
> +
> +     if ((sec = malloc(sizeof(*sec))) == NULL)
> +             return NULL;
> +
> +     if ((usm = calloc(1, sizeof(struct usm_sec))) == NULL) {
> +             free(sec);
> +             return NULL;
> +     }
> +     if ((usm->user = malloc(userlen)) == NULL) {
> +             free(sec);
> +             free(usm);
> +             return NULL;
> +     }
> +     memcpy(usm->user, user, userlen);
> +     usm->userlen = userlen;
> +
> +     sec->model = SNMP_SEC_USM;
> +     sec->init = usm_doinit;
> +     sec->genparams = usm_genparams;
> +     sec->encpdu = usm_encpdu;
> +     sec->parseparams = usm_parseparams;
> +     sec->decpdu = usm_decpdu;
> +     sec->finalparams = usm_finalparams;
> +     sec->free = usm_free;
> +     sec->freecookie = free;
> +     sec->data = usm;
> +     return sec;
> +}
> +
> +static int
> +usm_doinit(struct snmp_agent *agent)
> +{
> +     struct ber_element *ber;
> +     struct usm_sec *usm = agent->v3->sec->data;
> +     int level;
> +     size_t userlen;
> +
> +     if (usm->engineidset && usm->bootsset && usm->timeset)
> +             return 0;
> +
> +     level = agent->v3->level;
> +     agent->v3->level = SNMP_MSGFLAG_REPORT;
> +     userlen = usm->userlen;
> +     usm->userlen = 0;
> +
> +     if ((ber = snmp_get(agent, NULL, 0)) == NULL) {
> +             agent->v3->level = level;
> +             usm->userlen = userlen;
> +             return -1;
> +     }
> +     ber_free_element(ber);
> +
> +     agent->v3->level = level;
> +     usm->userlen = userlen;
> +
> +     /* Ugly hack for HP Laserjet */
> +     if (!usm->engineidset || !usm->bootsset || !usm->timeset) {
> +             if ((ber = snmp_get(agent, NULL, 0)) == NULL)
> +                     return -1;
> +             ber_free_element(ber);
> +     }
> +     return 0;
> +}
> +
> +static char *
> +usm_genparams(struct snmp_agent *agent, size_t *len, void **cookie)
> +{
> +     struct ber ber;
> +     struct ber_element *params, *digestelm;
> +     struct usm_sec *usm = agent->v3->sec->data;
> +     char digest[USM_MAX_DIGESTLEN];
> +     char *secparams = NULL, *buf;
> +     ssize_t berlen;
> +     struct usm_cookie *usmcookie;
> +     struct timespec now, timediff;
> +
> +     bzero(digest, sizeof(digest));
> +
> +     if ((usmcookie = calloc(1, sizeof(*usmcookie))) == NULL)
> +             return NULL;
> +     *cookie = usmcookie;
> +
> +     arc4random_buf(&(usmcookie->salt), sizeof(usmcookie->salt));
> +     if (usm->timeset) {
> +             if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) {
> +                     free(usmcookie);
> +                     return NULL;
> +             }
> +             timespecsub(&now, &(usm->timecheck), &timediff);
> +             usmcookie->time = usm->time + timediff.tv_sec;
> +     } else
> +             usmcookie->time = 0;
> +     usmcookie->boots = usm->boots;
> +
> +     if ((params = ber_printf_elements(NULL, "{xddxxx}", usm->engineid,
> +         usm->engineidlen, usmcookie->boots, usmcookie->time, usm->user,
> +         usm->userlen, digest, agent->v3->level & SNMP_MSGFLAG_AUTH ?
> +         usm_digestlen(usm->digest) : (size_t) 0, &(usmcookie->salt),
> +         agent->v3->level & SNMP_MSGFLAG_AUTH ? sizeof(usmcookie->salt) :
> +         (size_t) 0)) == NULL) {
> +             free(usmcookie);
> +             return NULL;
> +     }
> +
> +     if (ber_scanf_elements(params, "{SSSSe",  &digestelm) == -1) {
> +             ber_free_element(params);
> +             free(usmcookie);
> +             return NULL;
> +     }
> +
> +     ber_set_writecallback(digestelm, usm_digest_pos, usmcookie);
> +
> +     bzero(&ber, sizeof(ber));
> +     ber_set_application(&ber, smi_application);
> +     if ((berlen = ber_write_elements(&ber, params)) != -1 &&
> +         ber_get_writebuf(&ber, (void **)&buf) != -1 &&
> +         (secparams = malloc(berlen)) != NULL)
> +             memcpy(secparams, buf, berlen);
> +     *len = berlen;
> +     ber_free_element(params);
> +     ber_free(&ber);
> +     return secparams;
> +}
> +
> +static struct ber_element *
> +usm_encpdu(struct snmp_agent *agent, struct ber_element *pdu, void *cookie)
> +{
> +     struct usm_sec *usm = agent->v3->sec->data;
> +     struct usm_cookie *usmcookie = cookie;
> +     struct ber ber;
> +     struct ber_element *retpdu;
> +     char *serialpdu, *encpdu;
> +     ssize_t pdulen;
> +     size_t encpdulen;
> +
> +     bzero(&ber, sizeof(ber));
> +     ber_set_application(&ber, smi_application);
> +     pdulen = ber_write_elements(&ber, pdu);
> +     if (pdulen == -1)
> +             return NULL;
> +
> +     ber_get_writebuf(&ber, (void **)&serialpdu);
> +
> +     encpdu = usm_crypt(usm->cipher, 1, usm->privkey, usmcookie, serialpdu,
> +         pdulen, &encpdulen);
> +     ber_free(&ber);
> +     if (encpdu == NULL)
> +             return NULL;
> +
> +     retpdu = ber_add_nstring(NULL, encpdu, encpdulen);
> +     free(encpdu);
> +     return retpdu;
> +}
> +
> +static char *
> +usm_crypt(const EVP_CIPHER *cipher, int do_enc, char *key,
> +    struct usm_cookie *cookie, char *serialpdu, size_t pdulen, size_t 
> *outlen)
> +{
> +     EVP_CIPHER_CTX ctx;
> +     size_t i;
> +     char iv[EVP_MAX_IV_LENGTH];
> +     char *salt = (char *)&(cookie->salt);
> +     char *outtext;
> +     int len, len2;
> +     uint32_t ivv;
> +
> +     switch (EVP_CIPHER_type(cipher)) {
> +     case NID_des_cbc:
> +             /* RFC3414, chap 8.1.1.1. */
> +             for (i = 0; i < 8; i++)
> +                     iv[i] = salt[i] ^ key[USM_SALTOFFSET + i];
> +             break;
> +     case NID_aes_128_cfb128:
> +             /* RFC3826, chap 3.1.2.1. */
> +             ivv = htobe32(cookie->boots);
> +             memcpy(iv, &ivv, sizeof(ivv));
> +             ivv = htobe32(cookie->time);
> +             memcpy(iv + sizeof(ivv), &ivv, sizeof(ivv));
> +             memcpy(iv + 2 * sizeof(ivv), &(cookie->salt),
> +                 sizeof(cookie->salt));
> +             break;
> +     default:
> +             return NULL;
> +     }
> +
> +     bzero(&ctx, sizeof(ctx));
> +     if (!EVP_CipherInit(&ctx, cipher, key, iv, do_enc))
> +             return NULL;
> +
> +     EVP_CIPHER_CTX_set_padding(&ctx, do_enc);
> +
> +     *outlen = EVP_CIPHER_block_size(cipher);
> +     /* Maximum output size */
> +     *outlen = pdulen + (*outlen - (pdulen % *outlen));
> +
> +     if ((outtext = malloc(*outlen)) == NULL)
> +             return NULL;
> +
> +     if (EVP_CipherUpdate(&ctx, outtext, &len, serialpdu, pdulen) &&
> +         EVP_CipherFinal_ex(&ctx, outtext + len, &len2))
> +             *outlen = len + len2;
> +     else {
> +             free(outtext);
> +             outtext = NULL;
> +     }
> +
> +     EVP_CIPHER_CTX_cleanup(&ctx);
> +
> +     return outtext;
> +}
> +
> +static int
> +usm_finalparams(struct snmp_agent *agent, char *buf, size_t buflen,
> +    size_t secparamsoffset, void *cookie)
> +{
> +     struct usm_sec *usm = agent->v3->sec->data;
> +     struct usm_cookie *usmcookie = cookie;
> +     u_char digest[EVP_MAX_MD_SIZE];
> +
> +     if ((agent->v3->level & SNMP_MSGFLAG_AUTH) == 0)
> +             return 0;
> +
> +     if (usm->authlevel != USM_KEY_LOCALIZED)
> +             return -1;
> +
> +     if (HMAC(usm->digest, usm->authkey, EVP_MD_size(usm->digest), buf,
> +         buflen, digest, NULL) == NULL)
> +             return -1;
> +
> +     memcpy(buf + secparamsoffset + usmcookie->digestoffset, digest,
> +         usm_digestlen(usm->digest));
> +     return 0;
> +}
> +
> +static int
> +usm_parseparams(struct snmp_agent *agent, char *packet, size_t packetlen,
> +    off_t secparamsoffset, char *buf, size_t buflen, uint8_t level,
> +    void **cookie)
> +{
> +     struct usm_sec *usm = agent->v3->sec->data;
> +     struct ber ber;
> +     struct ber_element *secparams;
> +     char *engineid, *user, *digest, *salt;
> +     size_t engineidlen, userlen, digestlen, saltlen;
> +     struct timespec now, timediff;
> +     off_t digestoffset;
> +     char exp_digest[EVP_MAX_MD_SIZE];
> +     struct usm_cookie *usmcookie;
> +
> +     bzero(&ber, sizeof(ber));
> +     bzero(exp_digest, sizeof(exp_digest));
> +
> +     ber_set_application(&ber, smi_application);
> +     ber_set_readbuf(&ber, buf, buflen);
> +     if ((secparams = ber_read_elements(&ber, NULL)) == NULL)
> +             return -1;
> +     ber_free(&ber);
> +
> +     if ((usmcookie = malloc(sizeof(*usmcookie))) == NULL)
> +             goto fail;
> +     *cookie = usmcookie;
> +
> +     if (ber_scanf_elements(secparams, "{xddxpxx}", &engineid, &engineidlen,
> +         &(usmcookie->boots), &(usmcookie->time), &user, &userlen,
> +         &digestoffset, &digest, &digestlen, &salt, &saltlen) == -1)
> +             goto fail;
> +     if (saltlen != sizeof(usmcookie->salt) && saltlen != 0)
> +             goto fail;
> +     memcpy(&(usmcookie->salt), salt, saltlen);
> +
> +     if (!usm->engineidset) {
> +             if (usm_setengineid(agent->v3->sec, engineid,
> +                 engineidlen) == -1)
> +                     goto fail;
> +     } else {
> +             if (usm->engineidlen != engineidlen)
> +                     goto fail;
> +             if (memcmp(usm->engineid, engineid, engineidlen) != 0)
> +                     goto fail;
> +     }
> +
> +     if (!usm->bootsset) {
> +             usm->boots = usmcookie->boots;
> +             usm->bootsset = 1;
> +     } else {
> +             if (usmcookie->boots < usm->boots)
> +                     goto fail;
> +             if (usmcookie->boots > usm->boots) {
> +                     usm->bootsset = 0;
> +                     usm->timeset = 0;
> +                     usm_doinit(agent);
> +                     goto fail;
> +             }
> +     }
> +
> +     if (!usm->timeset) {
> +             usm->time = usmcookie->time;
> +             if (clock_gettime(CLOCK_MONOTONIC, &usm->timecheck) == -1)
> +                     goto fail;
> +             usm->timeset = 1;
> +     } else {
> +             if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)
> +                     goto fail;
> +             timespecsub(&now, &(usm->timecheck), &timediff);
> +             if (usmcookie->time <
> +                 usm->time + timediff.tv_sec - USM_MAX_TIMEWINDOW ||
> +                 usmcookie->time >
> +                 usm->time + timediff.tv_sec + USM_MAX_TIMEWINDOW) {
> +                     usm->bootsset = 0;
> +                     usm->timeset = 0;
> +                     usm_doinit(agent);
> +                     goto fail;
> +             }
> +     }
> +     /*
> +      * Don't assume these are set if both are zero.
> +      * Ugly hack for HP Laserjet
> +      */
> +     if (usm->boots == 0 && usm->time == 0) {
> +             usm->bootsset = 0;
> +             usm->timeset = 0;
> +     }
> +
> +     if (userlen != usm->userlen ||
> +         memcmp(user, usm->user, userlen) != 0)
> +             goto fail;
> +
> +     if (level & SNMP_MSGFLAG_AUTH) {
> +             if (digestlen != usm_digestlen(usm->digest))
> +                     goto fail;
> +     }
> +     if ((agent->v3->level & SNMP_MSGFLAG_AUTH)) {
> +             bzero(packet + secparamsoffset + digestoffset, digestlen);
> +             if (HMAC(usm->digest, usm->authkey, EVP_MD_size(usm->digest), 
> packet,
> +                 packetlen, exp_digest, NULL) == NULL)
> +                     goto fail;
> +
> +             if (memcmp(exp_digest, digest, digestlen) != 0)
> +                     goto fail;
> +     } else
> +             if (digestlen != 0)
> +                     goto fail;
> +
> +     ber_free_element(secparams);
> +     return 0;
> +
> +fail:
> +     free(usmcookie);
> +     ber_free_element(secparams);
> +     return -1;
> +}
> +
> +struct ber_element *
> +usm_decpdu(struct snmp_agent *agent, char *encpdu, size_t encpdulen, void 
> *cookie)
> +{
> +     struct usm_sec *usm = agent->v3->sec->data;
> +     struct usm_cookie *usmcookie = cookie;
> +     struct ber ber;
> +     struct ber_element *scopedpdu;
> +     char *rawpdu;
> +     size_t rawpdulen;
> +
> +     if ((rawpdu = usm_crypt(usm->cipher, 0, usm->privkey, usmcookie,
> +         encpdu, encpdulen, &rawpdulen)) == NULL)
> +             return NULL;
> +
> +     bzero(&ber, sizeof(ber));
> +     ber_set_application(&ber, smi_application);
> +     ber_set_readbuf(&ber, rawpdu, rawpdulen);
> +     scopedpdu = ber_read_elements(&ber, NULL);
> +     ber_free(&ber);
> +     free(rawpdu);
> +
> +     return scopedpdu;
> +}
> +
> +static void
> +usm_digest_pos(void *data, size_t offset)
> +{
> +     struct usm_cookie *usmcookie = data;
> +
> +     usmcookie->digestoffset = offset;
> +}
> +
> +static void
> +usm_free(void *data)
> +{
> +     struct usm_sec *usm = data;
> +
> +     free(usm->user);
> +     free(usm->authkey);
> +     free(usm->privkey);
> +     free(usm->engineid);
> +     free(usm);
> +}
> +
> +int
> +usm_setauth(struct snmp_sec *sec, const EVP_MD *digest, const char *key,
> +    size_t keylen, enum usm_key_level level)
> +{
> +     struct usm_sec *usm = sec->data;
> +     char *lkey;
> +
> +     /*
> +      * We could transform a master key to a local key here if we already
> +      * have usm_setengineid called. Sine snmpc.c is the only caller at
> +      * the moment there's no need, since it always calls this function
> +      * first.
> +      */
> +     if (level == USM_KEY_PASSWORD) {
> +             if ((usm->authkey = usm_passwd2mkey(digest, key)) == NULL)
> +                     return -1;
> +             level = USM_KEY_MASTER;
> +             keylen = EVP_MD_size(digest);
> +     } else {
> +             if (keylen != (size_t)EVP_MD_size(digest)) {
> +                     errno = EINVAL;
> +                     return -1;
> +             }
> +             if ((lkey = malloc(keylen)) == NULL)
> +                     return -1;
> +             memcpy(lkey, key, keylen);
> +             usm->authkey = lkey;
> +     }
> +     usm->digest = digest;
> +     usm->authlevel = level;
> +     return 0;
> +}
> +
> +int
> +usm_setpriv(struct snmp_sec *sec, const EVP_CIPHER *cipher, const char *key,
> +    size_t keylen, enum usm_key_level level)
> +{
> +     struct usm_sec *usm = sec->data;
> +     char *lkey;
> +
> +     if (usm->digest == NULL) {
> +             errno = EINVAL;
> +             return -1;
> +     }
> +
> +     /*
> +      * We could transform a master key to a local key here if we already
> +      * have usm_setengineid called. Sine snmpc.c is the only caller at
> +      * the moment there's no need, since it always calls us first.
> +      */
> +     if (level == USM_KEY_PASSWORD) {
> +             if ((usm->privkey = usm_passwd2mkey(usm->digest, key)) == NULL)
> +                     return -1;
> +             level = USM_KEY_MASTER;
> +             keylen = EVP_MD_size(usm->digest);
> +     } else {
> +             if (keylen != (size_t)EVP_MD_size(usm->digest)) {
> +                     errno = EINVAL;
> +                     return -1;
> +             }
> +             if ((lkey = malloc(keylen)) == NULL)
> +                     return -1;
> +             memcpy(lkey, key, keylen);
> +             usm->privkey = lkey;
> +     }
> +     usm->cipher = cipher;
> +     usm->privlevel = level;
> +     return 0;
> +}
> +
> +int
> +usm_setengineid(struct snmp_sec *sec, char *engineid, size_t engineidlen)
> +{
> +     struct usm_sec *usm = sec->data;
> +     char *mkey;
> +
> +     if (usm->engineid != NULL)
> +             free(usm->engineid);
> +     if ((usm->engineid = malloc(engineidlen)) == NULL)
> +             return -1;
> +     memcpy(usm->engineid, engineid, engineidlen);
> +     usm->engineidlen = engineidlen;
> +     usm->engineidset = 1;
> +
> +     if (usm->authlevel == USM_KEY_MASTER) {
> +             mkey = usm->authkey;
> +             if ((usm->authkey = usm_mkey2lkey(usm, usm->digest,
> +                 mkey)) == NULL) {
> +                     usm->authkey = mkey;
> +                     return -1;
> +             }
> +             free(mkey);
> +             usm->authlevel = USM_KEY_LOCALIZED;
> +     }
> +     if (usm->privlevel == USM_KEY_MASTER) {
> +             mkey = usm->privkey;
> +             if ((usm->privkey = usm_mkey2lkey(usm, usm->digest,
> +                 mkey)) == NULL) {
> +                     usm->privkey = mkey;
> +                     return -1;
> +             }
> +             free(mkey);
> +             usm->privlevel = USM_KEY_LOCALIZED;
> +     }
> +
> +     return 0;
> +}
> +
> +int
> +usm_setbootstime(struct snmp_sec *sec, uint32_t boots, uint32_t time)
> +{
> +     struct usm_sec *usm = sec->data;
> +
> +     if (clock_gettime(CLOCK_MONOTONIC, &(usm->timecheck)) == -1)
> +             return -1;
> +
> +     usm->boots = boots;
> +     usm->bootsset = 1;
> +     usm->time = time;
> +     usm->timeset = 1;
> +     return 0;
> +}
> +
> +static char *
> +usm_passwd2mkey(const EVP_MD *md, const char *passwd)
> +{
> +     EVP_MD_CTX ctx;
> +     int i, count;
> +     const u_char *pw;
> +     u_char *c;
> +     u_char keybuf[EVP_MAX_MD_SIZE > 64 ? EVP_MAX_MD_SIZE : 64];
> +     unsigned dlen;
> +     char *key;
> +
> +     bzero(&ctx, sizeof(ctx));
> +     EVP_DigestInit_ex(&ctx, md, NULL);
> +     pw = (const u_char *)passwd;
> +     for (count = 0; count < 1048576; count += 64) {
> +             c = keybuf;
> +             for (i = 0; i < 64; i++) {
> +                     if (*pw == '\0')
> +                             pw = (const u_char *)passwd;
> +                     *c++ = *pw++;
> +             }
> +             EVP_DigestUpdate(&ctx, keybuf, 64);
> +     }
> +     EVP_DigestFinal_ex(&ctx, keybuf, &dlen);
> +     EVP_MD_CTX_cleanup(&ctx);
> +
> +     if ((key = malloc(dlen)) == NULL)
> +             return NULL;
> +     memcpy(key, keybuf, dlen);
> +     return key;
> +}
> +
> +static char *
> +usm_mkey2lkey(struct usm_sec *usm, const EVP_MD *md, const char *mkey)
> +{
> +     EVP_MD_CTX ctx;
> +     u_char buf[EVP_MAX_MD_SIZE];
> +     u_char *lkey;
> +     unsigned lklen;
> +
> +
> +     bzero(&ctx, sizeof(ctx));
> +     EVP_DigestInit_ex(&ctx, md, NULL);
> +
> +     EVP_DigestUpdate(&ctx, mkey, EVP_MD_size(md));
> +     EVP_DigestUpdate(&ctx, usm->engineid, usm->engineidlen);
> +     EVP_DigestUpdate(&ctx, mkey, EVP_MD_size(md));
> +
> +     EVP_DigestFinal_ex(&ctx, buf, &lklen);
> +     EVP_MD_CTX_cleanup(&ctx);
> +
> +     if ((lkey = malloc(lklen)) == NULL)
> +             return NULL;
> +     memcpy(lkey, buf, lklen);
> +     return lkey;
> +}
> +
> +static size_t
> +usm_digestlen(const EVP_MD *md)
> +{
> +     switch (EVP_MD_type(md)) {
> +     case NID_md5:
> +     case NID_sha1:
> +                return 12;
> +        case NID_sha224:
> +                return 16;
> +        case NID_sha256:
> +                return 24;
> +        case NID_sha384:
> +                return 32;
> +        case NID_sha512:
> +                return 48;
> +        default:
> +                return 0;
> +
> +     }
> +}
> diff --git a/usm.h b/usm.h
> new file mode 100644
> index 0000000..b1aea8b
> --- /dev/null
> +++ b/usm.h
> @@ -0,0 +1,34 @@
> +/*   $OpenBSD: snmp.h,v 1.1 2019/08/09 06:17:59 martijn Exp $        */
> +
> +/*
> + * Copyright (c) 2019 Martijn van Duren <mart...@openbsd.org>
> + *
> + * Permission to use, copy, modify, and distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +#include "snmp.h"
> +
> +enum usm_key_level {
> +     USM_KEY_UNSET = 0,
> +     USM_KEY_PASSWORD,
> +     USM_KEY_MASTER,
> +     USM_KEY_LOCALIZED
> +};
> +
> +struct snmp_sec *usm_init(const char *, size_t);
> +int usm_setauth(struct snmp_sec *, const EVP_MD *, const char *, size_t,
> +    enum usm_key_level);
> +int usm_setpriv(struct snmp_sec *, const EVP_CIPHER *, const char *, size_t,
> +    enum usm_key_level);
> +int usm_setengineid(struct snmp_sec *, char *, size_t);
> +int usm_setbootstime(struct snmp_sec *, uint32_t, uint32_t);
> 

Reply via email to