Прочитать тело https в SWI-Prolog

Еще один вопрос от меня .. В общем, пытаюсь определить authenticate_alexa/1.

Это (я думаю) обеспечивает ту же функцию, что и этот java-код от Amazon: https://github.com/amzn/alexa-skills-kit-java/blob/master/src/com/amazon/SpeechletRequestSignatureVerifier.java

Инструкции о том, что для этого нужно сделать, находятся по адресу: https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/developing-an-alexa-skill-as-a-web-service

Проблема, с которой я столкнулся, заключается в том, что мне нужно прочитать полный запрос httpsbody и найти его хеш-значение, чтобы убедиться, что он работает с ключом из полученной цепочки (шаг 7 проверки подписи).

Поэтому мне нужно определить httpsbody/2, который будет возвращать тело в точности, а затем иметь правильный код для проверки хеш-значений.

Мой текущий код:

alexa(Request):-
    authenticate_alexa(Request),
    %http_read_json_dict(Request,DictIn),
    %handle_dict(DictIn,DictOut),
    my_json_answer(hello,DictOut),
    reply_json(DictOut).

authenticate_alexa(Request):-
    check_sigchainurl(Request,URL),
    portray_clause(user_error,URL),
    get_certs(URL,[ACert|CertsRest]),
    checkcertvalid_time(ACert),
    checkchain([ACert|CertsRest]),
    memberchk(key(Key),ACert),
    base64decodesig_encryptedSig(Request,ESig),
%   httpsbody(Request,Body), %how do we read the body of the request?
        httpsbodybytes(Request,Body),
        portray_clause(user_error,'Http Body from httpsbody(Request,Body) :'),
        portray_clause(user_error,Body),
    crypto_data_hash(Body,Hash,[algorithm(sha1),encoding(octet)]),
    portray_clause(user_error,'Serviced signed Hash:'),
    portray_clause(user_error,Hash),
        atom_string(Hash,HashString),
    string_concat("0x",HashString,HashString2),
    number_string(HashNumber,HashString2),
    portray_clause(user_error,'Hash as Dec'),
    portray_clause(user_error,HashNumber),

    signature_pow(ESig,0x010001,Key,ClpfdHash),
    portray_clause(user_error,'clpfdhash:'),
    portray_clause(user_error,ClpfdHash),
        %rsa_verify(Key,Hash,ESig,[type(sha1)]),
    portray_clause(user_error,done).

get_certs(URL,Certs):-
   setup_call_cleanup(
        http_open(URL,Stream,[]),
        ssl_peer_certificate_chain(Stream,Certs),
        close(Stream)
          ).

signature_pow(Sig, Exp, P, Pow) :-
        portray_clause(user_error,'ESig:'),
    portray_clause(user_error,Sig),
    (atom(Sig) -> portray_clause(user_error,'it is an atom');portray_clause(user_error,'it is not an atom')),
    (number(Sig) -> portray_clause(user_error,'it is a number');portray_clause(user_error,'it is not a number')),
    atom_string(Sig,Sigstring),
    string_concat("0x",Sigstring,SigString2),
    number_string(SigNumber,SigString2),
    portray_clause(user_error,'Exp:'),
    portray_clause(user_error,Exp),
    portray_clause(user_error,'P:'),
    portray_clause(user_error,P),
    P =public_key(rsa(P2,EXP2,_,_,_,_,_,_)),
    portray_clause(user_error,'P just number:'),
        portray_clause(user_error,P2),
    (string(P2) -> portray_clause(user_error,'String hex P number');portray_clause(user_error,notstring)),
    string_concat("0x",P2,NewString),
    portray_clause(user_error,NewString),
    number_string(P3,NewString),
    portray_clause(user_error,'P number as dec:'),
    portray_clause(user_error,P3),  
        Pow #= SigNumber^Exp mod P3,
    portray_clause(user_error,'Pow'),
    portray_clause(user_error,Pow),
    portray_clause(user_error,verified).

check_sigchainurl(Request,URL):-
    memberchk(signaturecertchainurl(URL),Request),
    parse_url(URL,P), %what about normalise url? I dont think it is needed
    memberchk(protocol(https),P),%should make case insenstive
    memberchk(host('s3.amazonaws.com'),P), %should make case insenstive
    memberchk(path(Path),P),
    string_concat('/echo.api/',_,Path),
    (memberchk(port(Port),P) -> Port =443 ; true).

checkcertvalid_time(Acert):-
    memberchk(notbefore(NotBefore),Acert),
    memberchk(notafter(NotAfter),Acert),
    get_time(NowA),
    Now is round(NowA),
    Now #>NotBefore,
    Now #<NotAfter.

checkchain(Chain):-
        length(Chain,L),
    L#>1.           %Insure chain has more than one cert
    %portray_clause(user_error,Chain),
    checkchain_h(Chain).

checkchain_h([_]). %Reached the root.
checkchain_h(Chain):-
        Chain =[C1,C2|Rest],
        memberchk(signature(Sig),C1),
    memberchk(to_be_signed(Signed),C1),
    memberchk(key(Key),C2),
    hex_bytes(Signed,Bytes),
    crypto_data_hash(Bytes,Hash,[algorithm(sha256),encoding(octet)]),
    rsa_verify(Key,Hash,Sig,[type(sha256)]),
    checkchain_h([C2|Rest]).

base64decodesig_encryptedSig(Request,Hex):-
    memberchk(signature(B64Sig),Request),
    portray_clause(user_error,'base64 encoded sig:'),
    portray_clause(user_error,B64Sig),
    base64(ESig,B64Sig),
    atom_codes(ESig,Bytes),
    hex_bytes(Hex,Bytes).
    %portray_clause(user_error,'Esig is base64 sig decoded using base64//2:'),
    %portray_clause(user_error,ESig).
        %portray_clause(user_error,'Can not print esig').

httpsbodybytes(Request,BodyBytes2):-
    memberchk(input(In),Request),
    portray_clause(user_error,"Bytes:"),
    mygetbody2(In,BodyBytes,0),
    list_butlast(BodyBytes,BodyBytes2).

mygetbody2(_Stream,[],-1).
mygetbody2(Stream,[Byte|Bytes],Check):-
    dif(Check,-1),
    get_byte(Stream,Byte),
    %portray_clause(user_error,Byte),
    mygetbody2(Stream,Bytes,Byte).

list_butlast([X|Xs], Ys) :-                 % use auxiliary predicate ...
   list_butlast_prev(Xs, Ys, X).            % ... which lags behind by one item

list_butlast_prev([], [], _).
list_butlast_prev([X1|Xs], [X0|Ys], X0) :-  
   list_butlast_prev(Xs, Ys, X1).

Который при получении запроса выводит:

'https://s3.amazonaws.com/echo.api/echo-api-cert-4.pem'.
'base64 encoded sig:'.
'Nu2v3C8hWNSF7G7N/B5WWqFnhuGMPJeMGi76FanbF8D4so+AiRSWVaehPXoaiLZpCiEb5Ysd+QFCBEt5/5a6MoLh4JTAhwGcnGUs+GbZo7rkk12t6meUTldnBNZ4/1boUNGurWCmPy1pkCWyHIt7udWj2zCdHx+4cFCKy8GVWfjOtZ3n52LWzGd+HO0RF0GQ25cSoFQGr2I5bT060Bodt3PrZob/nWnPxqayC+y53Itt/I0YUtM2QnPYByx/GJuyzMUuA0/v0PH15oGOE+wwYszr1C6mmEKMRdkCzFEO6z4kuw6UeXDaECS9ak04XCp+gsYkHQjJYpoB3s8T6qeNGQ=='.
"Bytes:".
'Http Body from httpsbody(Request,Body) :'.
[123, 34, 118, 101, 114, 115, 105, 111, 110, 34, 58, 34, 49, 46, 48, 34, 44, 34, 115, 101, 115, 115, 105, 111, 110, 34, 58, 123, 34, 110, 101, 119, 34, 58, 102, 97, 108, 115, 101, 44, 34, 115, 101, 115, 115, 105, 111, 110, 73, 100, 34, 58, 34, 83, 101, 115, 115, 105, 111, 110, 73, 100, 46, 99, 55, 99, 54, 49, 55, 51, 100, 45, 98, 98, 102, 52, 45, 52, 56, 57, 101, 45, 97, 48, 98, 97, 45, 52, 54, 55, 102, 101, 56, 48, 51, 100, 97, 101, 99, 34, 44, 34, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 34, 58, 123, 34, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 73, 100, 34, 58, 34, 97, 109, 122, 110, 49, 46, 97, 115, 107, 46, 115, 107, 105, 108, 108, 46, 97, 50, 55, 101, 98, 53, 48, 53, 45, 102, 99, 101, 102, 45, 52, 57, 98, 102, 45, 56, 57, 55, 53, 45, 51, 101, 49, 97, 54, 100, 55, 98, 55, 99, 55, 52, 34, 125, 44, 34, 97, 116, 116, 114, 105, 98, 117, 116, 101, 115, 34, 58, 123, 125, 44, 34, 117, 115, 101, 114, 34, 58, 123, 34, 117, 115, 101, 114, 73, 100, 34, 58, 34, 97, 109, 122, 110, 49, 46, 97, 115, 107, 46, 97, 99, 99, 111, 117, 110, 116, 46, 65, 71, 77, 77, 80, 53, 85, 81, 73, 81, 80, 89, 52, 69, 75, 90, 90, 72, 88, 71, 68, 52, 86, 72, 55, 89, 72, 80, 55, 88, 65, 51, 54, 84, 65, 79, 50, 82, 54, 76, 87, 79, 77, 89, 73, 88, 75, 78, 52, 68, 72, 88, 88, 51, 66, 73, 83, 53, 87, 83, 82, 78, 84, 53, 65, 55, 86, 79, 75, 86, 82, 76, 50, 77, 75, 65, 78, 77, 86, 51, 75, 50, 68, 77, 79, 66, 50, 87, 75, 85, 68, 84, 72, 73, 74, 86, 87, 73, 77, 83, 65, 51, 76, 81, 75, 82, 73, 73, 83, 72, 71, 55, 80, 71, 55, 77, 89, 70, 65, 53, 50, 72, 77, 73, 67, 81, 87, 53, 53, 76, 50, 73, 90, 65, 76, 70, 90, 80, 77, 82, 54, 67, 74, 83, 74, 54, 74, 55, 90, 82, 75, 54, 54, 77, 87, 82, 55, 66, 89, 88, 69, 76, 66, 73, 50, 75, 79, 69, 90, 81, 73, 72, 68, 88, 79, 72, 72, 90, 70, 71, 90, 50, 81, 83, 90, 82, 87, 52, 75, 55, 79, 55, 53, 82, 50, 89, 88, 74, 54, 85, 87, 65, 86, 70, 77, 53, 65, 34, 125, 125, 44, 34, 114, 101, 113, 117, 101, 115, 116, 34, 58, 123, 34, 116, 121, 112, 101, 34, 58, 34, 73, 110, 116, 101, 110, 116, 82, 101, 113, 117, 101, 115, 116, 34, 44, 34, 114, 101, 113, 117, 101, 115, 116, 73, 100, 34, 58, 34, 69, 100, 119, 82, 101, 113, 117, 101, 115, 116, 73, 100, 46, 101, 54, 98, 56, 102, 101, 101, 100, 45, 101, 99, 101, 51, 45, 52, 101, 97, 97, 45, 97, 102, 50, 100, 45, 99, 52, 55, 102, 54, 53, 55, 53, 57, 50, 49, 54, 34, 44, 34, 116, 105, 109, 101, 115, 116, 97, 109, 112, 34, 58, 34, 50, 48, 49, 55, 45, 48, 54, 45, 49, 57, 84, 48, 57, 58, 52, 54, 58, 51, 53, 90, 34, 44, 34, 108, 111, 99, 97, 108, 101, 34, 58, 34, 101, 110, 45, 71, 66, 34, 44, 34, 105, 110, 116, 101, 110, 116, 34, 58, 123, 34, 110, 97, 109, 101, 34, 58, 34, 103, 101, 116, 65, 78, 101, 119, 70, 97, 99, 116, 34, 125, 125, 125].
'Serviced signed Hash:'.
'64f1329a1f707809174ee50a6df9f65099bba371'.
'Hash as Dec'.
576277964734800966164187927075551858658582963057.
'ESig:'.
'36edafdc2f2158d485ec6ecdfc1e565aa16786e18c3c978c1a2efa15a9db17c0f8b28f8089149655a7a13d7a1a88b6690a211be58b1df90142044b79ff96ba3282e1e094c087019c9c652cf866d9a3bae4935dadea67944e576704d678ff56e850d1aead60a63f2d699025b21c8b7bb9d5a3db309d1f1fb870508acbc19559f8ceb59de7e762d6cc677e1ced11174190db9712a05406af62396d3d3ad01a1db773eb6686ff9d69cfc6a6b20becb9dc8b6dfc8d1852d3364273d8072c7f189bb2ccc52e034fefd0f1f5e6818e13ec3062ccebd42ea698428c45d902cc510eeb3e24bb0e947970da1024bd6a4d385c2a7e82c6241d08c9629a01decf13eaa78d19'.
'it is an atom'.
'it is not a number'.
'Exp:'.
65537.
'String hex P number'.
'P number as dec:'.
23367091749731801702903258951332579149824717752403036738901918995576378221876991769215986117060832205938888292020670601919717320529579897428555756633750717630113812467159229456710215094841206836055184753491046351652384568208399197333334554354625485949013821994244854426626217946383766717620084958916568639573896625634280251135248126961353512501885364402868885624724204807768627558428281558589460261611023934365963620820837651899784648073096603839587883308136461448176498481934437054583664797923342609641294880521414948881981392762983173267396409953920339084175033120756130451537931813023420728016059287928243275252817.
'Pow'.
12996685404671854237337597057543403676067050401691031855263389356566563793267812208256044577937494803172529297811694697178075070962940655950099948830185429064594023055861430145065585159411714852185690089480769442404348006975883315459440182824265725834630838968686976962484449232569444592352993640898180649431183234665232203068528805991370779087967630073792656438871568328046165107095895856647485355472888016164031315511155737623289424119686691313121050647697592969024984185347837785283636035540443593041046047481149874499335289745021686457982828945403796990195913021023926745047308779421759663632404133499645864797253.
verified.
'clpfdhash:'.
12996685404671854237337597057543403676067050401691031855263389356566563793267812208256044577937494803172529297811694697178075070962940655950099948830185429064594023055861430145065585159411714852185690089480769442404348006975883315459440182824265725834630838968686976962484449232569444592352993640898180649431183234665232203068528805991370779087967630073792656438871568328046165107095895856647485355472888016164031315511155737623289424119686691313121050647697592969024984185347837785283636035540443593041046047481149874499335289745021686457982828945403796990195913021023926745047308779421759663632404133499645864797253.
done.
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Два значения хеш-функции не совпадают. Так что в настоящее время он не работает.


person user27815    schedule 17.06.2017    source источник


Ответы (1)


Предварительный ответ:

Я думаю, что с read_string/3 вы на правильном пути, но вам нужно разбить это на более управляемый случай, чтобы у других была возможность помочь. Например, добавьте к своему вопросу:

  • полное тело HTTPS, которое вы получили
  • хеш, который вы для него вычислили
  • хэш, который служба подписала для него.

Я объяснил, как получить хэш, в который вы вошли:

https://stackoverflow.com/a/44524628/1613573

См. Расчет на основе CLP (FD).

Обратите внимание, что в момент установки соединения (через TLS) это чисто проблема уровня HTTP (не HTTPS).

Полезные предикаты:

Я вижу, вы также боретесь с рассуждениями о фактической подписи в кодировке Base64. Чтобы понять это, вы можете применить следующие шаги:

  • используйте base64/2, чтобы получить простой атом, т. е. base64(Plain, Encoded)
  • используйте atom_codes/2, чтобы получить список кодов, представляющих атом, т. е. atom_codes(Plain, Bytes)
  • используйте hex_bytes/2 с library(crypto) по получить шестнадцатеричную кодировку подписи, т. е. hex_bytes(Hex, Bytes).

Таким образом, вы получаете удобное шестнадцатеричное представление подписи. Вы можете использовать это для дальнейших вычислений, используя ограничения CLP (FD) или rsa_verify/4.

Я пробовал эти вычисления на вашем примере и получил для Pow #= Sig^Exp mod M целое число, которое заканчивается на:

b2feb64cd1ae11365f8917b71bb751142b63f159e12c02a1720b78958c3e7f7e

в шестнадцатеричной системе счисления. Это не рассчитанный вами хеш. Следовательно, может возникнуть проблема с кодировкой, которую вы используете для вычисления собственного хэша данных для проверки подписи. См. Значения для crypto_data_hash/3. В частности, я рекомендую crypto_data_hash(Bytes, Hash, [encoding(bytes)]), где вы должны убедиться, что Bytes является фактическим списком байтов, который представляет передаваемые данные.

person mat    schedule 17.06.2017
comment
Я не уверен, что следую расчету clp (fd). Я понимаю: вместо использования rsa_verify/4 inauthenticate_alexa/1 я должен использовать signature_pow(Sig,Exp,P,Pow) с Sig = ESig, а P - это ключ, какое значение Exp? 0x010001? тогда Pow - это рассчитанный хеш? Но Esig - это простой текст подписи base64 ... и это набор странных символов, которые плохо выводятся на экран ... не число, насколько я могу судить. Дополню вопрос некоторыми выводами. - person user27815; 17.06.2017
comment
Вывод слишком неструктурирован, чтобы сказать, что именно происходит. Пожалуйста, используйте portray_clause/1 или "~q" модификатор формата, чтобы создать допустимые термины Пролога, которые могут быть обработаны с помощью Пролога. В остальном, похоже, что подписанный хеш в точности идентичен хешу, который вы вычислили самостоятельно. Это означает, что подпись действительна. - person mat; 17.06.2017
comment
Я попытался сделать его более понятным, обновив код и показав результат с предложением изобразить ... насколько мне известно, я не вычислял хэш! - person user27815; 17.06.2017
comment
Спасибо за указатели ... Теперь это имеет больше смысла, кроме несоответствия хеш-значений! Я обновил код вопроса, чтобы читать байты, а затем использовал crypto_data_hash с кодировкой как octet, а не bytes? Если вы видите что-то явно неправильное, было бы хорошо знать! Спасибо за вашу помощь. - person user27815; 19.06.2017
comment
Да, конечно, я имел в виду encoding(octet). Вы можете упростить сопровождение кода, используя модификатор формата ~q. Например: format("Pow = ~q", [Pow]). Помимо этого, я рекомендую вам использовать шестнадцатеричное кодирование вычисленных чисел, используя модификатор формата ~16r. Если подпись действительна, то конец мощности в шестнадцатеричном представлении должен совпадать с хешем, вычисленным для тела HTTP. Еще один метод, который вы можете попробовать, - это загрузить код проверки, предоставляемый Amazon, и сравнить вычисленные хэши и полученные байты с таковыми из вашей программы. - person mat; 19.06.2017