The specification requires tweaking to fail when P' is infinity.
Should the reference implementation and tweak vectors enforce this case as well?
<details>
<summary>diff</summary>
diff --git a/bip-0459/gen_test_vectors.py b/bip-0459/gen_test_vectors.py
index 2624b37..e18720b 100644
--- a/bip-0459/gen_test_vectors.py
+++ b/bip-0459/gen_test_vectors.py
@@ -416,7 +416,7 @@ def gen_tweak_vectors(f):
writer = csv.writer(f)
writer.writerow((
"index", "internal_secret_key", "tweak", "is_xonly",
- "expected_secret_key", "expected_pubkey", "comment",
+ "expected_result", "expected_secret_key", "expected_pubkey", "comment",
))
def row(idx, sk, t, is_xonly, comment):
@@ -426,10 +426,24 @@ def gen_tweak_vectors(f):
writer.writerow((
idx, sk.to_bytes().hex().upper(), t.to_bytes().hex().upper(),
"TRUE" if is_xonly else "FALSE",
+ "TRUE",
sk_out.to_bytes().hex().upper(), pk_out.to_bytes_compressed().hex().upper(),
comment,
))
+ def error_row(idx, sk, t, is_xonly, comment):
+ for tweak_fn, key in ((TweakSK, sk), (TweakPK, sk * G)):
+ try:
+ tweak_fn(key, t, is_xonly)
+ except ValueError:
+ continue
+ raise AssertionError(f"{tweak_fn.__name__} did not fail")
+ writer.writerow((
+ idx, sk.to_bytes().hex().upper(), t.to_bytes().hex().upper(),
+ "TRUE" if is_xonly else "FALSE",
+ "FALSE", "", "", comment,
+ ))
+
sk1 = scalar_from_byte(1)
even = sk1 if has_even_y(sk1 * G) else -sk1
odd = -even
@@ -438,6 +452,8 @@ def gen_tweak_vectors(f):
row(1, odd, t, False, "Plain tweak of an odd-y key")
row(2, even, t, True, "X-only tweak of an even-y key")
row(3, odd, t, True, "X-only tweak of an odd-y key")
+ error_row(4, odd, -odd, False, "Plain tweak resulting in the point at infinity")
+ error_row(5, odd, odd, True, "X-only tweak resulting in the point at infinity")
if __name__ == "__main__":
diff --git a/bip-0459/reference.py b/bip-0459/reference.py
index cd1cf59..bcf2e61 100644
--- a/bip-0459/reference.py
+++ b/bip-0459/reference.py
@@ -46,13 +46,17 @@ def xbytes(P: GE) -> bytes:
def TweakSK(sk: Scalar, t: Scalar, is_xonly: bool) -> Scalar:
d = sk if (not is_xonly or has_even_y(sk * G)) else -sk
- return d + t
+ tweaked_sk = d + t
+ if tweaked_sk == 0:
+ raise ValueError("tweaked secret key is zero")
+ return tweaked_sk
def TweakPK(pk: GE, t: Scalar, is_xonly: bool) -> GE:
P = pk if (not is_xonly or has_even_y(pk)) else -pk
Q = P + t * G
- assert not Q.infinity
+ if Q.infinity:
+ raise ValueError("tweaked public key is the point at infinity")
return Q
diff --git a/bip-0459/run_test_vectors.py b/bip-0459/run_test_vectors.py
index 551f9b1..d445d89 100644
--- a/bip-0459/run_test_vectors.py
+++ b/bip-0459/run_test_vectors.py
@@ -206,14 +206,29 @@ def run_tweak_tests() -> List[str]:
next(reader)
for row in reader:
- index, sk_str, t_str, is_xonly_str, exp_sk_str, exp_pk_str, comment = row
+ (index, sk_str, t_str, is_xonly_str, expected_result,
+ exp_sk_str, exp_pk_str, comment) = row
sk = Scalar.from_bytes_checked(bytes.fromhex(sk_str))
t = Scalar.from_bytes_checked(bytes.fromhex(t_str))
is_xonly = (is_xonly_str == "TRUE")
- sk_out = TweakSK(sk, t, is_xonly)
- pk_out = TweakPK(sk * G, t, is_xonly)
- if (sk_out.to_bytes().hex().upper() != exp_sk_str
- or pk_out.to_bytes_compressed().hex().upper() != exp_pk_str):
+
+ try:
+ sk_out = TweakSK(sk, t, is_xonly)
+ except ValueError:
+ sk_out = None
+ try:
+ pk_out = TweakPK(sk * G, t, is_xonly)
+ except ValueError:
+ pk_out = None
+
+ if expected_result == "TRUE":
+ matches = (sk_out is not None
+ and pk_out is not None
+ and sk_out.to_bytes().hex().upper() == exp_sk_str
+ and pk_out.to_bytes_compressed().hex().upper() == exp_pk_str)
+ else:
+ matches = sk_out is None and pk_out is None
+ if not matches:
failures.append(f"Tweak #{index}: {comment}")
return failures
diff --git a/bip-0459/vectors/test_vectors_tweak.csv b/bip-0459/vectors/test_vectors_tweak.csv
index fcaa22b..4e3e544 100644
--- a/bip-0459/vectors/test_vectors_tweak.csv
+++ b/bip-0459/vectors/test_vectors_tweak.csv
@@ -1,5 +1,7 @@
-index,internal_secret_key,tweak,is_xonly,expected_secret_key,expected_pubkey,comment
-0,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040,0202020202020202020202020202020202020202020202020202020202020202,FALSE,0101010101010101010101010101010101010101010101010101010101010101,031B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,Plain tweak of an even-y key
-1,0101010101010101010101010101010101010101010101010101010101010101,0202020202020202020202020202020202020202020202020202020202020202,FALSE,0303030303030303030303030303030303030303030303030303030303030303,02531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337,Plain tweak of an odd-y key
-2,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040,0202020202020202020202020202020202020202020202020202020202020202,TRUE,0101010101010101010101010101010101010101010101010101010101010101,031B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,X-only tweak of an even-y key
-3,0101010101010101010101010101010101010101010101010101010101010101,0202020202020202020202020202020202020202020202020202020202020202,TRUE,0101010101010101010101010101010101010101010101010101010101010101,031B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,X-only tweak of an odd-y key
+index,internal_secret_key,tweak,is_xonly,expected_result,expected_secret_key,expected_pubkey,comment
+0,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040,0202020202020202020202020202020202020202020202020202020202020202,FALSE,TRUE,0101010101010101010101010101010101010101010101010101010101010101,031B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,Plain tweak of an even-y key
+1,0101010101010101010101010101010101010101010101010101010101010101,0202020202020202020202020202020202020202020202020202020202020202,FALSE,TRUE,0303030303030303030303030303030303030303030303030303030303030303,02531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE337,Plain tweak of an odd-y key
+2,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040,0202020202020202020202020202020202020202020202020202020202020202,TRUE,TRUE,0101010101010101010101010101010101010101010101010101010101010101,031B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,X-only tweak of an even-y key
+3,0101010101010101010101010101010101010101010101010101010101010101,0202020202020202020202020202020202020202020202020202020202020202,TRUE,TRUE,0101010101010101010101010101010101010101010101010101010101010101,031B84C5567B126440995D3ED5AABA0565D71E1834604819FF9C17F5E9D5DD078F,X-only tweak of an odd-y key
+4,0101010101010101010101010101010101010101010101010101010101010101,FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFDB9ADDBE5AE479F3ABED15D8BCF354040,FALSE,FALSE,,,Plain tweak resulting in the point at infinity
+5,0101010101010101010101010101010101010101010101010101010101010101,0101010101010101010101010101010101010101010101010101010101010101,TRUE,FALSE,,,X-only tweak resulting in the point at infinity
</details>