--- /dev/null
+package File::KDBX::Key::ChallengeResponse;
+# ABSTRACT: A challenge-response key
+
+use warnings;
+use strict;
+
+use File::KDBX::Error;
+use namespace::clean;
+
+use parent 'File::KDBX::Key';
+
+our $VERSION = '999.999'; # VERSION
+
+sub init {
+ my $self = shift;
+ my $primitive = shift or throw 'Missing key primitive';
+
+ $self->{responder} = $primitive;
+
+ return $self->hide;
+}
+
+sub raw_key {
+ my $self = shift;
+ if (@_) {
+ my $challenge = shift // '';
+ # Don't challenge if we already have the response.
+ return $self->SUPER::raw_key if $challenge eq ($self->{challenge} // '');
+ $self->_set_raw_key($self->challenge($challenge, @_));
+ $self->{challenge} = $challenge;
+ }
+ $self->SUPER::raw_key;
+}
+
+=method challenge
+
+ $response = $key->challenge($challenge, @options);
+
+Issue a challenge and get a response, or throw if the responder failed.
+
+=cut
+
+sub challenge {
+ my $self = shift;
+
+ my $responder = $self->{responder} or throw 'Cannot issue challenge without a responder';
+ return $responder->(@_);
+}
+
+1;
+__END__
+
+=head1 SYNOPSIS
+
+ my $key = File::KDBX::Key::ChallengeResponse->(
+ responder => sub { my $challenge = shift; ...; return $response },
+ );
+
+=head1 DESCRIPTION
+
+=cut