Crypt-OPAQUE
view release on metacpan or search on metacpan
lib/Crypt/OPAQUE.pm view on Meta::CPAN
#ABSTRACT: OPAQUE protocol
package Crypt::OPAQUE;
use strict;
use warnings;
#use bignum;
require Exporter;
use Crypt::KeyDerivation ':all';
use Carp;
use Crypt::OpenSSL::Hash2Curve;
use Crypt::OpenSSL::Base::Func;
use Crypt::OpenSSL::EC;
use Crypt::OpenSSL::Bignum;
use Crypt::OpenSSL::ECDSA;
use Crypt::OPRF;
#use Smart::Comments;
our $VERSION = 0.012;
our @ISA = qw(Exporter);
our @EXPORT = qw/
create_cleartext_credentials
store
recover
create_registration_request
create_registration_response
finalize_registration_request
derive_random_pwd
create_credential_request
create_credential_response
recover_credentials
/;
our @EXPORT_OK = @EXPORT;
sub recover_credentials {
my (
$cred_request, $cred_response, $pwd, $c_id, $s_id, $Nseed, $group_name, $info, $DST, $hash_name, $expand_message_func,
$mac_func, $pwd_harden_func, $unpack_func
) = @_;
my $evaluate_element = hex2point( $cred_request->{ec_params}{name}, unpack( "H*", $cred_response->{Z} ) );
my $randomized_pwd =
derive_random_pwd( $cred_request->{ec_params}, $pwd, $cred_request->{blind}, $evaluate_element, $hash_name, $pwd_harden_func );
### randomized_pwd: unpack("H*", $randomized_pwd)
my $hash_func = EVP_get_digestbyname( $hash_name );
my $Nh = EVP_MD_get_size( $hash_func );
my $masking_key = Crypt::KeyDerivation::hkdf_expand( $randomized_pwd, $hash_name, $Nh, "MaskingKey" );
### masking_key: unpack("H*", $masking_key)
my $L = length( $cred_response->{masked_response} );
### $L
my $masking_nonce = $cred_response->{masking_nonce};
my $credential_response_pad = Crypt::KeyDerivation::hkdf_expand( $masking_key, $hash_name, $L, $masking_nonce . "CredentialResponsePad" );
my $plain_response = $credential_response_pad ^ $cred_response->{masked_response};
my $unpack_r = $unpack_func->( $plain_response );
my ( $s_pub, $envelope_nonce, $envelope_auth_tag ) = @$unpack_r;
my $envelope = { nonce => $envelope_nonce, auth_tag => $envelope_auth_tag };
my $recover_r = recover(
$randomized_pwd, $s_pub, $envelope, $s_id, $c_id, $Nseed, $group_name, $info, $DST, $hash_name, $expand_message_func,
$mac_func
);
$recover_r->{s_pub} = $s_pub;
### recover s_pub: unpack("H*", $s_pub)
### recover c_priv: $recover_r->{c_priv}->to_hex
### recover export_key: unpack("H*", $recover_r->{export_key})
return $recover_r;
} ## end sub recover_credentials
sub create_credential_response {
my (
$request, $s_pub, $oprf_seed, $credential_identifier, $DSI, $envelope, $masking_key, $Nn, $Nseed, $group_name, $info, $DST,
$hash_name, $expand_message_func, $point_compress_t, $pack_func
) = @_;
### blindElement: unpack("H*", $request->{data})
### s_pub: unpack("H*", $s_pub)
### oprf_seed: unpack("H*", $oprf_seed)
### $credential_identifier
### $DSI
### nonce: unpack("H*", $envelope->{nonce})
### auth_tag: unpack("H*", $envelope->{auth_tag})
### masking_key: unpack("H*", $masking_key)
### $Nseed
### $info
### $DST
my $res_r = create_registration_response(
$request, $s_pub, $oprf_seed, $credential_identifier, $DSI, $Nseed, $group_name, $info, $DST,
$hash_name, $expand_message_func, $point_compress_t
);
my $masking_nonce_bn = ( ref( $Nn ) eq 'Crypt::OpenSSL::Bignum' ) ? $Nn : random_bn( $Nn );
my $masking_nonce = $masking_nonce_bn->to_bin;
### masking_nonce: unpack("H*", $masking_nonce)
#my $Npk = length($s_pub);
#my $Ne = length($masking_key) + length($masking_nonce);
#my $L = $Npk + $Ne;
my $pack_msg = $pack_func->( [ $s_pub, $envelope->{nonce}, $envelope->{auth_tag} ] );
my $L = length( $pack_msg );
my $credential_response_pad = Crypt::KeyDerivation::hkdf_expand( $masking_key, $hash_name, $L, $masking_nonce . "CredentialResponsePad" );
### credential_response_pad: unpack("H*", $credential_response_pad)
my $masked_response = $credential_response_pad ^ $pack_msg;
### masked_response: unpack("H*", $masked_response)
### Z: unpack("H*", $res_r->{response}{data})
### $L
### pack_msg: unpack("H*", $pack_msg)
my $cred_res = {
Z => $res_r->{response}{data}, # evaluate_element_binary
masking_nonce => $masking_nonce,
masked_response => $masked_response,
};
return $cred_res;
} ## end sub create_credential_response
sub create_credential_request {
return create_registration_request( @_ );
}
sub finalize_registration_request {
my (
$request, $response, $pwd, $c_id, $s_id, $Nn, $Nseed, $group_name, $info, $DST, $hash_name, $expand_message_func, $mac_func,
lib/Crypt/OPAQUE.pm view on Meta::CPAN
### stretched_oprf_output: unpack("H*", $stretched_oprf_output)
my $randomized_pwd = Crypt::KeyDerivation::hkdf_extract( $oprf_output . $stretched_oprf_output, '', $hash_name );
### randomized_pwd: unpack("H*", $randomized_pwd)
return $randomized_pwd;
}
sub create_registration_response {
my (
$request, $s_pub, $oprf_seed, $credential_identifier, $DSI, $Nseed, $group_name, $info, $DST, $hash_name,
$expand_message_func, $point_compress_t
) = @_;
### $request
my $ikm = Crypt::KeyDerivation::hkdf_expand( $oprf_seed, $hash_name, $Nseed, $credential_identifier . $DSI );
### ikm: unpack("H*", $ikm)
my $kU_ec_key_r = derive_key_pair( $group_name, $ikm, $info, $DST, $hash_name, $expand_message_func );
my $kU = $kU_ec_key_r->{priv_bn};
### kU: $kU->to_hex
### $group_name
my $blindedElement_hex = unpack( "H*", $request->{data} );
### $blindedElement_hex
my $ec_params = get_ec_params( $group_name );
my $blind_element = hex2point( $ec_params->{name}, $blindedElement_hex );
### blinded_element: Crypt::OpenSSL::EC::EC_POINT::point2hex($ec_params->{group}, $blind_element, 2, $ec_params->{ctx});
my $evaluate_element = evaluate( $ec_params->{group}, $blind_element, $kU, $ec_params->{ctx} );
### evaluate_element: Crypt::OpenSSL::EC::EC_POINT::point2hex($ec_params->{group}, $evaluate_element, 2, $ec_params->{ctx});
#evaluated_message = self.config.oprf_suite.group.serialize(evaluated_element)
#my $evaluate_element_hex = sn_point2hex( $group_name, $evaluate_element, $point_compress_t );
my $evaluate_element_hex = Crypt::OpenSSL::EC::EC_POINT::point2hex($ec_params->{group}, $evaluate_element, 2, $ec_params->{ctx});
my $evaluate_element_binary = pack( "H*", $evaluate_element_hex );
### $evaluate_element_hex
my $response = { data => $evaluate_element_binary, s_pub => $s_pub };
### $response
return { response => $response, kU => $kU, ec_params => $ec_params };
} ## end sub create_registration_response
sub create_registration_request {
my ( $pwd, $blind, $DSI, $group_name, $type, $hash_name, $expand_message_func, $clear_cofactor_flag ) = @_;
my $blindedElement;
( $blind, $blindedElement ) =
blind( $pwd, $blind, $DSI, $group_name, $type, $hash_name, $expand_message_func, $clear_cofactor_flag );
my $ec_params = get_ec_params( $group_name );
#my $blindedElement_hex = sn_point2hex( $group_name, $blindedElement, 2 );
my $blindedElement_hex = Crypt::OpenSSL::EC::EC_POINT::point2hex($ec_params->{group}, $blindedElement, 2, $ec_params->{ctx});
my $request = { data => pack( "H*", $blindedElement_hex ) };
return { request => $request, blind => $blind, ec_params => $ec_params };
}
sub create_cleartext_credentials {
my ( $s_pub, $c_pub, $s_id, $c_id ) = @_;
$s_id //= $s_pub;
$c_id //= $c_pub;
my $cleartext_credentials = join(
"", $s_pub,
map { i2osp( length( $_ ), 2 ) . $_ } ( $s_id, $c_id ) );
return $cleartext_credentials;
}
sub store {
my ( $randomized_pwd, $s_pub, $s_id, $c_id, $Nn, $Nseed, $group_name, $info, $DST, $hash_name, $expand_message_func, $mac_func ) =
@_;
my $envelope_nonce_bn = ( ref( $Nn ) eq 'Crypt::OpenSSL::Bignum' ) ? $Nn : random_bn( $Nn );
my $envelope_nonce = $envelope_nonce_bn->to_bin;
my $hash_func = EVP_get_digestbyname( $hash_name );
my $Nh = EVP_MD_get_size( $hash_func );
my $masking_key = Crypt::KeyDerivation::hkdf_expand( $randomized_pwd, $hash_name, $Nh, "MaskingKey" );
my $auth_key = Crypt::KeyDerivation::hkdf_expand( $randomized_pwd, $hash_name, $Nh, $envelope_nonce . "AuthKey" );
my $export_key = Crypt::KeyDerivation::hkdf_expand( $randomized_pwd, $hash_name, $Nh, $envelope_nonce . "ExportKey" );
### auth_key: unpack("H*", $auth_key)
my $seed = Crypt::KeyDerivation::hkdf_expand( $randomized_pwd, $hash_name, $Nseed, $envelope_nonce . "PrivateKey" );
### seed: unpack("H*", $seed)
my $c_ec_key_r = derive_key_pair( $group_name, $seed, $info, $DST, $hash_name, $expand_message_func );
my $c_priv = $c_ec_key_r->{priv_bn};
my $c_pub = $c_ec_key_r->{pub_bin};
### c_priv: $c_priv->to_hex
my $cleartext_credentials = create_cleartext_credentials( $s_pub, $c_pub, $s_id, $c_id );
### cleartext_credentails: unpack("H*", $cleartext_credentials)
my $auth_tag = $mac_func->( $envelope_nonce . $cleartext_credentials, $auth_key );
my $envelope = { auth_tag => $auth_tag, nonce => $envelope_nonce };
return {
envelope => $envelope, c_pub => $c_pub, masking_key => $masking_key,
export_key => $export_key,
c_priv => $c_priv, auth_key => $auth_key,
cleartext_credentails => $cleartext_credentials,
};
} ## end sub store
sub recover {
my (
$randomized_pwd, $s_pub, $envelope, $s_id, $c_id, $Nseed, $group_name, $info, $DST, $hash_name, $expand_message_func,
$mac_func
) = @_;
my $hash_func = EVP_get_digestbyname( $hash_name );
my $Nh = EVP_MD_get_size( $hash_func );
my $auth_key = Crypt::KeyDerivation::hkdf_expand( $randomized_pwd, $hash_name, $Nh, $envelope->{nonce} . "AuthKey" );
### auth_key: unpack("H*", $auth_key)
my $export_key = Crypt::KeyDerivation::hkdf_expand( $randomized_pwd, $hash_name, $Nh, $envelope->{nonce} . "ExportKey" );
#my $masking_key = hkdf_expand($randomized_pwd, $hash_name, $Nh, "MaskingKey");
### export_key: unpack("H*", $export_key)
my $seed = Crypt::KeyDerivation::hkdf_expand( $randomized_pwd, $hash_name, $Nseed, $envelope->{nonce} . "PrivateKey" );
### seed: unpack("H*", $seed)
my $c_ec_key_r = derive_key_pair( $group_name, $seed, $info, $DST, $hash_name, $expand_message_func );
my $c_priv = $c_ec_key_r->{priv_bn};
my $c_pub = $c_ec_key_r->{pub_bin};
### c_priv: $c_priv->to_hex
my $cleartext_credentials = create_cleartext_credentials( $s_pub, $c_pub, $s_id, $c_id );
my $expected_tag = $mac_func->( $envelope->{nonce} . $cleartext_credentials, $auth_key );
if ( $envelope->{auth_tag} ne $expected_tag ) {
croak "not match envelope.auth_tag";
}
return {
export_key => $export_key,
c_priv => $c_priv,
c_ec_key_r => $c_ec_key_r,
};
} ## end sub recover
1;
( run in 0.676 second using v1.01-cache-2.11-cpan-39bf76dae61 )