summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--cacerts.pem333
-rw-r--r--rebiss.py522
-rwxr-xr-xrun.sh2
-rw-r--r--ssl.key28
-rw-r--r--ssl.pem40
6 files changed, 926 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..bee8a64
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+__pycache__
diff --git a/cacerts.pem b/cacerts.pem
new file mode 100644
index 0000000..16de510
--- /dev/null
+++ b/cacerts.pem
@@ -0,0 +1,333 @@
+-----BEGIN CERTIFICATE-----
+MIIHYTCCBUmgAwIBAgIIVWzBnzXxlcowDQYJKoZIhvcNAQELBQAwcDELMAkGA1UE
+BhMCQkcxGDAWBgNVBGETD05UUkJHLTIwMTIzMDQyNjESMBAGA1UEChMJQk9SSUNB
+IEFEMRAwDgYDVQQLEwdCLVRydXN0MSEwHwYDVQQDExhCLVRydXN0IFJvb3QgQWR2
+YW5jZWQgQ0EwHhcNMTgwNjAxMTMyOTM0WhcNMzMwNTMxMTMyOTM0WjB3MQswCQYD
+VQQGEwJCRzEYMBYGA1UEYRMPTlRSQkctMjAxMjMwNDI2MRIwEAYDVQQKEwlCT1JJ
+Q0EgQUQxEDAOBgNVBAsTB0ItVHJ1c3QxKDAmBgNVBAMTH0ItVHJ1c3QgT3BlcmF0
+aW9uYWwgQWR2YW5jZWQgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
+AQC4X1C5+lc6L831DczO5X8lbZhJZReYpt4CsQvvomIcs+cR+gBnO8ZBxPAhe5TG
+DKbEAbULe8S6UtGdhq3QG80wBuAgSF6fV4qeq/s4af7idG3HmIIClVwtphuu0EoY
+sGSnXz6td+h/vEUE06tZ7KICM/oXju1n6BhIZlH2TuhfwCwgU8VILORWp74gA87S
+LSIXmTqM/rsa+Cqk1cB6k4dCCdkyvKAaQA5IMV3yARDLk2tYdy6G7oOq4GTyHcL2
+2/VnHrA481OwDJ68HnwcfYXzVoVA8tSnZ2lM7cW8xdVCd4s36A7vKuiPm4UtxXeV
+/sC9MQgI+r6yinQDsGSLAEpPpd+09JrgvqY9R8JsBHQmNPsSjcI50MXsnhaWobz1
+ga/ys4N/Wn+4n7HrWkFjP6x653DhtiFGWBMXcB8t3gX0EKqxLblxobEn12pEkoQD
+BpcoXbpZ25wXvCEuRAhHO/+YuXDWGaj2oZ/6hTADTgd9UEs9UXlGBBRg8oZbcjDg
+Ap/scpVtnGyYTgLBQn7HlXwoDtjyzrtWrr4DpSVRtJ0YVYVWm15a8R338eegZwUt
+RZ2+30iBR8BDZlZ8XJE6xqgdyzMs9W4A+UJjgysI9CDxNFXfoDdYlK/PHCsB/B4R
+hkIdelRQ6mlV1r0AlBPrYxRm5bIrYJP7kLj77jbvmyzcoQIDAIi3o4IB9jCCAfIw
+HQYDVR0OBBYEFAfcqjB2mLeFS20DGMjjzad7NoLvMB8GA1UdIwQYMBaAFIjbQu2J
+BTIMcicMRhvhxgle7MkhMCEGA1UdEgQaMBiGFmh0dHA6Ly93d3cuYi10cnVzdC5v
+cmcwEgYDVR0TAQH/BAgwBgEB/wIBADCBpwYDVR0gBIGfMIGcMEAGCisGAQQB+3YB
+BwEwMjAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5iLXRydXN0Lm9yZy9kb2N1bWVu
+dHMvY3BzMA0GCysGAQQB+3YBBwEBMA0GCysGAQQB+3YBBwECMA0GCysGAQQB+3YB
+BwEDMA0GCysGAQQB+3YBBwEEMA0GCysGAQQB+3YBBwEFMA0GCysGAQQB+3YBBwEG
+MA4GA1UdDwEB/wQEAwIBBjBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsLmIt
+dHJ1c3Qub3JnL3JlcG9zaXRvcnkvQi1UcnVzdFJvb3RBQ0EuY3JsMHgGCCsGAQUF
+BwEBBGwwajAjBggrBgEFBQcwAYYXaHR0cDovL29jc3AuYi10cnVzdC5vcmcwQwYI
+KwYBBQUHMAKGN2h0dHA6Ly9jYS5iLXRydXN0Lm9yZy9yZXBvc2l0b3J5L0ItVHJ1
+c3RSb290QUNBT0NTUC5jZXIwDQYJKoZIhvcNAQELBQADggIBALo0i4vghSPkaLAz
+6auMu1Y6ITa1ur3UDgZ1mPvItTlFynQPZTGXGDwBFhX5Fx8raa7lWti7dhaaz01Q
+iTJKVpPvpSzNp7yde5u+l4/ciD+x0THzyBo2Xr2pBDFlqFrCOGpVsU6T8BGgDxaj
+Vu2H0wF7GS4WJ7gNnz60n06gwYh+T0PhOhqInmrIfYmuw90Pj075aTASRuCvJSrS
+AV7l9VlkeE8bhTNAsF9A9n3tOl2gEYAMHTDA78i5jG/EsuuKRupGta3UcbA56Sr8
+EMLIM6GvRcBdszpugDwgHVe2Uu1Nem+hiZlxR7KbrkVWVgGzGxWgwcDu+Zd1d0o1
+8VePC4IiG8hTZIZoXFheKvskZVe9AF/9H1RS6saNsIVDGs27oFSZ6Jf9tYXVcNyU
+w0m6ewWVB5Rjbqx6u7DVbii9z2oD/mRwuE6kJxhFk528UMuYhQg6qHQWh6PIbUWO
+2mqzmIqWdB+VnVxEfbTmR260nqoZG820quMTiUCC0TAyrKT0/luq+sPwsoZAev6l
+pWwOvpXiI8NaFEVy9UIbJGSVambpSvhuN3CWToGcucqsFeHFdg4S2qjXcV0ryNKF
+QIWfARnE/rBqTbbnKhujjxfp+WO55RAG3MDduOeIu31fbg2BJgvdLkL4VKse8HO4
+hUr5/mi/HgyvqTAzoCf/KTxV7lYm
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIHMzCCBRugAwIBAgIIaQ5Pt5rtE5QwDQYJKoZIhvcNAQELBQAwcTELMAkGA1UE
+BhMCQkcxGDAWBgNVBGETD05UUkJHLTIwMTIzMDQyNjESMBAGA1UEChMJQk9SSUNB
+IEFEMRAwDgYDVQQLEwdCLVRydXN0MSIwIAYDVQQDExlCLVRydXN0IFJvb3QgUXVh
+bGlmaWVkIENBMB4XDTE4MDYwMTEzNDQ1MFoXDTMzMDUzMTEzNDQ1MFoweDELMAkG
+A1UEBhMCQkcxGDAWBgNVBGETD05UUkJHLTIwMTIzMDQyNjESMBAGA1UEChMJQk9S
+SUNBIEFEMRAwDgYDVQQLEwdCLVRydXN0MSkwJwYDVQQDEyBCLVRydXN0IE9wZXJh
+dGlvbmFsIFF1YWxpZmllZCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
+ggIBALotZe4YXbj/eg9cTbUEIm96CN/g6pr0mkSMQzcGoFJ/ob7ag7EFfXMh3p0o
+aYx0sq3Sd3cRdSaFlkJhYHtuz6UqA6g/2iIjTehPS4a+NLIgkU7jfVjZ0rAZM/0J
+Yeh2BPwgw0wiSwpviHTvMIpu7DHFNeWruocEKvgq4YqZriJRtPNapOFjRQWSTeZc
+0btyn4prkLE54SreFQ/+z33spbgrnhI9m90GG6kMr/vx0E+iUYkVklCdBrTBcNll
+gM+bF2V6iMcbLgnnjU1biHvPpZTd6cedJM3rHC9LLLE8Dxq95MmMK9vLoOCU0Sog
+AjEx4XeZnS3h3xwA4qF6FCLe7D8FfNA0gWJO49h2GjDhnAzTGhyep6tH1p1Snizo
+Pq+Z1rFcHOYrOXfJf5EplaZ0ls32HlFSGCCqHFOcx+NFcZelubJUsA0f7tYT2Pud
+MmT0QrrFB/BODl4625UZlsiEWG0YLRkujBQgTYKjdSS9hiX9ZVcf8t/zeEibe1FC
+nEjmrm8G+79bqAPa8dT3zy7FgWxN+YkYbqMaIdXmtCYLemmDHeYij/UbjrcSJ0Um
+mR41PunO9bX64Jh2EsZn6TKUqWqJu/vGoDo93rvkIFcRMrD5Fe36G0WKcE7ytP57
+ekYYAotaVCpp4ecCyhPwV+BRBRqUPbw1asxvikrph4PlWn9JAgMAl6mjggHGMIIB
+wjAdBgNVHQ4EFgQUJ88IQwTwxYM3Z4EXTfwF5ttli7AwHwYDVR0jBBgwFoAU8oTu
+LjX+8PrYUFCwnEiJ6lov2aswIQYDVR0SBBowGIYWaHR0cDovL3d3dy5iLXRydXN0
+Lm9yZzASBgNVHRMBAf8ECDAGAQH/AgEAMHgGA1UdIARxMG8wQAYKKwYBBAH7dgEG
+ATAyMDAGCCsGAQUFBwIBFiRodHRwOi8vd3d3LmItdHJ1c3Qub3JnL2RvY3VtZW50
+cy9jcHMwDQYLKwYBBAH7dgEGAQEwDQYLKwYBBAH7dgEGAQIwDQYLKwYBBAH7dgEG
+AQMwDgYDVR0PAQH/BAQDAgEGMEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwu
+Yi10cnVzdC5vcmcvcmVwb3NpdG9yeS9CLVRydXN0Um9vdFFDQS5jcmwweAYIKwYB
+BQUHAQEEbDBqMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5iLXRydXN0Lm9yZzBD
+BggrBgEFBQcwAoY3aHR0cDovL2NhLmItdHJ1c3Qub3JnL3JlcG9zaXRvcnkvQi1U
+cnVzdFJvb3RRQ0FPQ1NQLmNlcjANBgkqhkiG9w0BAQsFAAOCAgEAiPODGc6gWdHj
+ROgQ2cy0KtZROQDl8ax0fTNJGmCvj714uBzuTvBECtLX8xZRmIvpd6PZq6Nexl4p
+XhS04SLdz7MhakN970d12/cGxkdYotTEpx0WuXqPC3J0A5VWY8LLsJ8JhVoO4xp4
+HaNE4abnCj+uXURFLJsoxbEKy1iqHMy7lopej9sCuOjp6jNQa+J4b4mEx8RtFn4V
+TnaC+19z3VQaPCaWfpJYnO1DsHSunZTq95UUVnjvgDM3WxuBRFkc551Q5dh4A+gr
+Gw6yPW5ijOC1eTxpG6DMgJL41G7ftGX1AsXhOuxoWRf9r2sCdQJUVe/CtZ37Vjm1
+QUS+TmTAjLXInh/sVNFC3xs7wt2umPQYevTeS4KRrxXgcv92ClW8qZGXoqnsrNvx
+9ATuHSMwysTBqrdEAAxqUrPrt2nb58yp+2Aa44CHhzXCfpFN9J84nwJhIOEVhLBa
+VelsP5IpiCfUTDtluNFNZ+0lWws7ugwt0Y4UwNVkEbm+TxYe3taAoOcf3eZP2+Bf
+VNM/MANpcY7h5uNdOxhRhB7l6MdvMJIMEq3RzEH+IH4tEBb56+H6Dkp93k1Hif6d
+Iwa9vleIueDpL3R3aG7yv2lq2xFVUlB6r+zEFAXmGA6X26R44TwWO1tLVCYR76D7
+81dYNmLCvS/sJ4idGjW2TQDAOOB6mZ0=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIG7TCCBNWgAwIBAgIBATANBgkqhkiG9w0BAQsFADBwMQswCQYDVQQGEwJCRzEY
+MBYGA1UEYRMPTlRSQkctMjAxMjMwNDI2MRIwEAYDVQQKEwlCT1JJQ0EgQUQxEDAO
+BgNVBAsTB0ItVHJ1c3QxITAfBgNVBAMTGEItVHJ1c3QgUm9vdCBBZHZhbmNlZCBD
+QTAeFw0xNzA0MjQxNTU1NDBaFw0zNzA0MjQxNTU1NDBaMHAxCzAJBgNVBAYTAkJH
+MRgwFgYDVQRhEw9OVFJCRy0yMDEyMzA0MjYxEjAQBgNVBAoTCUJPUklDQSBBRDEQ
+MA4GA1UECxMHQi1UcnVzdDEhMB8GA1UEAxMYQi1UcnVzdCBSb290IEFkdmFuY2Vk
+IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvHDWX60/6UWsnNB9
+e0pTVK79v8xmGJwzzziPGJQcJn8vYL47ahbhL54m05nswif36k6WglCPR2+pFryg
+HiTeUyDOpv96qTKOr3gpu2IrVxnL1078fCPQSta1xbWWGVQNJMx1td2TOfOv3QVU
+azix/1kyfFxgTYIKpeYxm55/04eKiSy7ls6a38SXrrEEQTKCT2YmpqF6oTb+IXFw
+K/b66wVDtEAbM93lfmz/EYDongHWK3ZJ4Vu+VhlDuhXhEee45FCaEmGUmQY4CYey
+OKzR0FZPvv58QrcopfNRmJEtJzc5KNXwX96N4VDI1NO1+y1dYW206806HcoFLzTG
+KXrxjsLROPM2LhGGqlOIEnvEHcSzmuqm6WiEZWkVjpsxLg3aYrcIy8R+KFAe+r4w
+doveCJB9J3LqxsrppLEm1Vi7BHKpctzRypfgKwtREmG635mxFcDWGvdlpNxOS6wI
+Rxq/uWYr5ENFZqXeccPk5IO27CPN32LXpqdTG1NMeJNCSuWeY/aisZx3ZkDpEhVv
+UuYkVEP9SPPC0/FlUa01BZQV+OHmbu86AFeqfRBU+Gz6xJ7/4kiiXHTHlQQD2AOq
+IaI4x3sWOzr471lQGct6qf5T9IW2rYoK61HvgsyTRAMgwMG8SdPjiovh7pW3w2Im
+bWA/Cs2axUYZTTuxm4PmjBYEPe0CAwC8eaOCAZAwggGMMB0GA1UdDgQWBBSI20Lt
+iQUyDHInDEYb4cYJXuzJITAfBgNVHSMEGDAWgBSI20LtiQUyDHInDEYb4cYJXuzJ
+ITAhBgNVHRIEGjAYhhZodHRwOi8vd3d3LmItdHJ1c3Qub3JnMA8GA1UdEwEB/wQF
+MAMBAf8wRQYDVR0gBD4wPDA6BgRVHSAAMDIwMAYIKwYBBQUHAgEWJGh0dHA6Ly93
+d3cuYi10cnVzdC5vcmcvZG9jdW1lbnRzL2NwczAOBgNVHQ8BAf8EBAMCAQYwRQYD
+VR0fBD4wPDA6oDigNoY0aHR0cDovL2NybC5iLXRydXN0Lm9yZy9yZXBvc2l0b3J5
+L0ItVHJ1c3RSb290QUNBLmNybDB4BggrBgEFBQcBAQRsMGowIwYIKwYBBQUHMAGG
+F2h0dHA6Ly9vY3NwLmItdHJ1c3Qub3JnMEMGCCsGAQUFBzAChjdodHRwOi8vY2Eu
+Yi10cnVzdC5vcmcvcmVwb3NpdG9yeS9CLVRydXN0Um9vdEFDQU9DU1AuY2VyMA0G
+CSqGSIb3DQEBCwUAA4ICAQA80F3bd7EII8Mbe+KApILCK5rgNKyzLBkpy074MCf+
+rRo1rnALybBUZSIyIOaxTFYEzHfubCMfGSd5VzZ9sJqCdM+REq7npzsx7f9fqfhi
+USnJD+nrpLfyGNG0YsLFJS2KUPpMwBzrTTylgSVVnrLL38c976vJKBYQ7IsUt0Lr
+DKYXoK+mBBsMlEfjgVsgsoeTF23tnqbAj6O9j7q0bkmw3AJkrCBEnhu183H0/Fnf
+u8NV9s7vY5byFE8+fJGocr91VB8F/BKoduZO07Zo9DqbAn0lQttraGKTtxNbyM7W
+hUgo9xaTkBHAx6PdjtW+cFCntKVibR4Pyaqqer2HOFVd/r+tG9FgEBX6fLkBdMmC
+wgrvD8317zMyW9oQBi7VL0lGEVU1c7vrSAvNMBWkM0eATh40JF3Lt+/rJGttXL14
++rW+JiUHVXV1ki0wUPgbS9P4Q7rjWte+43Z4axfYBOn079m2OXhDK/9lUvLMSPzh
+loLq5DVzPtiehveg3YN8HuyyaJx/YN1FUGDzzmD4YGnKdW+OuVa7tQtc5Lxxl6Vs
+YiEt6o5w81z5+4epgsathT1eNE3rjzIPqbCHCgGrXNgTWdqNC1U2XSNcLSA2UmIR
+oR4TomufK6LDMFhiXFN841Ly1lrJRaYVi6l83AAFHDHQynFHFbw1PWWlxaLl/IEw
+/Q==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIG7zCCBNegAwIBAgIBATANBgkqhkiG9w0BAQsFADBxMQswCQYDVQQGEwJCRzEY
+MBYGA1UEYRMPTlRSQkctMjAxMjMwNDI2MRIwEAYDVQQKEwlCT1JJQ0EgQUQxEDAO
+BgNVBAsTB0ItVHJ1c3QxIjAgBgNVBAMTGUItVHJ1c3QgUm9vdCBRdWFsaWZpZWQg
+Q0EwHhcNMTcwNDI1MTUyODQzWhcNMzcwNDI1MTUyODQzWjBxMQswCQYDVQQGEwJC
+RzEYMBYGA1UEYRMPTlRSQkctMjAxMjMwNDI2MRIwEAYDVQQKEwlCT1JJQ0EgQUQx
+EDAOBgNVBAsTB0ItVHJ1c3QxIjAgBgNVBAMTGUItVHJ1c3QgUm9vdCBRdWFsaWZp
+ZWQgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCvMcOLZlM8z4Sm
+jI6q0pT07UaZdFy9YdcErzJO2n8QSm4T9NQb9HYthmGNa/ZsBSh18rmC80pVpd9h
+PCYr6uiMK/8dSBrY65pIU2HLSqVrhe+b0Qx6OS6lkKKYlHProEas3R0pNTTrg2hO
+xJsEazBBghZC9ZUpermXR2ZlKM7QDMzEocRGVDjq4X49zx1a5TYlGJBAmAfwfVLt
+rWeIlYjLAXu0Y4ntSuYQFX9d3uIUaVMgW+9S+XK+MuBZosSHPwl1VHMCbnvJH+9v
+99/xheYYwfNsIesMI/cLdipaUFi76IhwgZk3741OACScqWl9/I2KKI+HfwPNQXKe
+988kwJ6C9k0Q3BrzpFiOySU9YGQLd21nq5/0qvINmWp/hQn0J9BYnJYkX8yKTNEq
+rEBV6xReIHHBVIgFCc89gcElryHHEJPw0HQOPdN9Yu099t7begrp9NCjY3h/RSg0
+JRua1pvIHxER3wCdjRuRdCDCcwpIMamVwsEtPs24lvgzBC0fqtVOP47uqEKgCcqY
+lEx/cCgQI4bnZxY4WHXYHUgmBiH9iMUD9mly3+JzvO8oaPQXjIUg1oIAGkRLRPH0
+JNqmG+6Uw/b4mAbwYS5DDnFp6bN3MrSvPvEu0T7LTIkoKF9nZjByaMSSoOV36CD/
+kc0rQ2KIUNGcrKevvM4QocOPrau5owIDAMv9o4IBkDCCAYwwHQYDVR0OBBYEFPKE
+7i41/vD62FBQsJxIiepaL9mrMB8GA1UdIwQYMBaAFPKE7i41/vD62FBQsJxIiepa
+L9mrMCEGA1UdEgQaMBiGFmh0dHA6Ly93d3cuYi10cnVzdC5vcmcwDwYDVR0TAQH/
+BAUwAwEB/zBFBgNVHSAEPjA8MDoGBFUdIAAwMjAwBggrBgEFBQcCARYkaHR0cDov
+L3d3dy5iLXRydXN0Lm9yZy9kb2N1bWVudHMvY3BzMA4GA1UdDwEB/wQEAwIBBjBF
+BgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsLmItdHJ1c3Qub3JnL3JlcG9zaXRv
+cnkvQi1UcnVzdFJvb3RRQ0EuY3JsMHgGCCsGAQUFBwEBBGwwajAjBggrBgEFBQcw
+AYYXaHR0cDovL29jc3AuYi10cnVzdC5vcmcwQwYIKwYBBQUHMAKGN2h0dHA6Ly9j
+YS5iLXRydXN0Lm9yZy9yZXBvc2l0b3J5L0ItVHJ1c3RSb290UUNBT0NTUC5jZXIw
+DQYJKoZIhvcNAQELBQADggIBAJkWw21cnW4B9sVeetOiaT3MdeN3Zz2PWZO4kkFE
+yLQ8Yp0U0a4fj/c/09sO+D6KXXOnmCmSB+vbGMBdT6OTsgeierCJxOEtKWdxKRQx
+rhDSwhYGiYvrATojdJAaaRS6Sz7AiezmqE6Nm0s3nWDk0Ne84YR4QQAHQ0HyX2oK
+6+sP/1WuCVH1hQAT6mR1T+H6E+dqtRKi6luWICcGhls0l6SwhfvUioAe17cX1DTS
+mnzNJ7f5kkwAih7s6vLgYltsEhqF/Mdlwmr2bkz4/Oo/5lorZNRrcNnsSUIdi6Ke
+smZnxiotIVYjYktLOMFHlI1EzqNX9N0hD3wGoaoh6q+pdD3ynl0euih/A20gI35F
+7NqeCJunQIbpfVR6C7NDzLlT62SFHeyO7HYXyrZHzabbsAIjoJuzyMR+fgTPDgWt
+1w9ro/jGBWxijoOUtajWoIB/QsnbTuAbhVoSLI5cKBaEiQkcDh4seHecBzsidUHI
+EBp0767rftu1SGBjXTRjoq2uPJvqdssAO09PRx5UoRWq/HvYfLz+1yC8TnH0A2uU
++bxt2xIgGmV4LAeLWo9GZpNlg4JhnwMO064UnQiaYJP7eS4cJaFAmWnFQfe65tij
+NantvhJGbRL0dmV9fk5MfGFDCkYNj5Eop8GqLdVGQQsdx3LtjQsdK2bgwRhEAMQ1
++uQZ
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+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
+So+/SJP8UkgxMHsQgwU=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIIYzCCBkugAwIBAgIBAjANBgkqhkiG9w0BAQUFADBrMQswCQYDVQQGEwJCRzEO
+MAwGA1UEBxMFU29maWExIDAeBgNVBAoTF0JPUklDQSAtIEJBTktTRVJWSUNFIEFE
+MRAwDgYDVQQLEwdCLVRydXN0MRgwFgYDVQQDEw9CLVRydXN0IFJvb3QgQ0EwHhcN
+MTAwODE3MTQxMjQ4WhcNMjUwODE3MTQxMjQ4WjCCAQsxCzAJBgNVBAYTAkJHMQ4w
+DAYDVQQIEwVTb2ZpYTEOMAwGA1UEBxMFU29maWExLzAtBgNVBAoTJkJPUklDQSAt
+IEJBTktTRVJWSUNFIEFELCBFSUsgMjAxMjMwNDI2MRAwDgYDVQQLEwdCLVRydXN0
+MSMwIQYDVQQDExpCLVRydXN0IE9wZXJhdGlvbmFsIENBIFFFUzEnMCUGA1UECRMe
+YnVsLiBUc2FyaWdyYWRza28gc2hvc2UgTm8gMTE3MQ0wCwYDVQQREwQxNzg0MSEw
+HwYJKoZIhvcNAQkBFhJjYTVxZXNAYi10cnVzdC5vcmcxGTAXBgNVBBQTECszNTkg
+MiA5IDIxNSAxMDAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC7Rdek
+BYc7uX8wQ38Q3isEtAsyag33JNeu16awt7Ikl/IJcv7W8r3Jkgv+PShbkgJBj0CH
+Oyndwn+MaojYVqwY3HKJYZBBCeo7IO28GXqIyk/Vh9jajADbReGzf9Pf63E1ZYaH
+c182EcGZ3txweIWo2OjnI64+YiyL/X8U+xkUDgSr5hFKmNp8cL2YpSSKr3qtFWvj
+50/GG3rkZ65IytEhQblRuWLoCMFed7rdo+s96jc+PmGHykAe0zIeJfOMgwf0QxEE
+A8xHIBqtgWatFBZ/MddZi9RhOJK9x5xBRSLFXOB4RekMBD6St/Np7C9XUoDufAUb
+LcbdnVOnyOs7eDioMDMR2fNqxiS86gjp4f38UgEX4Zvv7jbXzbKqUvGwm6ettcIJ
+cGcgygEDCh+StWnrms56ZDUzSaRro3jgdRq5taRo5PLKNjBtvGFlxQe5uoUFUGkK
+RIW2ufGIsyN3CuF+XE0Gc14mM8MJ4W5xlvG45zC2zdiicNmJ5fsYKP5WBrj5eqbt
+EG6rlD9nQoQ9m/Z51/wU+CLwkBV5gwDSp3qhEJgXWccBni63XelGi2y3hlqfmWcH
+Ye0c0qk0phD8FlwPovvYw7cMRZ1LOl43lj4U+oDWYWeDBwRhC2FccBxL51mWouVC
+rHhrQEuMctCdBEIKWJ5BdOAx5AokmShkTinX+wIDAJvNo4ICbjCCAmowHQYDVR0O
+BBYEFPI3d+hH+ukeEoLVuddycKlmD72KMIGVBgNVHSMEgY0wgYqAFJumSDojHzqp
+qIgoV2TtBJYcMMidoW+kbTBrMQswCQYDVQQGEwJCRzEOMAwGA1UEBxMFU29maWEx
+IDAeBgNVBAoTF0JPUklDQSAtIEJBTktTRVJWSUNFIEFEMRAwDgYDVQQLEwdCLVRy
+dXN0MRgwFgYDVQQDEw9CLVRydXN0IFJvb3QgQ0GCAQEwIQYDVR0SBBowGIYWaHR0
+cDovL3d3dy5iLXRydXN0Lm9yZzASBgNVHRMBAf8ECDAGAQH/AgEDMIHfBgNVHSAE
+gdcwgdQwRAYKKwYBBAH7dgEFATA2MDQGCCsGAQUFBwIBFihodHRwOi8vd3d3LmIt
+dHJ1c3Qub3JnL2RvY3VtZW50cy9jYTUvY3BzMEUGCysGAQQB+3YBBQEBMDYwNAYI
+KwYBBQUHAgEWKGh0dHA6Ly93d3cuYi10cnVzdC5vcmcvZG9jdW1lbnRzL2NhNS9j
+cHMwRQYLKwYBBAH7dgEFAQIwNjA0BggrBgEFBQcCARYoaHR0cDovL3d3dy5iLXRy
+dXN0Lm9yZy9kb2N1bWVudHMvY2E1L2NwczAOBgNVHQ8BAf8EBAMCAQYwUwYDVR0f
+BEwwSjBIoEagRIZCaHR0cDovL3d3dy5iLXRydXN0Lm9yZy9yZXBvc2l0b3J5L2Nh
+NXJvb3QvY3JsL2ItdHJ1c3RfY2E1X3Jvb3QuY3JsMDMGCCsGAQUFBwEBBCcwJTAj
+BggrBgEFBQcwAYYXaHR0cDovL29jc3AuYi10cnVzdC5vcmcwDQYJKoZIhvcNAQEF
+BQADggIBAJvP/zRgVMRkPkim5qvaHv7fL56IrVPml/mlD2Yqyry9tG3lhajTdG+D
+T3JWaYHjff8dB7D20XK3KQUf0cKbKufuRlHqCCrGnIE3qWXxCzyEGxtCZm4+eUsI
+bEgZQ0O0Sc968f69mmz1KgkIepDCW9dsgRwypOo8IjruD199fr0TpMltvqlArEF3
+EbOeJZ6d0tszWVLCy5Y7F970T+hgKicVkf9RXUmgdj9Xl1y3QWnDY4CVn3bQf732
+M3hpv1e/vmL86fz+JAtwEpw7ijAwO1XImfnjqTKjITRWoAUsH65CxZBOPIPix3kE
+FcQLngZNdrmW23CBUBbjHI8kx0VvOEImsOUxSMboN2WeuaL6MOzoIxjSWaJwrVBO
+s5/wMAB7AAYC0QxlUAUcOwkWoZRYLneQDpsDYngT7XOAVkaMiMTfWChg26EIGQdW
+Wijib0ScvDvoNTF4XkLHWSBrKB2G1SNTuz20ZYqSm7Vm2M2mC90gUHQJOky+w4bC
+V7FqETqcUhRp1LjSDqdiRysW9ahCgKKFRT2F26IVmPK4nYXDmBBy2MK9loTIY7lG
+tGVz4XLKxCkJZ8GWaZ7f5PDbQfaeNzSFWdVHYyP4EI8xYiOzDBnuUaQxmeJwrG8E
+TC+JPMS0GuY8BkEuwnjuEVpAeNCiOycik3cPODHGPz8ZAvuQKNC2
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIG4TCCBMmgAwIBAgIJAK7EeUZ21Q7RMA0GCSqGSIb3DQEBCwUAMHgxCzAJBgNV
+BAYTAkJHMRgwFgYDVQRhEw9OVFJCRy0yMDEyMzA0MjYxEjAQBgNVBAoTCUJPUklD
+QSBBRDEQMA4GA1UECxMHQi1UcnVzdDEpMCcGA1UEAxMgQi1UcnVzdCBPcGVyYXRp
+b25hbCBRdWFsaWZpZWQgQ0EwHhcNMTgwNjAxMTEzMzIzWhcNMjMwNTMxMTEzMzIz
+WjB+MQswCQYDVQQGEwJCRzEYMBYGA1UEYRMPTlRSQkctMjAxMjMwNDI2MRIwEAYD
+VQQKEwlCT1JJQ0EgQUQxEDAOBgNVBAsTB0ItVHJ1c3QxLzAtBgNVBAMTJkItVHJ1
+c3QgUXVhbGlmaWVkIFRpbWUgU3RhbXAgQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEA7Mx265YrppQtywsh3VXKOHF9Fviji9mrzmgmx0jT
+lbxGMx3+9RG+F23QOF8KpRSOhQ6164rMfmuWTW40KLOTGw6ATEI1GoIXmivkb+Q4
+rm2zmwQPWYCpDurDRbtz3dY63CdKErVy5GJlw/vEEZl0xiJ/fwyVhsdKDIbhnUFs
+RnaRPVB5htOLcClAjEMJLYmRVkT1dH2Tq36+LkYSIt7Sy2mH1929AWzHAHYx/ryP
+DRbVqoiXEiGUAZrP8f11/82mLp6iDjTno8zfoZlNaiMxvaR7zBGSBXXSHdZL1EKI
+3R4YB2DV3E/qfe93l6Pejm5PdL58HLnKP1euji8AueGyXQIDANkVo4ICZjCCAmIw
+HQYDVR0OBBYEFFeWkxGiXJLO+yOeatiFDFC3sDqkMB8GA1UdIwQYMBaAFCfPCEME
+8MWDN2eBF038BebbZYuwMCEGA1UdEgQaMBiGFmh0dHA6Ly93d3cuYi10cnVzdC5v
+cmcwIQYDVR0RBBowGIYWaHR0cDovL3RzYS5iLXRydXN0Lm9yZzAJBgNVHRMEAjAA
+MFUGA1UdIAROMEwwQAYKKwYBBAH7dgEGAzAyMDAGCCsGAQUFBwIBFiRodHRwOi8v
+d3d3LmItdHJ1c3Qub3JnL2RvY3VtZW50cy9jcHMwCAYGBACPegECMA4GA1UdDwEB
+/wQEAwIGwDAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDBMBgNVHR8ERTBDMEGgP6A9
+hjtodHRwOi8vY3JsLmItdHJ1c3Qub3JnL3JlcG9zaXRvcnkvQi1UcnVzdE9wZXJh
+dGlvbmFsUUNBLmNybDB/BggrBgEFBQcBAQRzMHEwIwYIKwYBBQUHMAGGF2h0dHA6
+Ly9vY3NwLmItdHJ1c3Qub3JnMEoGCCsGAQUFBzAChj5odHRwOi8vY2EuYi10cnVz
+dC5vcmcvcmVwb3NpdG9yeS9CLVRydXN0T3BlcmF0aW9uYWxRQ0FPQ1NQLmNlcjCB
+gAYIKwYBBQUHAQMEdDByMBUGCCsGAQUFBwsCMAkGBwQAi+xJAQIwCAYGBACORgEB
+MAgGBgQAjkYBBDBFBgYEAI5GAQUwOzA5FjNodHRwczovL3d3dy5iLXRydXN0Lm9y
+Zy9kb2N1bWVudHMvcGRzL3RzX3Bkc19lbi5wZGYTAmVuMA0GCSqGSIb3DQEBCwUA
+A4ICAQCUL8wk1fFd3SIogse9DeCF7hrUV0BN6IZdhfynbzszjtaVU6rmYhzkpZpR
+Z9JtM9L8Gl8KJ2YRiEohqCjR5ipZt/f/OEeWsvANnfJEWDYG5DBVtFxOx+UBqmaN
+T/BS+rmLiIENBW2+cm5QWJPu2z9S1WgD5qYatbeg4lxAzrUm1PBoQS3AWCGDYwmb
+U3P94ZRkNv93ncld++N348JFp67u/3ZDVGvh6mfCqzWeiISppo7ttTn+Pr9d8UZt
+WAxjwRZpy7N62wGyCHJE83WjYdOxgayQNpUgDfl0QNMOUKkhCyEogw/3FJsszdpI
+sR8rcMTT2GEZd5px+mCUADeN9mJr6f7j5Yb0zYrVGgNukuRe+mX6YxgXckyK+jBW
+WEqblhOmWmQZGBYUiB0xSNB+va9EeF+O+SRZsWK7dq8VfqejMLuCgU2BGHiaSpGT
+4+oA8mk8Sn7x/TTX0CmUI/l53IAY9RtuHJlaBiF4idyJ1yL2aY2pW1iC1W7vV1Vc
+yDyf/GCpBFADocoE0dtEMEBb3qULtPUwUSXWMwxylPTl1iHibYfSauDkQxiCYtY5
+5AgEI4EkR5v8IyH461OiELXeQjUwDFOqWVDKAd3f7DToJ6K2/YzRo/YAzjn1+vS6
+HMFFvitv9TXR00hL0d55MBqicpQqlGgUDbDw/09fNv9e/GQ6Kw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+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
+-----END CERTIFICATE-----
diff --git a/rebiss.py b/rebiss.py
new file mode 100644
index 0000000..28a3e44
--- /dev/null
+++ b/rebiss.py
@@ -0,0 +1,522 @@
+# SPDX-License-Identifier: GPL-3.0-only
+
+import base64
+import logging
+import getpass
+import hashlib
+import json
+import os
+import re
+import subprocess
+import unicodedata
+import time
+import datetime
+
+import OpenSSL.crypto as cr
+
+
+class HashAlg:
+ __slots__ = ('len', 'hashlib_name', 'mech')
+
+ def __init__(self, len, hashlib_name, mech):
+ self.len = len
+ self.hashlib_name = hashlib_name
+ self.mech = mech
+
+HASH_ALG = {
+ 'SHA256': HashAlg(32, 'sha256', 'SHA256-RSA-PKCS'),
+ 'SHA384': HashAlg(48, 'sha384', 'SHA384-RSA-PKCS'),
+ 'SHA512': HashAlg(64, 'sha512', 'SHA512-RSA-PKCS'),
+}
+
+
+def pkcs11_list():
+ lines = subprocess.check_output(['pkcs11-tool', '--list-slots'], text=True).splitlines()
+ readers = []
+ for line in lines:
+ m = re.fullmatch('Slot ([0-9]+) \\(.*\\): .*', line)
+ if m is None:
+ continue
+ readers.append(m.group(1))
+
+ certs = {}
+ for reader in readers:
+ lines = subprocess.check_output(['pkcs15-tool', '--reader', reader, '-c'], text=True).splitlines()
+ for line in lines:
+ m = re.fullmatch('\tID *: *([0-9a-f]+)$', line)
+ if m is None:
+ continue
+ cert_id = m.group(1)
+ cert_data = subprocess.check_output(['pkcs15-tool', '--reader', reader, '--read-certificate', cert_id])
+ cert = cr.load_certificate(cr.FILETYPE_PEM, cert_data)
+ certs[reader,cert_id] = cert
+
+ return certs
+
+def pkcs11_sign(key, msg, hash_alg, pin):
+ hash_val = hashlib.new(hash_alg.hashlib_name, msg).digest()
+
+ reader,kid = key
+ env = os.environ.copy()
+ env['PIN'] = pin
+ proc = subprocess.run(['pkcs11-tool', '--slot', reader, '--id', kid, '-m', hash_alg.mech, '--pin', 'env:PIN', '--sign'], input=hash_val, stdout=subprocess.PIPE, env=env, check=True)
+
+ return proc.stdout
+
+
+def load_ts(ts):
+ t = time.strptime(ts.decode('ascii'), '%Y%m%d%H%M%SZ')
+ return datetime.datetime(
+ t.tm_year,
+ t.tm_mon,
+ t.tm_mday,
+ t.tm_hour,
+ t.tm_min,
+ t.tm_sec,
+ tzinfo=datetime.timezone.utc,
+ )
+
+def check_selector(cert, selector, valid_only):
+ if valid_only:
+ valid_from = load_ts(cert.get_notBefore())
+ valid_until = load_ts(cert.get_notAfter())
+ if not (valid_from <= datetime.datetime.now(datetime.timezone.utc) <= valid_until):
+ return False
+
+ vals = selector.get('issuers')
+ if vals is not None:
+ issuer_cn = [val.decode('utf-8', 'surrogateescape') for name,val in cert.get_issuer().get_components() if name == b'CN']
+ if issuer_cn:
+ issuer_cn = issuer_cn[0]
+ else:
+ issuer_cn = ''
+ if f'CN={issuer_cn}' not in vals:
+ return False
+
+ return True
+
+def describe_name(val):
+ return ', '.join(f'{k.decode("ascii")}={escape_bad(v.decode("utf-8", "surrogateescape"), ",=")}' for k,v in val.get_components())
+
+def describe_cert(cert):
+ valid_from = load_ts(cert.get_notBefore())
+ valid_until = load_ts(cert.get_notAfter())
+ return (
+ f'Issuer: {describe_name(cert.get_issuer())}\n'
+ f'Subject: {describe_name(cert.get_subject())}\n'
+ f'Valid from: {valid_from:%Y-%m-%d %H:%M:%S} UTC\n'
+ f'Valid until: {valid_until:%Y-%m-%d %H:%M:%S} UTC\n'
+ )
+
+def escape_bad(l, esc=''):
+ r = []
+ for c in l:
+ o = ord(c)
+ if 0xdc80 <= o <= 0xdd00:
+ r.append(f'\\x{ord(c)-0xdc00:02x}')
+ continue
+ if c == '\\':
+ r.append('\\\\')
+ continue
+ if o < 32 or o == 127 or (c in esc and o < 128):
+ r.append(f'\\x{ord(c):02x}')
+ continue
+ cat = unicodedata.category(c)
+ if cat in {'Cc', 'Cf', 'Cs'} or (cat.startswith('Z') and c != ' ') or c in esc:
+ if o < 0x10000:
+ r.append(f'\\u{o:04x}')
+ else:
+ r.append(f'\\U{o:08x}')
+ continue
+ r.append(c)
+ return ''.join(r)
+
+def print_blob(name, data, /):
+ if type(data) is str:
+ kind = 'text'
+ elif type(data) is bytes:
+ kind = 'blob'
+ data = '\n'.join(escape_bad(l.decode('utf-8', 'surrogateescape')) for l in data.splitlines())
+ else:
+ kind = 'repr'
+ data = repr(data)
+ print(f'{name} ({kind}):')
+
+ for line in data.splitlines():
+ print(f' > {line!s}')
+
+def prompt(txt, /, default=False):
+ while True:
+ if default:
+ d = 'Y/n'
+ else:
+ d = 'y/N'
+ txt = input(f'{txt} [{d}]? ')
+ if txt in {'y', 'Y'}:
+ return True
+ if txt in {'n', 'N'}:
+ return False
+ if not txt:
+ return default
+
+STATUS_TEXT = {
+ 200: 'OK',
+ 201: 'Created',
+ 202: 'Accepted',
+ 203: 'Non-Authoritative Information',
+ 204: 'No Content',
+ 205: 'Reset Content',
+ 206: 'Partial Content',
+ 207: 'Multi-Status',
+ 208: 'Already Reported',
+ 226: 'IM Used',
+ 300: 'Multiple Choices',
+ 301: 'Moved Permanently',
+ 302: 'Found',
+ 303: 'See Other',
+ 304: 'Not Modified',
+ 305: 'Use Proxy',
+ 306: 'Switch Proxy',
+ 307: 'Temporary Redirect',
+ 308: 'Permanent Redirect',
+ 400: 'Bad Request',
+ 401: 'Unauthorized',
+ 402: 'Payment Required',
+ 403: 'Forbidden',
+ 404: 'Not Found',
+ 405: 'Method Not Allowed',
+ 406: 'Not Acceptable',
+ 407: 'Proxy Authentication Required',
+ 408: 'Request Timeout',
+ 409: 'Conflict',
+ 410: 'Gone',
+ 411: 'Length Required',
+ 412: 'Precondition Failed',
+ 413: 'Payload Too Large',
+ 414: 'URI Too Long',
+ 415: 'Unsupported Media Type',
+ 416: 'Range Not Satisfiable',
+ 417: 'Expectation Failed',
+ 418: 'I\'m a teapot',
+ 421: 'Misdirected Request',
+ 422: 'Unprocessable Entity',
+ 423: 'Locked',
+ 424: 'Failed Dependency',
+ 425: 'Too Early',
+ 426: 'Upgrade Required',
+ 428: 'Precondition Required',
+ 429: 'Too Many Requests',
+ 431: 'Request Header Fields Too Large',
+ 451: 'Unavailable For Legal Reasons',
+ 500: 'Internal Server Error',
+ 501: 'Not Implemented',
+ 502: 'Bad Gateway',
+ 503: 'Service Unavailable',
+ 504: 'Gateway Timeout',
+ 505: 'HTTP Version Not Supported',
+ 506: 'Variant Also Negotiates',
+ 507: 'Insufficient Storage',
+ 508: 'Loop Detected',
+ 510: 'Not Extended',
+ 511: 'Network Authentication Required',
+}
+
+STATUS_FULL = {status: f'{status} {text}' for status,text in STATUS_TEXT.items()}
+
+class Req:
+ __slots__ = ('path', 'query_string', 'method', 'headers', 'input')
+
+ def __init__(self, env):
+ self.path = env['PATH_INFO']
+ self.query_string = env.get('QUERY_STRING')
+ self.method = env['REQUEST_METHOD']
+ self.input = env['wsgi.input']
+ headers = self.headers = {}
+ for k,v in env.items():
+ if not k.startswith('HTTP_'):
+ continue
+ k = ''.join(map(str.title, k[5:].split('_')))
+ headers[k] = v
+
+ def read_data(self, /):
+ d = self.input
+ del self.input
+ return d.read()
+
+ def resp(self, /, status, headers, data, *, content_type):
+ assert type(status) is int
+ assert type(data) is bytes
+ assert type(content_type) is str
+ headers = [*headers]
+ assert all(type(k) is str and type(v) is str for k,v in headers)
+ hdr = headers.append
+
+ hdr(('Access-Control-Allow-Methods', 'GET, POST'))
+ hdr(('Access-Control-Allow-Headers', 'Accept, Content-Type'))
+
+ try:
+ hdr(('Access-Control-Allow-Origin', self.headers['Origin']))
+ except KeyError:
+ pass
+
+ if self.method != 'OPTIONS':
+ hdr(('Content-Type', content_type))
+ hdr(('Content-Length', f'{len(data)}'))
+
+ hdr(('Cache-Control', 'no-cache'))
+
+ return (status, headers, data)
+
+ def resp_json(self, /, data, *, status=200, content_type='application/json;charset=UTF-8', headers=()):
+ return self.resp(
+ status,
+ headers,
+ json.dumps(data).encode('utf-8'),
+ content_type=content_type,
+ )
+
+ def resp_error(self, status, /):
+ try:
+ text = STATUS_TEXT[status]
+ except KeyError:
+ text = f'HTTP {status}'
+ if message is not None:
+ text = f'{text}: {message}'
+ return self.resp(
+ status,
+ (),
+ text.encode('utf-8'),
+ content_type='text/plain',
+ )
+
+ def resp_404(self, /):
+ return self.resp_error(404, **kw)
+
+ def resp_405(self, /):
+ return self.resp_error(405, **kw)
+
+ def resp_biss_error(self, /, status, message):
+ return self.resp_json({
+ 'status': 'failed',
+ 'reasonCode': status,
+ 'reasonText': message,
+ }, status=status)
+
+ def resp_biss_ok(self, /, **kw):
+ return self.resp_json({
+ 'status': 'ok',
+ 'reasonCode': 200,
+ 'reasonText': 'response.success',
+ **kw,
+ })
+
+
+cr.X509StoreFlags.NO_CHECK_TIME = 0x200000
+store = cr.X509Store()
+store.set_flags(cr.X509StoreFlags.CRL_CHECK_ALL | cr.X509StoreFlags.X509_STRICT | cr.X509StoreFlags.NO_CHECK_TIME)
+store.load_locations('cacerts.pem')
+
+certs = pkcs11_list()
+
+def req_options(req):
+ return req.resp(200, (), b'', content_type='')
+
+def req_get_version(req):
+ return req.resp_json({
+ 'version': '2.30',
+ 'httpMethods': 'GET, POST',
+ 'contentTypes': 'data',
+ 'signatureTypes': 'signature',
+ 'selectorAvailable': True,
+ 'hashAlgorithms': 'SHA256, SHA384, SHA512',
+ })
+
+def req_get_status(req):
+ return req.resp_json('')
+
+def req_post_getsigner(req):
+ data = json.loads(req.read_data().decode('utf-8'))
+
+ valid_only = data.get('showValidCerts', False)
+ # WAT
+ if valid_only == 'true':
+ valid_only = True
+ if valid_only == 'false':
+ valid_only = False
+ if type(valid_only) is not bool:
+ return req.resp_biss_error(400, 'error.request.bad-type')
+
+ selector = data.get('selector', {})
+
+ for cert in certs.values():
+ if check_selector(cert, selector, valid_only):
+ break
+ else:
+ return req.resp_biss_error(403, 'error.user-canceled')
+
+ return req.resp_biss_ok(
+ chain = [
+ base64.b64encode(cr.dump_certificate(cr.FILETYPE_ASN1, cert)).decode('ascii'),
+ ],
+ )
+
+def req_post_sign(req):
+ data = json.loads(req.read_data().decode('utf-8'))
+
+ try:
+ own_cert_data = data['signerCertificateB64']
+ own_cert_data = base64.b64decode(own_cert_data)
+ except (KeyError, IndexError):
+ return req.resp_biss_error(400, 'error.wsp-cert-not-found')
+
+ for own_key,own_cert in certs.items():
+ if cr.dump_certificate(cr.FILETYPE_ASN1, own_cert) == own_cert_data:
+ break
+ else:
+ return req.resp_biss_error(400, 'error.request.bad-type')
+
+ # BISS ignores it
+ # if data['contentType'] != 'data':
+ # return req.resp_biss_error(400, 'error.request.bad-type')
+
+ try:
+ hash_alg = HASH_ALG[data.get('hashAlgorithm', 'SHA256')]
+ except KeyError:
+ return req.resp_biss_error(400, 'error.request.bad-type')
+
+ msgs = []
+ for i,(msg,sig,server_cert) in enumerate(zip(data['contents'], data['signedContents'], data['signedContentsCert'])):
+ msg = base64.b64decode(msg)
+ sig = base64.b64decode(sig)
+ server_cert = cr.load_certificate(cr.FILETYPE_ASN1, base64.b64decode(server_cert))
+
+ try:
+ # WAT?
+ cr.verify(server_cert, sig, hashlib.new(hash_alg.hashlib_name, msg).digest(), hash_alg.hashlib_name)
+ except cr.Error:
+ return req.resp_biss_error(400, 'error.request.signature-not-val')
+
+ verify_ctx = cr.X509StoreContext(store, server_cert, None)
+ try:
+ verify_ctx.verify_certificate()
+ except cr.X509StoreContextError as exc:
+ if not prompt(f'Server certificate invalid ({exc!s}); continue'):
+ return req.resp_biss_error(403, 'error.request.certificate-not-valid')
+
+ msgs.append((msg, server_cert))
+
+ t = data.get('confirmText')
+ if t is not None:
+ print_blob('Confirmation text', t)
+
+ t = data.get('additionalConfirmText')
+ if t is not None:
+ print_blob('Additional confirmation text', t)
+
+ for (msg,server_cert) in msgs:
+ print_blob(f'Message #{1+i}', msg)
+ print_blob('Server name', describe_cert(server_cert))
+
+ if not prompt('Sign'):
+ return req.resp_biss_error(403, 'error.user-canceled')
+
+ pin = getpass.getpass('PIN: ')
+
+ sigs = []
+ for (msg, server_cert) in msgs:
+ sigs.append(pkcs11_sign(own_key, msg, hash_alg, pin))
+
+ return req.resp_biss_ok(
+ signatures = [base64.b64encode(sig).decode('ascii') for sig in sigs],
+ )
+
+def req_post_chain_found(req):
+ # stub
+ data = json.loads(req.read_data().decode('utf-8'))
+ return req.resp_biss_ok(chainFound=True)
+
+
+def fix_methods(hnd):
+ assert type(hnd) is dict and all(type(k) is str for k in hnd)
+ hnd = hnd.copy()
+
+ if 'GET' in hnd and 'HEAD' not in hnd:
+ on_get = hnd['GET']
+ def on_head(req):
+ (status, headers, data) = on_get(req)
+ return (status, headers, None)
+ hnd['HEAD'] = on_head
+
+ hnd['OPTIONS'] = req_options
+
+ return hnd
+
+def resp_exc(env, resp, exc):
+ rdata = (
+ '<!doctype html><html><head><meta charset="utf-8" /><title>Internal Server Error</title></head><body>'
+ '<h1>Internal Server Error</h1>'
+ '</body></html>'
+ ).encode()
+ resp('500 Internal Server Error', [
+ ('Content-Type', 'text/html;charset=utf-8'),
+ ('Content-Length', f'{len(rdata)}'),
+ ])
+ return rdata
+
+
+handlers = {
+ '/version': {
+ 'GET': req_get_version,
+ },
+ '/status': {
+ 'GET': req_get_status,
+ },
+ '/getsigner': {
+ 'POST': req_post_getsigner,
+ },
+ '/sign': {
+ 'POST': req_post_sign,
+ },
+ '/chainFound': {
+ 'POST': req_post_chain_found,
+ },
+}
+
+handlers = {path: fix_methods(sub) for path,sub in handlers.items()}
+
+def application(env, resp):
+ try:
+ req = Req(env)
+ except Exception as exc:
+ logging.exception('Failed to parse request')
+ return resp_exc(env, resp, exc)
+
+ try:
+ h = handlers[req.path]
+ except KeyError:
+ h = Req.resp_404
+ else:
+ try:
+ h = h[req.method]
+ except KeyError:
+ h = Req.resp_405
+
+ try:
+ rcode, rhdr, rdata = h(req)
+ except Exception as exc:
+ logging.exception('Request handler failed')
+ return resp_exc(env, resp, exc)
+
+ assert type(rcode) is int
+
+ try:
+ rcode = STATUS_FULL[rcode]
+ except KeyError:
+ rcode = f'{rcode}'
+
+ resp(rcode, rhdr)
+ return rdata
+
+tty = os.open('/dev/tty', os.O_RDWR)
+os.dup2(tty, 0)
+os.dup2(tty, 1)
diff --git a/run.sh b/run.sh
new file mode 100755
index 0000000..dc75fda
--- /dev/null
+++ b/run.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+exec uwsgi --master --plugin python3 --https-socket 127.0.0.1:53952,ssl.pem,ssl.key --module rebiss
diff --git a/ssl.key b/ssl.key
new file mode 100644
index 0000000..52fcf99
--- /dev/null
+++ b/ssl.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDAdbiRz+ePjWQ5
+fb+LSNBsmryjHfzzg6qZH39mlbV5QLbR7Mj/jCOwkQ+BUc7GdrMuIBPwoKslEI7V
+xwIs3s0Wdh9oNnZEbRYMF/ad2wkgjjn/FCGGONm0EsRGipyTaOaai9B2wZ5lS1vE
+r+uxlrImtig83YRAn2Prj6ON+VMrnDUgiHvNcU9Ln8eZU+o8TaQouEsXgFz8Y33H
+KTR6cBsLbMCSifwdm4rUIMuAx99eO+uDetF3bd2bf6beqWTjV0xOMa8xzOfb54wM
+DVDWy7p1swT7/JmNbUeD+Ji8VjgZpjQ6inRQghm+UCb4AaazK/buVuZGS2ZKMRpu
+CkiegNj/AgMBAAECggEAHgtilEoEnUmBJLkc3qAFKtHJx/G7AW2DriLCJIq+HonR
+NcVFUstsQ43prJJPpCDMnlM9+aRYrfu/Pd/dgZFd1SH8i4FmW2Pne1zUUCvs6VWl
+TSI14jpcFfs7WRhzZ/m0zNnYYb7zvLQTxTPXU00HTk7FH6H5xDgoWjzSrKuy5aLY
+RM2lGZzylVzLag0MUJnp2mvB3lTeTQazritIp/8vhRZL129C7PGKYBAAjF/1MFu3
+OWE+r1yo4HtjcOi/NYn4hTcI3/wbPijkAVhlCIqolCkuZ0QT9h15DtszWC0O0XOr
+ifgIeM7sMCn4D9EKulWuj5+nBWZbeVxTZekgmqfyrQKBgQDd3Ob1SjXx8CIMIiaH
+G14HwU+FJv6ypnI4lEZPiW/TwXGR8fpLzZgnfqB9uhAH0stXcR0D8oeKvzPpcDyJ
+7sXt18kpwY2nj8W1KX41+lNnQEuQBYYh24B/rdYyujctKcTsXMbQcQVDLfot9MAm
+5wDkGlRs66opUFct5OBWchg0hQKBgQDeEqS33OPfhCJ+YrOaNjQXDi03gW4pSQsY
+upZ9FUVV8O1IsuzWjd2DR+Vv+gJ9k7ufaJ6RR3pO+n/lnCDK5oaZe0cm0NaSldHf
+PJAazN0e3tUnSlWKbYPskLYq2rA7LhLPAj69Qx8DN+HDVjG1fpjOxrnMCgtIN9Uk
+c2XcnA2gswKBgHTzxJ/bDdLKhfglbG/eIlQuN+/13V9pVF608tdsmJFksKyBhTK+
+Xw26t3lvQpINMXmXu/bfu0mNfxz4OEFxp8636GflivmgVVUpWUm0+pA8GVkDQHDK
+l9M2XOqCtSFOmmsKgRdR7Lc8HlxpX31iWlrj8ks0c4+xtTMRKp0HKfFNAoGAYDXB
+iSq4yrhrXUgTuabsEGUC7/4yJM7RcOg9lHlnraEQnCNwek/B/UNjwoyVEaS0b46a
+zCQkeB0M1gQ7LMyM2efP3qAxphYkc8vAPuwsjFhMDpak0rE5q37SfWR7X1hJq8e0
+OLjgsbs/IyqvpIrxVvw/uIdNdf9CZ2VbZyVwvgMCgYBPlnHKN5+rzGGO47PBB5GG
+M0WeFpHW72Q1AdZOiR3Tnqtg7u9Ew4vjgmDA2TPL4iTdGzqO/Mddji6Z6sCGKtcD
+NiJ7rY4EnvroPvxKfd5P99Uhz4M68FvCBrBuNVv7Cqct6nDvyPckYxjsEq0qnUju
+eFu5Djr9JvONTB/D2Aju1g==
+-----END PRIVATE KEY-----
diff --git a/ssl.pem b/ssl.pem
new file mode 100644
index 0000000..26c7ad9
--- /dev/null
+++ b/ssl.pem
@@ -0,0 +1,40 @@
+-----BEGIN CERTIFICATE-----
+MIIHFDCCBPygAwIBAgIIbHufn8GDHocwDQYJKoZIhvcNAQELBQAwdzELMAkGA1UE
+BhMCQkcxGDAWBgNVBGETD05UUkJHLTIwMTIzMDQyNjESMBAGA1UEChMJQk9SSUNB
+IEFEMRAwDgYDVQQLEwdCLVRydXN0MSgwJgYDVQQDEx9CLVRydXN0IE9wZXJhdGlv
+bmFsIEFkdmFuY2VkIENBMB4XDTIwMDcwMzEwMzkzN1oXDTMwMDcwMTEwMzkzN1ow
+gYAxEjAQBgNVBAMTCWxvY2FsaG9zdDEPMA0GA1UECxMGT1YgU1NMMRIwEAYDVQQK
+EwlCT1JJQ0EgQUQxGDAWBgNVBGETD05UUkJHLTIwMTIzMDQyNjEOMAwGA1UECBMF
+U29maWExDjAMBgNVBAcTBVNvZmlhMQswCQYDVQQGEwJCRzCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBAMB1uJHP54+NZDl9v4tI0GyavKMd/PODqpkff2aV
+tXlAttHsyP+MI7CRD4FRzsZ2sy4gE/CgqyUQjtXHAizezRZ2H2g2dkRtFgwX9p3b
+CSCOOf8UIYY42bQSxEaKnJNo5pqL0HbBnmVLW8Sv67GWsia2KDzdhECfY+uPo435
+UyucNSCIe81xT0ufx5lT6jxNpCi4SxeAXPxjfccpNHpwGwtswJKJ/B2bitQgy4DH
+314764N60Xdt3Zt/pt6pZONXTE4xrzHM59vnjAwNUNbLunWzBPv8mY1tR4P4mLxW
+OBmmNDqKdFCCGb5QJvgBprMr9u5W5kZLZkoxGm4KSJ6A2P8CAwEAAaOCApgwggKU
+MB0GA1UdDgQWBBRvv8jv0YzeAofvbLsnXHqZbALpSTAfBgNVHSMEGDAWgBQH3Kow
+dpi3hUttAxjI482nezaC7zAhBgNVHRIEGjAYhhZodHRwOi8vd3d3LmItdHJ1c3Qu
+b3JnMAkGA1UdEwQCMAAwYAYDVR0gBFkwVzBBBgsrBgEEAft2AQcBBjAyMDAGCCsG
+AQUFBwIBFiRodHRwOi8vd3d3LmItdHJ1c3Qub3JnL2RvY3VtZW50cy9jcHMwCAYG
+Z4EMAQICMAgGBgQAj3oBBzAOBgNVHQ8BAf8EBAMCBaAwTwYDVR0lBEgwRgYIKwYB
+BQUHAwEGCCsGAQUFBwMCBggrBgEFBQcDBAYIKwYBBQUHAwUGCCsGAQUFBwMGBggr
+BgEFBQcDBwYIKwYBBQUIAgIwTAYDVR0fBEUwQzBBoD+gPYY7aHR0cDovL2NybC5i
+LXRydXN0Lm9yZy9yZXBvc2l0b3J5L0ItVHJ1c3RPcGVyYXRpb25hbEFDQS5jcmww
+ewYIKwYBBQUHAQEEbzBtMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5iLXRydXN0
+Lm9yZzBGBggrBgEFBQcwAoY6aHR0cDovL2NhLmItdHJ1c3Qub3JnL3JlcG9zaXRv
+cnkvQi1UcnVzdE9wZXJhdGlvbmFsQUNBLmNlcjBpBggrBgEFBQcBAwRdMFswFQYI
+KwYBBQUHCwIwCQYHBACL7EkBAjBCBgYEAI5GAQUwODA2FjBodHRwczovL3d3dy5i
+LXRydXN0Lm9yZy9kb2N1bWVudHMvcGRzL3Bkc19lbi5wZGYTAmVuMCsGA1UdEQQk
+MCKCCWxvY2FsaG9zdIcEfwAAAYEPYWRtaW5AYm9yaWNhLmJnMA0GCSqGSIb3DQEB
+CwUAA4ICAQCpXjBStAqiKxVLmMAdEuIS+8Yv9BsMX48G2+tTZmXf+F+UHDYXrPw3
+xqWn3Ot53ATxC0FNaKt26z7YHFVWUzPooZrF2Cacy1D3Xt63z1o5XXPVa2lW5wW5
++FQDx4DX1hbRfQJVSKblUeobtZiBWsYoGWejMSMUw106e4Lx4vbHugFTX5Y/1joN
+hdZtVijsjujyrMwCDgrNkxvISBxoXl+ia7QEWYGdfJbGoOZ55w/amlpR14TkX4Xt
+Ksl1aqhVF7Q8Lv3vOYgZPUfATASohW9p97mTG/onws3dcTfAc74xMX6BhPQJofm4
+sDg6EkYPvY7DWowJV8K7qoeZf+sXYKlXe2QRwQjALCVtUrUtGR57PdFtYj84EuTf
+3jHhfbLpGk9lG70dkoac/yelExgLOUe3/gDZXqkatKiaTXMyrgsCneTF9x0HnkCp
+KF2wjPH1Ytegc6Yegu3UjqhHgKDg5V9IdcCpdG/rIfUWS7IYXXu+GA6woAXZf431
+i5Hwj/H2I0SMK6KpOBOwYZODLt8HCgxTC7DR6dmor4i+Fl2TkNnEzjs8dgOkwNT+
+Cv2BfDw17o/BHsO2ucCwRBIJM46RtLf7tAX6ud2i2lKdDA525HzDFHZNyhBs7zC3
+PYBiNOVzue6ETbW5ptVi5yt/+aomZzseNsm868KIZTqPHdLzVZJVrw==
+-----END CERTIFICATE-----