def decode(buffer: bytes): from samson.public_key.ecdsa import ECDSA cert, _left_over = decoder.decode(buffer, asn1Spec=rfc2459.Certificate()) pub_info = cert['tbsCertificate']['subjectPublicKeyInfo'] curve_params, _ = decoder.decode( Bytes(pub_info['algorithm']['parameters'])) p = int(curve_params[1][1]) b = Bytes(curve_params[2][1]).int() q = int(curve_params[4]) gx, gy = ECDSA.decode_point(Bytes(curve_params[3])) curve = WeierstrassCurve(a=-3, b=b, ring=ZZ / ZZ(p), cardinality=q, base_tuple=(gx, gy)) x, y = ECDSA.decode_point(Bytes(int(pub_info['subjectPublicKey']))) ecdsa = ECDSA(curve.G, None, d=1) ecdsa.Q = curve(x, y) return ecdsa
def test_ecdsa(self): for curve in [P224, P256, P384, P521]: for _ in range(5): ca = ECDSA(curve.G) leaf = ECDSA(curve.G) self._run_test(ca, leaf)
def _run_test(self, curve, x, message, H, k, expected_sig): ecdsa = ECDSA(curve.G, H, d=x) r, s = ecdsa.sign(message, k=k) sig = (int(r), int(s)) self.assertEqual(sig, expected_sig) self.assertTrue(ecdsa.verify(message, sig))
def decode(buffer: bytes, **kwargs) -> object: """ Decodes a JWK JSON string into an ECDSA object. Parameters: buffer (bytes/str): JWK JSON string. Returns: ECDSA: ECDSA object. """ from samson.public_key.ecdsa import ECDSA if issubclass(type(buffer), (bytes, bytearray)): buffer = buffer.decode() jwk = json.loads(buffer) curve = JWK_INVERSE_CURVE_LOOKUP[jwk['crv']] x = Bytes(url_b64_decode(jwk['x'].encode('utf-8'))).int() y = Bytes(url_b64_decode(jwk['y'].encode('utf-8'))).int() if 'd' in jwk: d = Bytes(url_b64_decode(jwk['d'].encode('utf-8'))).int() else: d = 0 ecdsa = ECDSA(G=curve.G, hash_obj=None, d=d) ecdsa.Q = curve(x, y) return ecdsa
def _run_import_pem_enc(self, enc_priv): with self.assertRaises(ValueError): ECDSA.import_key(enc_priv) enc_ecdsa = ECDSA.import_key(enc_priv, PEM_PASSPHRASE) dec_ecdsa = ECDSA.import_key(TEST_PEM_DEC) self.assertEqual((enc_ecdsa.G, enc_ecdsa.d, enc_ecdsa.Q), (dec_ecdsa.G, dec_ecdsa.d, dec_ecdsa.Q))
def derive(self, kek: tuple, derived_length: int, header: dict) -> Bytes: from samson.encoding.general import PKIAutoParser # Support input formats # 1) (priv, pub): Allows user to specify their own private key. if type(kek) is tuple: priv_key, peer_pub = kek # 2) priv: Pull 'epk from header. Used in decryption. elif 'epk' in header: priv_key = kek peer_pub = PKIAutoParser.import_key( json.dumps(header['epk']).encode('utf-8')) # 3) pub: Ephemeral private key. else: priv_key = ECDHE(G=kek.curve.G) peer_pub = kek # Need to clean up priv and pub keys if type(priv_key) is ECDSA: priv_key = ECDHE(d=priv_key.d, G=priv_key.G) if hasattr(peer_pub, 'Q'): peer_pub = peer_pub.Q elif hasattr(peer_pub, 'pub'): peer_pub = peer_pub.pub # Add 'epk' header if not present if not 'epk' in header: if type(priv_key) in [ECDSA, ECDHE]: encoded_key = ECDSA( G=priv_key.G, d=priv_key.d).export_public_key(encoding=PKIEncoding.JWK) else: encoded_key = JWKEdDSAPublicKey.encode(priv_key) header['epk'] = json.loads(encoded_key.decode()) # Actual key derivation process agreement_key = priv_key.derive_key(peer_pub) apu = url_b64_decode( header['apu'].encode('utf-8')) if 'apu' in header else b'' apv = url_b64_decode( header['apv'].encode('utf-8')) if 'apv' in header else b'' alg_id = header[self.key_alg].encode('utf-8') other_info = b''.join([ Bytes(len(item)).zfill(4) + Bytes.wrap(item) for item in [alg_id, apu, apv] ]) + Bytes(derived_length * 8).zfill(4) kdf = ConcatKDF(SHA256(), derived_length) return kdf.derive(agreement_key, other_info)
def decode(buffer: bytes, **kwargs): from samson.public_key.ecdsa import ECDSA items = bytes_to_der_sequence(buffer) d = Bytes(items[1]).int() x, y, curve = parse_ec_params(items, 2, 3) ecdsa = ECDSA(G=curve.G, hash_obj=None, d=d) ecdsa.Q = curve(x, y) return ecdsa
def test_import_export_public(self): ecdsa_pub = ECDSA.import_key(TEST_PUB) ecdsa_priv = ECDSA.import_key(TEST_PRIV) der_bytes = ecdsa_pub.export_public_key(encoding=PKIEncoding.X509) new_pub = ECDSA.import_key(der_bytes) self.assertEqual(ecdsa_pub.Q, ecdsa_priv.Q) self.assertEqual(new_pub.Q, ecdsa_priv.Q) self.assertEqual(der_bytes.replace(b'\n', b''), TEST_PUB.replace(b'\n', b''))
def test_import_export_private(self): ecdsa = ECDSA.import_key(TEST_PRIV) der_bytes = ecdsa.export_private_key(encoding=PKIEncoding.PKCS1) new_ecdsa = ECDSA.import_key(der_bytes) self.assertEqual((ecdsa.G, ecdsa.d, ecdsa.Q), (new_ecdsa.G, new_ecdsa.d, new_ecdsa.Q)) self.assertEqual((ecdsa.G.curve, ecdsa.d, ecdsa.Q), (EXPECTED_CURVE, EXPECTED_PRIV, PUB_POINT)) self.assertEqual(der_bytes.replace(b'\n', b''), TEST_PRIV.replace(b'\n', b''))
def test_import_openssh(self): for key, passphrase in [ TEST_OPENSSH0, TEST_OPENSSH1, TEST_OPENSSH2, TEST_OPENSSH3 ]: if passphrase: with self.assertRaises(ValueError): ECDSA.import_key(key) ecdsa = ECDSA.import_key(key, passphrase=passphrase) self.assertEqual(ecdsa.d * ecdsa.G, ecdsa.Q) self.assertLess(ecdsa.d, ecdsa.q)
def decode(buffer: bytes, **kwargs): from samson.public_key.ecdsa import ECDSA _, pub = parse_openssh_key(buffer, SSH_PUBLIC_HEADER, ECDSAPublicKey, ECDSAPrivateKey, None) curve, x_y_bytes, d = pub.curve, pub.x_y_bytes, 1 curve = SSH_INVERSE_CURVE_LOOKUP[curve.decode()] ecdsa = ECDSA(G=curve.G, hash_obj=None, d=d) ecdsa.Q = curve(*ECDSA.decode_point(x_y_bytes)) return ecdsa
def test_import_enc_gauntlet(self): supported_algos = RFC1423_ALGOS.keys() for algo in supported_algos: for _ in range(10): ecdsa = ECDSA(G=P256.G, hash_obj=None) key = Bytes.random(Bytes.random(1).int() + 1) enc_pem = ecdsa.export_private_key(encryption=algo, passphrase=key) dec_ecdsa = ECDSA.import_key(enc_pem, key) self.assertEqual((ecdsa.G, ecdsa.d, ecdsa.Q), (dec_ecdsa.G, dec_ecdsa.d, dec_ecdsa.Q))
def test_import_jwk(self): ec = ECDSA.import_key(TEST_JWK) jwk = ec.export_public_key(encoding=PKIEncoding.JWK) self.assertEqual(jwk, TEST_JWK) ec = ECDSA.import_key(TEST_JWK_PRIV) jwk = ec.export_private_key(encoding=PKIEncoding.JWK) as_dict = json.loads(TEST_JWK_PRIV.decode()) del as_dict['use'] del as_dict['alg'] self.assertEqual(json.loads(jwk.decode()), as_dict)
def decode(buffer: bytes, **kwargs): from samson.public_key.ecdsa import ECDSA items = bytes_to_der_sequence(buffer) # Move up OID for convenience items[0] = items[0][1] d = 1 x, y, curve = parse_ec_params(items, 0, 1) ecdsa = ECDSA(G=curve.G, hash_obj=None, d=d) ecdsa.Q = curve(x, y) return ecdsa
def test_openssh_gauntlet(self): num_runs = 6 num_enc = num_runs // 3 curves = [P192, P224, P256, P384, P521] for i in range(num_runs): curve = random.choice(curves) ecdsa = ECDSA(curve.G) passphrase = None if i < num_enc: passphrase = Bytes.random(Bytes.random(1).int()) priv = ecdsa.export_private_key(encoding=PKIEncoding.OpenSSH, encryption=b'aes256-ctr', passphrase=passphrase) pub_openssh = ecdsa.export_public_key(encoding=PKIEncoding.OpenSSH) pub_ssh2 = ecdsa.export_public_key(encoding=PKIEncoding.SSH2) new_priv = ECDSA.import_key(priv, passphrase=passphrase) new_pub_openssh = ECDSA.import_key(pub_openssh) new_pub_ssh2 = ECDSA.import_key(pub_ssh2) self.assertEqual((new_priv.d, new_priv.G, new_priv.Q), (ecdsa.d, ecdsa.G, ecdsa.Q)) self.assertEqual((new_pub_openssh.G, new_pub_openssh.Q), (ecdsa.G, ecdsa.Q)) self.assertEqual((new_pub_ssh2.G, new_pub_ssh2.Q), (ecdsa.G, ecdsa.Q))
def test_k_derivation(self): ecdsa = ECDSA(P256.G) k = Bytes.random(32).int() msgA = b'my first message' msgB = b'uh oh, two messages?!' sigA = ecdsa.sign(msgA, k) sigB = ecdsa.sign(msgB, k) found_k = ecdsa.derive_k_from_sigs(msgA, sigA, msgB, sigB) self.assertEqual(found_k, k) d = ecdsa.d self.assertEqual(ecdsa.derive_x_from_k(msgA, found_k, sigA), d)
def decode(buffer: bytes, **kwargs): from samson.public_key.ecdsa import ECDSA items = bytes_to_der_sequence(buffer) curve_oid = items[1][1].asTuple() params, _ = decoder.decode(bytes(items[2])) d = Bytes(params[1]).int() x, y = ECDSA.decode_point(Bytes(int(params[2]))) oid_bytes = ber_encoder.encode(ObjectIdentifier(curve_oid))[2:] curve = WS_OID_LOOKUP[oid_bytes] ecdsa = ECDSA(d=d, G=curve.G) ecdsa.Q = curve(x, y) return ecdsa
def test_import_x509(self): ec = ECDSA.import_key(TEST_X509) ec_bytes = ec.export_public_key(encoding=PKIEncoding.X509) self.assertEqual((ec.Q.x, ec.Q.y), ( 715947441162623524308031264370421599762967653523544747480787993496487140462283488974903669322082866021662891001767126467535751404779526256673589715857924084, 6284315030597594553103397980681739738230677011801289227519057103940802676199779900446162742685830902816710685363967012731548834638923262185574277733031408959 )) self.assertEqual(ec_bytes.replace(b'\n', b''), TEST_X509.replace(b'\n', b''))
def test_import_ssh(self): ecdsa_pub = ECDSA.import_key(TEST_SSH_PUB) ecdsa_ssh2_pub = ECDSA.import_key(TEST_SSH2_PUB) ecdsa_priv = ECDSA.import_key(TEST_SSH_PRIV) self.assertEqual((ecdsa_pub.G, ecdsa_pub.Q), (ecdsa_priv.G, ecdsa_priv.Q)) self.assertEqual((ecdsa_ssh2_pub.G, ecdsa_ssh2_pub.Q), (ecdsa_priv.G, ecdsa_priv.Q)) self.assertEqual(ecdsa_priv.d * ecdsa_priv.G, ecdsa_priv.Q) self.assertEqual( ecdsa_pub.export_public_key(encoding=PKIEncoding.OpenSSH).replace( b'\n', b''), TEST_SSH_PUB.replace(b'\n', b'')) self.assertEqual( ecdsa_ssh2_pub.export_public_key( encoding=PKIEncoding.SSH2).replace(b'\n', b''), TEST_SSH2_PUB_NO_CMT.replace(b'\n', b''))
def parse_ec_params(items, curve_idx, pub_point_idx): from samson.public_key.ecdsa import ECDSA curve_oid = items[curve_idx].asTuple() oid_bytes = ber_encoder.encode(ObjectIdentifier(curve_oid))[2:] curve = WS_OID_LOOKUP[oid_bytes] x_y_bytes = Bytes(int(items[pub_point_idx])) x, y = ECDSA.decode_point(x_y_bytes) return x, y, curve
def test_import_x509_cert(self): from subprocess import check_call ec = ECDSA.import_key(TEST_X509_CERT) self.assertEqual((ec.Q.x, ec.Q.y), ( 715947441162623524308031264370421599762967653523544747480787993496487140462283488974903669322082866021662891001767126467535751404779526256673589715857924084, 6284315030597594553103397980681739738230677011801289227519057103940802676199779900446162742685830902816710685363967012731548834638923262185574277733031408959 )) cert = ec.export_public_key(encoding=PKIEncoding.X509_CERT).decode() check_call([f'echo -n \"{cert}\" | openssl x509 -text'], shell=True)
def test_import_pkcs8(self): ec = ECDSA.import_key(TEST_PKCS8) ec_bytes = ec.export_private_key(encoding=PKIEncoding.PKCS8) self.assertEqual( ec.d, 2282649980877248464928985540593193992740494509534471044083643023670157012821680477618689736007052097550343217684448593053345246736083446705198105618319263331 ) self.assertEqual((ec.Q.x, ec.Q.y), ( 715947441162623524308031264370421599762967653523544747480787993496487140462283488974903669322082866021662891001767126467535751404779526256673589715857924084, 6284315030597594553103397980681739738230677011801289227519057103940802676199779900446162742685830902816710685363967012731548834638923262185574277733031408959 )) self.assertEqual(ec_bytes.replace(b'\n', b''), TEST_PKCS8.replace(b'\n', b''))
def test_gauntlet(self): for jwa in [JWASignatureAlg.HS256, JWASignatureAlg.HS384, JWASignatureAlg.HS512]: for _ in range(50): key = Bytes.random(16) jws = JWS.create(jwa, BODY, key) self.assertTrue(jws.verify(key)) for jwa, curve, hash_obj in [(JWASignatureAlg.ES256, P256, SHA256()), (JWASignatureAlg.ES384, P384, SHA384()), (JWASignatureAlg.ES512, P521, SHA512())]: for _ in range(10): key = ECDSA(G=curve.G, hash_obj=hash_obj) jws = JWS.create(jwa, BODY, key) self.assertTrue(jws.verify(key)) for jwa in [JWASignatureAlg.RS256, JWASignatureAlg.RS384, JWASignatureAlg.RS512, JWASignatureAlg.PS256, JWASignatureAlg.PS384, JWASignatureAlg.PS512]: for _ in range(10): key = RSA(2048) jws = JWS.create(jwa, BODY, key) correct = jws.verify(key) if not correct: print(key) print(jws) self.assertTrue(correct) for i in range(10): if i % 2: curve = EdwardsCurve25519 else: curve = EdwardsCurve448 key = EdDSA(curve=curve) jws = JWS.create(JWASignatureAlg.EdDSA, BODY, key) correct = jws.verify(key) if not correct: print(key) print(jws) self.assertTrue(correct)
def test_jwk_gauntlet(self): curves = [P192, P224, P256, P384, P521] for _ in range(100): curve = random.choice(curves) ecdsa = ECDSA(curve.G) priv = ecdsa.export_private_key(encoding=PKIEncoding.JWK) pub = ecdsa.export_public_key(encoding=PKIEncoding.JWK) new_priv = ECDSA.import_key(priv) new_pub = ECDSA.import_key(pub) self.assertEqual((new_priv.d, new_priv.G, new_priv.Q), (ecdsa.d, ecdsa.G, ecdsa.Q)) self.assertEqual((new_pub.G, new_pub.Q), (ecdsa.G, ecdsa.Q))
kB/cZjfDEvoQKBgQDgjoTWAoMHDwxhcnQ0xktfDtAv7WBGIydoovCtFax8YdiEVdcEUJfX JlMwGwCdw6U71/u8wT+VAvqs9XsOgFVI9mcTfX+9l3RoJWZltMrmsMCVNxxbbDz+sFBQhF YDsNEuhGxwUUjciw9LqCsjiG/vsNqTvf7liFbsNMbZghAbbQKBgDzw+2Seo9odwXaMA3fB ZIP+RSKngdpvc5VmhsiwJ5WMUg963PEWXEfXq24rK7DvYeTKZDLVdNa/xhQTKQXm5z+oof YJD9LDBaruRgPp/4tZaYwUrX1IQWRNSItu417FPUIZepUEht6NLOlhGb8u15T4jExjBlgx GKKQQ6RcM/pBAoGAYH9+QAVWTzs9Q8cOfvtTumbAOkhU3e8PaVzT9l1hARZ/F+dXfggwKA nVJ9ACxMklgYEAMg4Nh7h/BsJ6/jFR9QfGJc8BjPS/1l10EnLN2rLMH5NOQU9TKtOTv/YO jIl4avgHLYEQwY2Upht/Zkaka6lhVKoKFpMvX1QSu7ezukUCgYEA4evL4ELaxSYTSVX+Vz Q2z8l7riPWLFEqPvMHsgW5wKVkXHnpRx+PoeZQK/+AIdEOMh0zeXEecLg7uQ1T4qmWPDxB uDbtNosYyMryoNAwfQfTcMcc9QvnLXNMqLC7vm/hPlzwvogWyyWeYbOvsjPGSzfaw+DfbU pVYpArfNuzoBM= -----END PRIVATE KEY-----""") ES1_KEY = ECDSA.import_key(b"""-----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgpR4yjoqJ09GAriF++Pxu+I O1FxF9uAGsniq77Lc6woehRANCAAQbdf1V9k89vTxPbWlzYoiJnk+RZpufb5AX7D4mRJN+ o0NjMxFrNFUyiq3Y7+wa9k06Lg7KL06HN+kaax2/Fp3M -----END PRIVATE KEY-----""") ES1_KEY.hash_obj = SHA256() ES2_KEY = ECDSA.import_key(b"""-----BEGIN PRIVATE KEY----- MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDDxg/XHeJj3sTTsO8Jnczsxzc jLfwmbJlYMDg2SupAvsrck9iNktrlRlKDX3prWaquhZANiAASzOq9L0SGAfmP1NUxMKunV mxF707SBdr17rYhes0Q+SpnQ7GWliRcGivg501bxcKxri6EIqPlTSstDmtgCPE7rowKVMt jHB2itCKnpa4Zw6373AEe8xrxLrYvSlg1uPmw= -----END PRIVATE KEY-----""") ES2_KEY.hash_obj = SHA384()
def test_cross_alg(self): for ca in [DSA(), RSA(2048), ECDSA(G=P256.G)]: for leaf in [DSA(), RSA(2048), ECDSA(G=P256.G)]: self._run_test(ca, leaf)