App-OpenHAP
view release on metacpan or search on metacpan
lib/Protocol/HAP/Pairing.pm view on Meta::CPAN
# $self->clear_pairing_state($session):
# Reset the pairing lock. The server calls this after successful
# pairing or on connection close.
sub clear_pairing_state ( $self, $session = undef )
{
# Clear the state only if this session owns the lock or
# the caller gives no session
if ( !defined $session
|| !defined $self->{pairing_session_id}
|| $self->{pairing_session_id} == $session )
{
$self->{pairing_in_progress} = 0;
$self->{pairing_session_id} = undef;
}
return;
}
# $self->reset_auth_attempts:
# Reset the failed authentication counter. The server calls this
# after the SRP proof verification succeeds. Administrative
# actions also call it.
sub reset_auth_attempts ($self)
{
$self->{failed_auth_attempts} = 0;
$self->{store}->set_auth_attempts(0);
return;
}
# $self->_auth_failure($message):
# Count and persist a failed attempt (§8), log it with the
# attempt count, and return the M4 error response: MaxTries at
# the limit, Authentication below it.
sub _auth_failure ( $self, $message )
{
$self->{failed_auth_attempts}++;
$self->{store}->set_auth_attempts( $self->{failed_auth_attempts} );
$self->{logger}->warning(
'%s (attempt %d/%d)',
$message, $self->{failed_auth_attempts},
MAX_AUTH_ATTEMPTS
);
if ( $self->{failed_auth_attempts} >= MAX_AUTH_ATTEMPTS ) {
return $self->_error_response( kTLVError_MaxTries, 4 );
}
return $self->_error_response( kTLVError_Authentication, 4 );
}
# $self->get_failed_attempts:
# Get the current failed attempt count (for testing).
sub get_failed_attempts ($self)
{
return $self->{failed_auth_attempts};
}
# $self->_decode_request($body, $label):
# The decode-and-check preamble of both pairing endpoints.
# Return the request TLV as a hash reference and the state, or
# the empty list for a malformed TLV or a missing State
# ([HAP-TLV8 §10]).
sub _decode_request ( $self, $body, $label )
{
my %request = Protocol::HAP::TLV::decode($body);
unless ( defined $request{ kTLVType_State() } ) {
$self->{logger}
->warning( '%s rejected: malformed TLV request', $label );
return;
}
return ( \%request, unpack( 'C', $request{ kTLVType_State() } ) );
}
sub handle_pair_setup ( $self, $body, $session )
{
my ( $request, $state ) = $self->_decode_request( $body, 'Pair-setup' );
return $self->_error_response( kTLVError_Unknown, 2 ) unless $request;
my $method = unpack( 'C', $request->{ kTLVType_Method() } // "\x00" );
$self->{logger}
->debug( 'Pair-setup M%d received (method=%d)', $state, $method );
# Validate the method (0x00 = PairSetup, 0x01 = PairSetupWithAuth)
if ( $method != 0 && $method != 1 ) {
return $self->_error_response( kTLVError_Unknown, 2 );
}
if ( $state == 1 ) {
return $self->_pair_setup_m1_m2( $session, $method );
}
elsif ( $state == 3 ) {
return $self->_pair_setup_m3_m4( $request, $session );
}
elsif ( $state == 5 ) {
return $self->_pair_setup_m5_m6( $request, $session );
}
return $self->_error_response( kTLVError_Unknown, 2 );
}
sub _pair_setup_m1_m2 ( $self, $session, $method = 0 )
{
# Check if the failed attempts exceed the maximum
# (HAP-Pairing.md §8)
if ( $self->{failed_auth_attempts} >= MAX_AUTH_ATTEMPTS ) {
$self->{logger}
->warning('Pair-setup rejected: max attempts exceeded');
return $self->_error_response( kTLVError_MaxTries, 2 );
}
# Check if the accessory is already paired
# (HAP-Pairing.md §2.4). PairSetupWithAuth (method=1)
# permits pairing even when the accessory is already
# paired.
if ( $method == 0 ) {
my $pairings = $self->{store}->load_pairings();
if ( keys %$pairings > 0 ) {
( run in 2.678 seconds using v1.01-cache-2.11-cpan-9789f410c06 )