1%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*-
2%% vim: ts=4 sw=4 ft=erlang noet
3-module(jose_jwk_kty_okp_x25519_props).
4
5-include("jose_public_key.hrl").
6
7-include_lib("proper/include/proper.hrl").
8
9% -compile(export_all).
10
11base64url_binary() ->
12	?LET(Binary,
13		binary(),
14		jose_jwa_base64url:encode(Binary)).
15
16binary_map() ->
17	?LET(List,
18		list({base64url_binary(), base64url_binary()}),
19		maps:from_list(List)).
20
21x25519_secret() ->
22	binary(32).
23
24x25519_keypair(Secret) ->
25	{PK, S} = jose_curve25519:x25519_keypair(Secret),
26	PublicKey = #'jose_X25519PublicKey'{publicKey=PK},
27	SecretKey = #'jose_X25519PrivateKey'{publicKey=PublicKey, privateKey=S},
28	{SecretKey, PublicKey}.
29
30jwk_map() ->
31	?LET({AliceSecret, BobSecret},
32		{x25519_secret(), x25519_secret()},
33		begin
34			AliceKeys = {AlicePrivateKey, _} = x25519_keypair(AliceSecret),
35			BobKeys = x25519_keypair(BobSecret),
36			AlicePrivateJWK = jose_jwk:from_key(AlicePrivateKey),
37			{_, AlicePrivateJWKMap} = jose_jwk:to_map(AlicePrivateJWK),
38			Keys = {AliceKeys, BobKeys},
39			{Keys, AlicePrivateJWKMap}
40		end).
41
42jwk_gen() ->
43	?LET({Keys, AlicePrivateJWKMap},
44		jwk_map(),
45		{Keys, jose_jwk:from_map(AlicePrivateJWKMap)}).
46
47prop_from_der_and_to_der() ->
48	?FORALL({_Keys, AlicePrivateJWK, Password},
49		?LET({{Keys, AlicePrivateJWK}, Bytes},
50			{jwk_gen(), binary()},
51			{Keys, AlicePrivateJWK, jose_jwa_base64url:encode(Bytes)}),
52		begin
53			AlicePrivateDER = element(2, jose_jwk:to_der(AlicePrivateJWK)),
54			EncryptedAlicePrivateDER = element(2, jose_jwk:to_der(Password, AlicePrivateJWK)),
55			AlicePublicJWK = jose_jwk:to_public(AlicePrivateJWK),
56			AlicePublicDER = element(2, jose_jwk:to_der(AlicePublicJWK)),
57			AlicePrivateJWK =:= jose_jwk:from_der(AlicePrivateDER)
58			andalso AlicePrivateJWK =:= jose_jwk:from_der(Password, EncryptedAlicePrivateDER)
59			andalso AlicePublicJWK =:= jose_jwk:from_der(AlicePublicDER)
60		end).
61
62prop_from_map_and_to_map() ->
63	?FORALL({{{AlicePrivateKey, AlicePublicKey}, _}, AlicePrivateJWKMap},
64		?LET({{Keys, JWKMap}, Extras},
65			{jwk_map(), binary_map()},
66			{Keys, maps:merge(Extras, JWKMap)}),
67		begin
68			AlicePrivateJWK = jose_jwk:from_map(AlicePrivateJWKMap),
69			AlicePublicJWK = jose_jwk:to_public(AlicePrivateJWK),
70			AlicePublicJWKMap = element(2, jose_jwk:to_map(AlicePublicJWK)),
71			AlicePublicThumbprint = jose_jwk:thumbprint(AlicePublicJWK),
72			AlicePrivateJWKMap =:= element(2, jose_jwk:to_map(AlicePrivateJWK))
73			andalso AlicePrivateKey =:= element(2, jose_jwk:to_key(AlicePrivateJWK))
74			andalso AlicePublicKey =:= element(2, jose_jwk:to_public_key(AlicePrivateJWK))
75			andalso AlicePublicJWKMap =:= element(2, jose_jwk:to_public_map(AlicePrivateJWK))
76			andalso AlicePublicThumbprint =:= jose_jwk:thumbprint(AlicePrivateJWK)
77		end).
78
79prop_from_pem_and_to_pem() ->
80	?FORALL({_Keys, AlicePrivateJWK, Password},
81		?LET({{Keys, AlicePrivateJWK}, Bytes},
82			{jwk_gen(), binary()},
83			{Keys, AlicePrivateJWK, jose_jwa_base64url:encode(Bytes)}),
84		begin
85			AlicePrivatePEM = element(2, jose_jwk:to_pem(AlicePrivateJWK)),
86			EncryptedAlicePrivatePEM = element(2, jose_jwk:to_pem(Password, AlicePrivateJWK)),
87			AlicePublicJWK = jose_jwk:to_public(AlicePrivateJWK),
88			AlicePublicPEM = element(2, jose_jwk:to_pem(AlicePublicJWK)),
89			EncryptedAlicePublicPEM = element(2, jose_jwk:to_pem(Password, AlicePublicJWK)),
90			AlicePrivateJWK =:= jose_jwk:from_pem(AlicePrivatePEM)
91			andalso AlicePrivateJWK =:= jose_jwk:from_pem(Password, EncryptedAlicePrivatePEM)
92			andalso AlicePublicJWK =:= jose_jwk:from_pem(AlicePublicPEM)
93			andalso AlicePublicJWK =:= jose_jwk:from_pem(Password, EncryptedAlicePublicPEM)
94		end).
95
96prop_box_encrypt_and_box_decrypt() ->
97	?FORALL({{{_, {BobPrivateKey, BobPublicKey}}, AlicePrivateJWK}, PlainText},
98		{jwk_gen(), binary()},
99		begin
100			BobPrivateJWK = jose_jwk:from_key(BobPrivateKey),
101			BobPublicJWK = jose_jwk:from_key(BobPublicKey),
102			Encrypted = jose_jwk:box_encrypt_ecdh_es(PlainText, BobPublicJWK, AlicePrivateJWK),
103			CompactEncrypted = jose_jwe:compact(Encrypted),
104			Decrypted = {_, JWE} = jose_jwk:box_decrypt_ecdh_es(Encrypted, BobPrivateJWK),
105			{PlainText, JWE} =:= Decrypted
106			andalso {PlainText, JWE} =:= jose_jwk:block_decrypt(CompactEncrypted, BobPrivateJWK)
107		end).
108