Example #1
0
def test_ecdh_es_direct():
    bob_key = Key.generate(KeyAlg.P256)
    bob_jwk = bob_key.get_jwk_public()
    ephem_key = Key.generate(KeyAlg.P256)
    ephem_jwk = ephem_key.get_jwk_public()
    message = b"Hello there"
    alg = "ECDH-ES"
    enc = "A256GCM"
    apu = "Alice"
    apv = "Bob"
    protected_b64 = b64_url(f'{{"alg":"{alg}",'
                            f'"enc":"{enc}",'
                            f'"apu":"{b64_url(apu)}",'
                            f'"apv":"{b64_url(apv)}",'
                            f'"epk":{ephem_jwk}}}').encode("ascii")
    encrypted_msg = EcdhEs(enc, apu, apv).encrypt_direct(KeyAlg.A256GCM,
                                                         ephem_key,
                                                         bob_jwk,
                                                         message,
                                                         aad=protected_b64)
    ciphertext, tag, nonce = encrypted_msg.parts

    # switch to receiver

    message_recv = EcdhEs(enc, apu, apv).decrypt_direct(
        KeyAlg.A256GCM,
        ephem_jwk,
        bob_key,
        ciphertext,
        nonce=nonce,
        tag=tag,
        aad=protected_b64,
    )
    assert message_recv == message
Example #2
0
def test_ed25519():
    key = Key.generate(KeyAlg.ED25519)
    assert key.algorithm == KeyAlg.ED25519
    message = b"test message"
    sig = key.sign_message(message)
    assert key.verify_signature(message, sig)
    x25519_key = key.convert_key(KeyAlg.X25519)

    x25519_key_2 = Key.generate(KeyAlg.X25519)
    _kex = x25519_key.key_exchange(KeyAlg.XC20P, x25519_key_2)

    key.get_jwk_public()
Example #3
0
    async def test_1pu_decrypt_x(self):
        alg = KeyAlg.X25519
        alice_sk = Key.generate(alg)
        alice_pk = Key.from_jwk(alice_sk.get_jwk_public())
        bob_sk = Key.generate(alg)

        message_unknown_alg = JweEnvelope(
            protected={"alg": "NOT-SUPPORTED"},
        )
        message_unknown_alg.add_recipient(
            JweRecipient(encrypted_key=b"0000", header={"kid": BOB_KID})
        )
        with pytest.raises(
            test_module.DidcommEnvelopeError,
            match="Unsupported ECDH-1PU algorithm",
        ):
            _ = test_module.ecdh_1pu_decrypt(
                message_unknown_alg,
                BOB_KID,
                bob_sk,
                alice_pk,
            )

        message_unknown_enc = JweEnvelope(
            protected={"alg": "ECDH-1PU+A128KW", "enc": "UNKNOWN"},
        )
        message_unknown_enc.add_recipient(
            JweRecipient(encrypted_key=b"0000", header={"kid": BOB_KID})
        )
        with pytest.raises(
            test_module.DidcommEnvelopeError,
            match="Unsupported ECDH-1PU content encryption",
        ):
            _ = test_module.ecdh_1pu_decrypt(
                message_unknown_enc, BOB_KID, bob_sk, alice_pk
            )

        message_invalid_epk = JweEnvelope(
            protected={"alg": "ECDH-1PU+A128KW", "enc": "A256CBC-HS512", "epk": {}},
        )
        message_invalid_epk.add_recipient(
            JweRecipient(encrypted_key=b"0000", header={"kid": BOB_KID})
        )
        with pytest.raises(
            test_module.DidcommEnvelopeError,
            match="Error loading ephemeral key",
        ):
            _ = test_module.ecdh_1pu_decrypt(
                message_invalid_epk,
                BOB_KID,
                bob_sk,
                alice_pk,
            )
Example #4
0
def _create_keypair(key_type: KeyType, seed: Union[str, bytes] = None) -> Key:
    """Instantiate a new keypair with an optional seed value."""
    if key_type == KeyType.ED25519:
        alg = KeyAlg.ED25519
        method = None
    # elif key_type == KeyType.BLS12381G1:
    #     alg = KeyAlg.BLS12_381_G1
    elif key_type == KeyType.BLS12381G2:
        alg = KeyAlg.BLS12_381_G2
        method = SeedMethod.BlsKeyGen
    # elif key_type == KeyType.BLS12381G1G2:
    #     alg = KeyAlg.BLS12_381_G1G2
    else:
        raise WalletError(f"Unsupported key algorithm: {key_type}")
    if seed:
        try:
            if key_type == KeyType.ED25519:
                # not a seed - it is the secret key
                seed = validate_seed(seed)
                return Key.from_secret_bytes(alg, seed)
            else:
                return Key.from_seed(alg, seed, method=method)
        except AskarError as err:
            if err.code == AskarErrorCode.INPUT:
                raise WalletError("Invalid seed for key generation") from None
    else:
        return Key.generate(alg)
Example #5
0
    async def test_es_encrypt_x(self, session: Session):
        alg = KeyAlg.X25519
        bob_sk = Key.generate(alg)
        bob_pk = Key.from_jwk(bob_sk.get_jwk_public())

        with pytest.raises(
            test_module.DidcommEnvelopeError, match="No message recipients"
        ):
            _ = test_module.ecdh_es_encrypt({}, MESSAGE)

        with async_mock.patch(
            "aries_askar.Key.generate",
            async_mock.MagicMock(side_effect=AskarError(99, "")),
        ):
            with pytest.raises(
                test_module.DidcommEnvelopeError,
                match="Error creating content encryption key",
            ):
                _ = test_module.ecdh_es_encrypt({BOB_KID: bob_pk}, MESSAGE)

        with async_mock.patch(
            "aries_askar.Key.aead_encrypt",
            async_mock.MagicMock(side_effect=AskarError(99, "")),
        ):
            with pytest.raises(
                test_module.DidcommEnvelopeError,
                match="Error encrypting",
            ):
                _ = test_module.ecdh_es_encrypt({BOB_KID: bob_pk}, MESSAGE)
def test_crypto_box_seal():
    x25519_key = Key.generate(KeyAlg.X25519)

    msg = b"test message"
    sealed = crypto_box_seal(x25519_key, msg)
    opened = crypto_box_seal_open(x25519_key, sealed)
    assert msg == opened
def ecdh_es_encrypt(to_verkeys: Mapping[str, Key], message: bytes) -> bytes:
    """Encode a message using DIDComm v2 anonymous encryption."""
    wrapper = JweEnvelope(with_flatten_recipients=False)

    alg_id = "ECDH-ES+A256KW"
    enc_id = "XC20P"
    enc_alg = KeyAlg.XC20P
    wrap_alg = KeyAlg.A256KW

    if not to_verkeys:
        raise DidcommEnvelopeError("No message recipients")

    try:
        cek = Key.generate(enc_alg)
    except AskarError:
        raise DidcommEnvelopeError("Error creating content encryption key")

    for (kid, recip_key) in to_verkeys.items():
        try:
            epk = Key.generate(recip_key.algorithm, ephemeral=True)
        except AskarError:
            raise DidcommEnvelopeError("Error creating ephemeral key")
        enc_key = ecdh.EcdhEs(alg_id, None,
                              None).sender_wrap_key(wrap_alg, epk, recip_key,
                                                    cek)
        wrapper.add_recipient(
            JweRecipient(
                encrypted_key=enc_key.ciphertext,
                header={
                    "kid": kid,
                    "epk": epk.get_jwk_public()
                },
            ))

    wrapper.set_protected(OrderedDict([
        ("alg", alg_id),
        ("enc", enc_id),
    ]))
    try:
        payload = cek.aead_encrypt(message, aad=wrapper.protected_bytes)
    except AskarError:
        raise DidcommEnvelopeError("Error encrypting message payload")
    wrapper.set_payload(payload.ciphertext, payload.nonce, payload.tag)

    return wrapper.to_json().encode("utf-8")
Example #8
0
    async def test_es_round_trip(self, session: Session):
        alg = KeyAlg.X25519
        bob_sk = Key.generate(alg)
        bob_pk = Key.from_jwk(bob_sk.get_jwk_public())
        carol_sk = Key.generate(KeyAlg.P256)  # testing mixed recipient key types
        carol_pk = Key.from_jwk(carol_sk.get_jwk_public())

        enc_message = test_module.ecdh_es_encrypt(
            {BOB_KID: bob_pk, CAROL_KID: carol_pk}, MESSAGE
        )

        # receiver must have the private keypair accessible
        await session.insert_key("my_sk", bob_sk, tags={"kid": BOB_KID})

        plaintext, recip_kid, sender_kid = await test_module.unpack_message(
            session, enc_message
        )
        assert recip_kid == BOB_KID
        assert sender_kid is None
        assert plaintext == MESSAGE
Example #9
0
def test_aes_cbc_hmac():
    key = Key.generate(KeyAlg.A128CBC_HS256)
    assert key.algorithm == KeyAlg.A128CBC_HS256

    data = b"test message"
    nonce = key.aead_random_nonce()
    params = key.aead_params()
    assert params.nonce_length == 16
    assert params.tag_length == 16
    enc = key.aead_encrypt(data, nonce=nonce, aad=b"aad")
    dec = key.aead_decrypt(enc, nonce=nonce, aad=b"aad")
    assert data == bytes(dec)
Example #10
0
    async def test_1pu_round_trip(self, session: Session):
        alg = KeyAlg.X25519
        alice_sk = Key.generate(alg)
        alice_pk = Key.from_jwk(alice_sk.get_jwk_public())
        bob_sk = Key.generate(alg)
        bob_pk = Key.from_jwk(bob_sk.get_jwk_public())

        enc_message = test_module.ecdh_1pu_encrypt(
            {BOB_KID: bob_pk}, ALICE_KID, alice_sk, MESSAGE
        )

        # receiver must have the private keypair accessible
        await session.insert_key("my_sk", bob_sk, tags={"kid": BOB_KID})
        # for now at least, insert the sender public key so it can be resolved
        await session.insert_key("alice_pk", alice_pk, tags={"kid": ALICE_KID})

        plaintext, recip_kid, sender_kid = await test_module.unpack_message(
            session, enc_message
        )
        assert recip_kid == BOB_KID
        assert sender_kid == ALICE_KID
        assert plaintext == MESSAGE
Example #11
0
def test_ecdh_es_wrapped():
    bob_key = Key.generate(KeyAlg.X25519)
    bob_jwk = bob_key.get_jwk_public()
    ephem_key = Key.generate(KeyAlg.X25519)
    ephem_jwk = ephem_key.get_jwk_public()
    message = b"Hello there"
    alg = "ECDH-ES+A128KW"
    enc = "A256GCM"
    apu = "Alice"
    apv = "Bob"
    protected_b64 = b64_url(f'{{"alg":"{alg}",'
                            f'"enc":"{enc}",'
                            f'"apu":"{b64_url(apu)}",'
                            f'"apv":"{b64_url(apv)}",'
                            f'"epk":{ephem_jwk}}}').encode("ascii")
    cek = Key.generate(KeyAlg.A256GCM)
    encrypted_msg = cek.aead_encrypt(message, aad=protected_b64)
    ciphertext, tag, nonce = encrypted_msg.parts
    encrypted_key = EcdhEs(alg, apu,
                           apv).sender_wrap_key(KeyAlg.A128KW, ephem_key,
                                                bob_jwk, cek)
    encrypted_key = encrypted_key.ciphertext

    # switch to receiver

    cek_recv = EcdhEs(alg, apu, apv).receiver_unwrap_key(
        KeyAlg.A128KW,
        KeyAlg.A256GCM,
        ephem_jwk,
        bob_key,
        encrypted_key,
    )
    message_recv = cek_recv.aead_decrypt(ciphertext,
                                         nonce=nonce,
                                         tag=tag,
                                         aad=protected_b64)
    assert message_recv == message
Example #12
0
def _pack_message(to_verkeys: Sequence[str], from_key: Optional[Key],
                  message: bytes) -> bytes:
    """Encode a message using the DIDComm v1 'pack' algorithm."""
    wrapper = JweEnvelope()
    cek = Key.generate(KeyAlg.C20P)
    # avoid converting to bytes object: this way the only copy is zeroed afterward
    cek_b = key_get_secret_bytes(cek._handle)
    sender_vk = (bytes_to_b58(from_key.get_public_bytes()).encode("utf-8")
                 if from_key else None)
    sender_xk = from_key.convert_key(KeyAlg.X25519) if from_key else None

    for target_vk in to_verkeys:
        target_xk = Key.from_public_bytes(KeyAlg.ED25519,
                                          b58_to_bytes(target_vk)).convert_key(
                                              KeyAlg.X25519)
        if sender_vk:
            enc_sender = crypto_box.crypto_box_seal(target_xk, sender_vk)
            nonce = crypto_box.random_nonce()
            enc_cek = crypto_box.crypto_box(target_xk, sender_xk, cek_b, nonce)
            wrapper.add_recipient(
                JweRecipient(
                    encrypted_key=enc_cek,
                    header=OrderedDict([
                        ("kid", target_vk),
                        ("sender", b64url(enc_sender)),
                        ("iv", b64url(nonce)),
                    ]),
                ))
        else:
            enc_sender = None
            nonce = None
            enc_cek = crypto_box.crypto_box_seal(target_xk, cek_b)
            wrapper.add_recipient(
                JweRecipient(encrypted_key=enc_cek, header={"kid": target_vk}))
    wrapper.set_protected(
        OrderedDict([
            ("enc", "xchacha20poly1305_ietf"),
            ("typ", "JWM/1.0"),
            ("alg", "Authcrypt" if from_key else "Anoncrypt"),
        ]),
        auto_flatten=False,
    )
    enc = cek.aead_encrypt(message, aad=wrapper.protected_bytes)
    ciphertext, tag, nonce = enc.parts
    wrapper.set_payload(ciphertext, nonce, tag)
    return wrapper.to_json().encode("utf-8")
Example #13
0
async def test_key_store(store: Store):
    # test key operations in a new session
    async with store as session:
        # Create a new keypair
        keypair = Key.generate(KeyAlg.ED25519)

        # Store keypair
        key_name = "testkey"
        await session.insert_key(key_name,
                                 keypair,
                                 metadata="metadata",
                                 tags={"a": "b"})

        # Fetch keypair
        fetch_key = await session.fetch_key(key_name)
        assert fetch_key and fetch_key.name == key_name and fetch_key.tags == {
            "a": "b"
        }

        # Update keypair
        await session.update_key(key_name,
                                 metadata="updated metadata",
                                 tags={"a": "c"})

        # Fetch keypair
        fetch_key = await session.fetch_key(key_name)
        assert fetch_key and fetch_key.name == key_name and fetch_key.tags == {
            "a": "c"
        }

        # Check key equality
        thumbprint = keypair.get_jwk_thumbprint()
        assert fetch_key.key.get_jwk_thumbprint() == thumbprint

        # Fetch with filters
        keys = await session.fetch_all_keys(alg=KeyAlg.ED25519,
                                            thumbprint=thumbprint,
                                            tag_filter={"a": "c"},
                                            limit=1)
        assert len(keys) == 1 and keys[0].name == key_name

        # Remove
        await session.remove_key(key_name)
        assert await session.fetch_key(key_name) is None
def ecdh_1pu_encrypt(to_verkeys: Mapping[str, Key], sender_kid: str,
                     sender_key: Key, message: bytes) -> bytes:
    """Encode a message using DIDComm v2 authenticated encryption."""
    wrapper = JweEnvelope(with_flatten_recipients=False)

    alg_id = "ECDH-1PU+A256KW"
    enc_id = "A256CBC-HS512"
    enc_alg = KeyAlg.A256CBC_HS512
    wrap_alg = KeyAlg.A256KW
    agree_alg = sender_key.algorithm

    if not to_verkeys:
        raise DidcommEnvelopeError("No message recipients")

    try:
        cek = Key.generate(enc_alg)
    except AskarError:
        raise DidcommEnvelopeError("Error creating content encryption key")

    try:
        epk = Key.generate(agree_alg, ephemeral=True)
    except AskarError:
        raise DidcommEnvelopeError("Error creating ephemeral key")

    apu = b64url(sender_kid)
    apv = []
    for (kid, recip_key) in to_verkeys.items():
        if agree_alg:
            if agree_alg != recip_key.algorithm:
                raise DidcommEnvelopeError(
                    "Recipient key types must be consistent")
        else:
            agree_alg = recip_key.algorithm
        apv.append(kid)
    apv.sort()
    apv = b64url(".".join(apv))

    wrapper.set_protected(
        OrderedDict([
            ("alg", alg_id),
            ("enc", enc_id),
            ("apu", apu),
            ("apv", apv),
            ("epk", json.loads(epk.get_jwk_public())),
            ("skid", sender_kid),
        ]))
    try:
        payload = cek.aead_encrypt(message, aad=wrapper.protected_bytes)
    except AskarError:
        raise DidcommEnvelopeError("Error encrypting message payload")
    wrapper.set_payload(payload.ciphertext, payload.nonce, payload.tag)

    for (kid, recip_key) in to_verkeys.items():
        enc_key = ecdh.Ecdh1PU(alg_id, apu,
                               apv).sender_wrap_key(wrap_alg,
                                                    epk,
                                                    sender_key,
                                                    recip_key,
                                                    cek,
                                                    cc_tag=payload.tag)
        wrapper.add_recipient(
            JweRecipient(encrypted_key=enc_key.ciphertext, header={"kid":
                                                                   kid}))

    return wrapper.to_json().encode("utf-8")
Example #15
0
    async def test_unpack_message_1pu_x(self, session: Session):
        alg = KeyAlg.X25519
        alice_sk = Key.generate(alg)
        alice_pk = Key.from_jwk(alice_sk.get_jwk_public())
        bob_sk = Key.generate(alg)
        bob_pk = Key.from_jwk(bob_sk.get_jwk_public())

        # receiver must have the private keypair accessible
        await session.insert_key("my_sk", bob_sk, tags={"kid": BOB_KID})
        # for now at least, insert the sender public key so it can be resolved
        await session.insert_key("alice_pk", alice_pk, tags={"kid": ALICE_KID})

        message_1pu_no_skid = json.dumps(
            {
                "protected": b64url(json.dumps({"alg": "ECDH-1PU+A128KW"})),
                "recipients": [{"header": {"kid": BOB_KID}, "encrypted_key": "MTIzNA"}],
                "iv": "MTIzNA",
                "ciphertext": "MTIzNA",
                "tag": "MTIzNA",
            }
        )

        with pytest.raises(
            test_module.DidcommEnvelopeError,
            match="Sender key ID not provided",
        ):
            _ = await test_module.unpack_message(session, message_1pu_no_skid)

        message_1pu_unknown_skid = json.dumps(
            {
                "protected": b64url(
                    json.dumps({"alg": "ECDH-1PU+A128KW", "skid": "UNKNOWN"})
                ),
                "recipients": [{"header": {"kid": BOB_KID}, "encrypted_key": "MTIzNA"}],
                "iv": "MTIzNA",
                "ciphertext": "MTIzNA",
                "tag": "MTIzNA",
            }
        )

        with pytest.raises(
            test_module.DidcommEnvelopeError,
            match="Sender public key not found",
        ):
            _ = await test_module.unpack_message(session, message_1pu_unknown_skid)

        message_1pu_apu_invalid = json.dumps(
            {
                "protected": b64url(
                    json.dumps({"alg": "ECDH-1PU+A128KW", "skid": "A", "apu": "A"})
                ),
                "recipients": [{"header": {"kid": BOB_KID}, "encrypted_key": "MTIzNA"}],
                "iv": "MTIzNA",
                "ciphertext": "MTIzNA",
                "tag": "MTIzNA",
            }
        )

        with pytest.raises(
            test_module.DidcommEnvelopeError,
            match="Invalid apu value",
        ):
            _ = await test_module.unpack_message(session, message_1pu_apu_invalid)

        message_1pu_apu_mismatch = json.dumps(
            {
                "protected": b64url(
                    json.dumps(
                        {
                            "alg": "ECDH-1PU+A128KW",
                            "skid": ALICE_KID,
                            "apu": b64url("UNKNOWN"),
                        }
                    )
                ),
                "recipients": [{"header": {"kid": BOB_KID}, "encrypted_key": "MTIzNA"}],
                "iv": "MTIzNA",
                "ciphertext": "MTIzNA",
                "tag": "MTIzNA",
            }
        )

        with pytest.raises(
            test_module.DidcommEnvelopeError,
            match="Mismatch between skid and apu",
        ):
            _ = await test_module.unpack_message(session, message_1pu_apu_mismatch)