Implement NIP-44 decrypt
This commit is contained in:
parent
0cbe107a98
commit
154a8f6f8e
47
nip44.go
47
nip44.go
|
@ -1,6 +1,7 @@
|
|||
package nip44
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
|
@ -47,7 +48,7 @@ func Encrypt(key []byte, plaintext string, options *EncryptOptions) (string, err
|
|||
}
|
||||
}
|
||||
if version != 2 {
|
||||
return "", errors.New("unknown encryption version")
|
||||
return "", errors.New("unknown version")
|
||||
}
|
||||
if len(salt) != 32 {
|
||||
return "", errors.New("salt must be 32 bytes")
|
||||
|
@ -69,6 +70,50 @@ func Encrypt(key []byte, plaintext string, options *EncryptOptions) (string, err
|
|||
return base64.StdEncoding.EncodeToString(concat), nil
|
||||
}
|
||||
|
||||
func Decrypt(key []byte, ciphertext string) (string, error) {
|
||||
var (
|
||||
version int = 2
|
||||
decoded []byte
|
||||
dLen int
|
||||
salt []byte
|
||||
ciphertext_ []byte
|
||||
hmac_ []byte
|
||||
enc []byte
|
||||
nonce []byte
|
||||
auth []byte
|
||||
padded []byte
|
||||
unpaddedLen uint16
|
||||
unpadded []byte
|
||||
err error
|
||||
)
|
||||
if ciphertext[0:1] == "#" {
|
||||
return "", errors.New("unknown version")
|
||||
}
|
||||
if decoded, err = base64.StdEncoding.DecodeString(ciphertext); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if version = int(decoded[0]); version != 2 {
|
||||
return "", errors.New("unknown version")
|
||||
}
|
||||
dLen = len(decoded)
|
||||
salt, ciphertext_, hmac_ = decoded[1:33], decoded[33:dLen-32], decoded[dLen-32:]
|
||||
if enc, nonce, auth, err = messageKeys(key, salt); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !bytes.Equal(hmac_, sha256Hmac(auth, ciphertext_)) {
|
||||
return "", errors.New("invalid hmac")
|
||||
}
|
||||
if padded, err = chacha20Encrypt(enc, nonce, ciphertext_); err != nil {
|
||||
return "", err
|
||||
}
|
||||
unpaddedLen = binary.BigEndian.Uint16(padded[0:2])
|
||||
unpadded = padded[2 : unpaddedLen+2]
|
||||
if len(unpadded) == 0 || len(unpadded) != int(unpaddedLen) || len(padded) != 2+calcPadding(int(unpaddedLen)) {
|
||||
return "", errors.New("invalid padding")
|
||||
}
|
||||
return string(unpadded), nil
|
||||
}
|
||||
|
||||
func chacha20Encrypt(key []byte, nonce []byte, message []byte) ([]byte, error) {
|
||||
var (
|
||||
cipher *chacha20.Cipher
|
||||
|
|
|
@ -8,12 +8,13 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func assertEncrypt(t *testing.T, key string, salt string, plaintext string, expected string) {
|
||||
func assertCrypt(t *testing.T, key string, salt string, plaintext string, expected string) {
|
||||
var (
|
||||
k []byte
|
||||
s []byte
|
||||
actual string
|
||||
err error
|
||||
k []byte
|
||||
s []byte
|
||||
actual string
|
||||
decrypted string
|
||||
err error
|
||||
)
|
||||
if k, err = hex.DecodeString(key); err != nil {
|
||||
t.Errorf("hex decode failed for key")
|
||||
|
@ -24,11 +25,15 @@ func assertEncrypt(t *testing.T, key string, salt string, plaintext string, expe
|
|||
actual, err = nip44.Encrypt(k, plaintext, &nip44.EncryptOptions{Salt: s})
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equal(t, expected, actual)
|
||||
decrypted, err = nip44.Decrypt(k, expected)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equal(t, decrypted, plaintext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncrypt001(t *testing.T) {
|
||||
assertEncrypt(t,
|
||||
func TestCrypt001(t *testing.T) {
|
||||
assertCrypt(t,
|
||||
"c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
|
||||
"0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"a",
|
||||
|
@ -36,8 +41,8 @@ func TestEncrypt001(t *testing.T) {
|
|||
)
|
||||
}
|
||||
|
||||
func TestEncrypt002(t *testing.T) {
|
||||
assertEncrypt(t,
|
||||
func TestCrypt002(t *testing.T) {
|
||||
assertCrypt(t,
|
||||
"c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
|
||||
"f00000000000000000000000000000f00000000000000000000000000000000f",
|
||||
"🍕🫃",
|
||||
|
@ -45,8 +50,8 @@ func TestEncrypt002(t *testing.T) {
|
|||
)
|
||||
}
|
||||
|
||||
func TestEncrypt003(t *testing.T) {
|
||||
assertEncrypt(t,
|
||||
func TestCrypt003(t *testing.T) {
|
||||
assertCrypt(t,
|
||||
"94da47d851b9c1ed33b3b72f35434f56aa608d60e573e9c295f568011f4f50a4",
|
||||
"b635236c42db20f021bb8d1cdff5ca75dd1a0cc72ea742ad750f33010b24f73b",
|
||||
"表ポあA鷗ŒéB逍Üߪąñ丂㐀𠀀",
|
||||
|
@ -54,8 +59,8 @@ func TestEncrypt003(t *testing.T) {
|
|||
)
|
||||
}
|
||||
|
||||
func TestEncrypt004(t *testing.T) {
|
||||
assertEncrypt(t,
|
||||
func TestCrypt004(t *testing.T) {
|
||||
assertCrypt(t,
|
||||
"ab99c122d4586cdd5c813058aa543d0e7233545dbf6874fc34a3d8d9a18fbbc3",
|
||||
"b20989adc3ddc41cd2c435952c0d59a91315d8c5218d5040573fc3749543acaf",
|
||||
"ability🤝的 ȺȾ",
|
||||
|
@ -63,8 +68,8 @@ func TestEncrypt004(t *testing.T) {
|
|||
)
|
||||
}
|
||||
|
||||
func TestEncrypt005(t *testing.T) {
|
||||
assertEncrypt(t,
|
||||
func TestCrypt005(t *testing.T) {
|
||||
assertCrypt(t,
|
||||
"a449f2a85c6d3db0f44c64554a05d11a3c0988d645e4b4b2592072f63662f422",
|
||||
"8d4442713eb9d4791175cb040d98d6fc5be8864d6ec2f89cf0895a2b2b72d1b1",
|
||||
"pepper👀їжак",
|
||||
|
@ -72,8 +77,8 @@ func TestEncrypt005(t *testing.T) {
|
|||
)
|
||||
}
|
||||
|
||||
func TestEncrypt006(t *testing.T) {
|
||||
assertEncrypt(t,
|
||||
func TestCrypt006(t *testing.T) {
|
||||
assertCrypt(t,
|
||||
"decde9938ffcb14fa7ff300105eb1bf239469af9baf376e69755b9070ae48c47",
|
||||
"2180b52ae645fcf9f5080d81b1f0b5d6f2cd77ff3c986882bb549158462f3407",
|
||||
"( ͡° ͜ʖ ͡°)",
|
||||
|
@ -81,8 +86,8 @@ func TestEncrypt006(t *testing.T) {
|
|||
)
|
||||
}
|
||||
|
||||
func TestEncrypt007(t *testing.T) {
|
||||
assertEncrypt(t,
|
||||
func TestCrypt007(t *testing.T) {
|
||||
assertCrypt(t,
|
||||
"c6f2fde7aa00208c388f506455c31c3fa07caf8b516d43bf7514ee19edcda994",
|
||||
"e4cd5f7ce4eea024bc71b17ad456a986a74ac426c2c62b0a15eb5c5c8f888b68",
|
||||
"مُنَاقَشَةُ سُبُلِ اِسْتِخْدَامِ اللُّغَةِ فِي النُّظُمِ الْقَائِمَةِ وَفِيم يَخُصَّ التَّطْبِيقَاتُ الْحاسُوبِيَّةُ،",
|
||||
|
@ -90,8 +95,8 @@ func TestEncrypt007(t *testing.T) {
|
|||
)
|
||||
}
|
||||
|
||||
func TestEncrypt008(t *testing.T) {
|
||||
assertEncrypt(t,
|
||||
func TestCrypt008(t *testing.T) {
|
||||
assertCrypt(t,
|
||||
"c6f2fde7aa00208c388f506455c31c3fa07caf8b516d43bf7514ee19edcda994",
|
||||
"38d1ca0abef9e5f564e89761a86cee04574b6825d3ef2063b10ad75899e4b023",
|
||||
"الكل في المجمو عة (5)",
|
||||
|
@ -99,8 +104,8 @@ func TestEncrypt008(t *testing.T) {
|
|||
)
|
||||
}
|
||||
|
||||
func TestEncrypt009(t *testing.T) {
|
||||
assertEncrypt(t,
|
||||
func TestCrypt009(t *testing.T) {
|
||||
assertCrypt(t,
|
||||
"c6f2fde7aa00208c388f506455c31c3fa07caf8b516d43bf7514ee19edcda994",
|
||||
"4f1a31909f3483a9e69c8549a55bbc9af25fa5bbecf7bd32d9896f83ef2e12e0",
|
||||
"𝖑𝖆𝖟𝖞 社會科學院語學研究所",
|
||||
|
@ -108,8 +113,8 @@ func TestEncrypt009(t *testing.T) {
|
|||
)
|
||||
}
|
||||
|
||||
func TestEncrypt010(t *testing.T) {
|
||||
assertEncrypt(t,
|
||||
func TestCrypt010(t *testing.T) {
|
||||
assertCrypt(t,
|
||||
"c6f2fde7aa00208c388f506455c31c3fa07caf8b516d43bf7514ee19edcda994",
|
||||
"a3e219242d85465e70adcd640b564b3feff57d2ef8745d5e7a0663b2dccceb54",
|
||||
"🙈 🙉 🙊 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 Powerلُلُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ冗",
|
||||
|
@ -117,24 +122,24 @@ func TestEncrypt010(t *testing.T) {
|
|||
)
|
||||
}
|
||||
|
||||
func TestEncrypt011(t *testing.T) {
|
||||
assertEncrypt(t,
|
||||
func TestCrypt011(t *testing.T) {
|
||||
assertCrypt(t,
|
||||
"7a1ccf5ce5a08e380f590de0c02776623b85a61ae67cfb6a017317e505b7cb51",
|
||||
"a000000000000000000000000000000000000000000000000000000000000001",
|
||||
"⁰⁴⁵₀₁₂",
|
||||
"AqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2+xmGnjIMPMqqJGmjdYAYZUDUyEEUO3/evHUaO40LePeR91VlMVZ7I+nKJPkaUiKZ3cQiQnA86Uwti2IxepmzOFN",
|
||||
)
|
||||
}
|
||||
func TestEncrypt012(t *testing.T) {
|
||||
assertEncrypt(t,
|
||||
func TestCrypt012(t *testing.T) {
|
||||
assertCrypt(t,
|
||||
"aa971537d741089885a0b48f2730a125e15b36033d089d4537a4e1204e76b39e",
|
||||
"b000000000000000000000000000000000000000000000000000000000000002",
|
||||
"A Peer-to-Peer Electronic Cash System",
|
||||
"ArAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACyuqG6RycuPyDPtwxzTcuMQu+is3N5XuWTlvCjligVaVBRydexaylXbsX592MEd3/Jt13BNL/GlpYpGDvLS4Tt/+2s9FX/16e/RDc+czdwXglc4DdSHiq+O06BvvXYfEQOPw=",
|
||||
)
|
||||
}
|
||||
func TestEncrypt013(t *testing.T) {
|
||||
assertEncrypt(t,
|
||||
func TestCrypt013(t *testing.T) {
|
||||
assertCrypt(t,
|
||||
"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
|
||||
"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
|
||||
"A purely peer-to-peer version of electronic cash would allow online payments to be sent directly from one party to another without going through a financial institution. Digital signatures provide part of the solution, but the main benefits are lost if a trusted third party is still required to prevent double-spending.",
|
||||
|
|
Loading…
Reference in New Issue