Implement NIP-44 decrypt
This commit is contained in:
parent
6339774765
commit
506db70831
47
nip44.go
47
nip44.go
@ -1,6 +1,7 @@
|
|||||||
package nip44
|
package nip44
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
@ -47,7 +48,7 @@ func Encrypt(key []byte, plaintext string, options *EncryptOptions) (string, err
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if version != 2 {
|
if version != 2 {
|
||||||
return "", errors.New("unknown encryption version")
|
return "", errors.New("unknown version")
|
||||||
}
|
}
|
||||||
if len(salt) != 32 {
|
if len(salt) != 32 {
|
||||||
return "", errors.New("salt must be 32 bytes")
|
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
|
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) {
|
func chacha20Encrypt(key []byte, nonce []byte, message []byte) ([]byte, error) {
|
||||||
var (
|
var (
|
||||||
cipher *chacha20.Cipher
|
cipher *chacha20.Cipher
|
||||||
|
@ -8,11 +8,12 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"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 (
|
var (
|
||||||
k []byte
|
k []byte
|
||||||
s []byte
|
s []byte
|
||||||
actual string
|
actual string
|
||||||
|
decrypted string
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if k, err = hex.DecodeString(key); err != nil {
|
if k, err = hex.DecodeString(key); err != nil {
|
||||||
@ -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})
|
actual, err = nip44.Encrypt(k, plaintext, &nip44.EncryptOptions{Salt: s})
|
||||||
if assert.NoError(t, err) {
|
if assert.NoError(t, err) {
|
||||||
assert.Equal(t, expected, actual)
|
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) {
|
func TestCrypt001(t *testing.T) {
|
||||||
assertEncrypt(t,
|
assertCrypt(t,
|
||||||
"c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
|
"c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001",
|
"0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
"a",
|
"a",
|
||||||
@ -36,8 +41,8 @@ func TestEncrypt001(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncrypt002(t *testing.T) {
|
func TestCrypt002(t *testing.T) {
|
||||||
assertEncrypt(t,
|
assertCrypt(t,
|
||||||
"c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
|
"c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
|
||||||
"f00000000000000000000000000000f00000000000000000000000000000000f",
|
"f00000000000000000000000000000f00000000000000000000000000000000f",
|
||||||
"🍕🫃",
|
"🍕🫃",
|
||||||
@ -45,8 +50,8 @@ func TestEncrypt002(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncrypt003(t *testing.T) {
|
func TestCrypt003(t *testing.T) {
|
||||||
assertEncrypt(t,
|
assertCrypt(t,
|
||||||
"94da47d851b9c1ed33b3b72f35434f56aa608d60e573e9c295f568011f4f50a4",
|
"94da47d851b9c1ed33b3b72f35434f56aa608d60e573e9c295f568011f4f50a4",
|
||||||
"b635236c42db20f021bb8d1cdff5ca75dd1a0cc72ea742ad750f33010b24f73b",
|
"b635236c42db20f021bb8d1cdff5ca75dd1a0cc72ea742ad750f33010b24f73b",
|
||||||
"表ポあA鷗ŒéB逍Üߪąñ丂㐀𠀀",
|
"表ポあA鷗ŒéB逍Üߪąñ丂㐀𠀀",
|
||||||
@ -54,8 +59,8 @@ func TestEncrypt003(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncrypt004(t *testing.T) {
|
func TestCrypt004(t *testing.T) {
|
||||||
assertEncrypt(t,
|
assertCrypt(t,
|
||||||
"ab99c122d4586cdd5c813058aa543d0e7233545dbf6874fc34a3d8d9a18fbbc3",
|
"ab99c122d4586cdd5c813058aa543d0e7233545dbf6874fc34a3d8d9a18fbbc3",
|
||||||
"b20989adc3ddc41cd2c435952c0d59a91315d8c5218d5040573fc3749543acaf",
|
"b20989adc3ddc41cd2c435952c0d59a91315d8c5218d5040573fc3749543acaf",
|
||||||
"ability🤝的 ȺȾ",
|
"ability🤝的 ȺȾ",
|
||||||
@ -63,8 +68,8 @@ func TestEncrypt004(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncrypt005(t *testing.T) {
|
func TestCrypt005(t *testing.T) {
|
||||||
assertEncrypt(t,
|
assertCrypt(t,
|
||||||
"a449f2a85c6d3db0f44c64554a05d11a3c0988d645e4b4b2592072f63662f422",
|
"a449f2a85c6d3db0f44c64554a05d11a3c0988d645e4b4b2592072f63662f422",
|
||||||
"8d4442713eb9d4791175cb040d98d6fc5be8864d6ec2f89cf0895a2b2b72d1b1",
|
"8d4442713eb9d4791175cb040d98d6fc5be8864d6ec2f89cf0895a2b2b72d1b1",
|
||||||
"pepper👀їжак",
|
"pepper👀їжак",
|
||||||
@ -72,8 +77,8 @@ func TestEncrypt005(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncrypt006(t *testing.T) {
|
func TestCrypt006(t *testing.T) {
|
||||||
assertEncrypt(t,
|
assertCrypt(t,
|
||||||
"decde9938ffcb14fa7ff300105eb1bf239469af9baf376e69755b9070ae48c47",
|
"decde9938ffcb14fa7ff300105eb1bf239469af9baf376e69755b9070ae48c47",
|
||||||
"2180b52ae645fcf9f5080d81b1f0b5d6f2cd77ff3c986882bb549158462f3407",
|
"2180b52ae645fcf9f5080d81b1f0b5d6f2cd77ff3c986882bb549158462f3407",
|
||||||
"( ͡° ͜ʖ ͡°)",
|
"( ͡° ͜ʖ ͡°)",
|
||||||
@ -81,8 +86,8 @@ func TestEncrypt006(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncrypt007(t *testing.T) {
|
func TestCrypt007(t *testing.T) {
|
||||||
assertEncrypt(t,
|
assertCrypt(t,
|
||||||
"c6f2fde7aa00208c388f506455c31c3fa07caf8b516d43bf7514ee19edcda994",
|
"c6f2fde7aa00208c388f506455c31c3fa07caf8b516d43bf7514ee19edcda994",
|
||||||
"e4cd5f7ce4eea024bc71b17ad456a986a74ac426c2c62b0a15eb5c5c8f888b68",
|
"e4cd5f7ce4eea024bc71b17ad456a986a74ac426c2c62b0a15eb5c5c8f888b68",
|
||||||
"مُنَاقَشَةُ سُبُلِ اِسْتِخْدَامِ اللُّغَةِ فِي النُّظُمِ الْقَائِمَةِ وَفِيم يَخُصَّ التَّطْبِيقَاتُ الْحاسُوبِيَّةُ،",
|
"مُنَاقَشَةُ سُبُلِ اِسْتِخْدَامِ اللُّغَةِ فِي النُّظُمِ الْقَائِمَةِ وَفِيم يَخُصَّ التَّطْبِيقَاتُ الْحاسُوبِيَّةُ،",
|
||||||
@ -90,8 +95,8 @@ func TestEncrypt007(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncrypt008(t *testing.T) {
|
func TestCrypt008(t *testing.T) {
|
||||||
assertEncrypt(t,
|
assertCrypt(t,
|
||||||
"c6f2fde7aa00208c388f506455c31c3fa07caf8b516d43bf7514ee19edcda994",
|
"c6f2fde7aa00208c388f506455c31c3fa07caf8b516d43bf7514ee19edcda994",
|
||||||
"38d1ca0abef9e5f564e89761a86cee04574b6825d3ef2063b10ad75899e4b023",
|
"38d1ca0abef9e5f564e89761a86cee04574b6825d3ef2063b10ad75899e4b023",
|
||||||
"الكل في المجمو عة (5)",
|
"الكل في المجمو عة (5)",
|
||||||
@ -99,8 +104,8 @@ func TestEncrypt008(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncrypt009(t *testing.T) {
|
func TestCrypt009(t *testing.T) {
|
||||||
assertEncrypt(t,
|
assertCrypt(t,
|
||||||
"c6f2fde7aa00208c388f506455c31c3fa07caf8b516d43bf7514ee19edcda994",
|
"c6f2fde7aa00208c388f506455c31c3fa07caf8b516d43bf7514ee19edcda994",
|
||||||
"4f1a31909f3483a9e69c8549a55bbc9af25fa5bbecf7bd32d9896f83ef2e12e0",
|
"4f1a31909f3483a9e69c8549a55bbc9af25fa5bbecf7bd32d9896f83ef2e12e0",
|
||||||
"𝖑𝖆𝖟𝖞 社會科學院語學研究所",
|
"𝖑𝖆𝖟𝖞 社會科學院語學研究所",
|
||||||
@ -108,8 +113,8 @@ func TestEncrypt009(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncrypt010(t *testing.T) {
|
func TestCrypt010(t *testing.T) {
|
||||||
assertEncrypt(t,
|
assertCrypt(t,
|
||||||
"c6f2fde7aa00208c388f506455c31c3fa07caf8b516d43bf7514ee19edcda994",
|
"c6f2fde7aa00208c388f506455c31c3fa07caf8b516d43bf7514ee19edcda994",
|
||||||
"a3e219242d85465e70adcd640b564b3feff57d2ef8745d5e7a0663b2dccceb54",
|
"a3e219242d85465e70adcd640b564b3feff57d2ef8745d5e7a0663b2dccceb54",
|
||||||
"🙈 🙉 🙊 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 Powerلُلُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ冗",
|
"🙈 🙉 🙊 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) {
|
func TestCrypt011(t *testing.T) {
|
||||||
assertEncrypt(t,
|
assertCrypt(t,
|
||||||
"7a1ccf5ce5a08e380f590de0c02776623b85a61ae67cfb6a017317e505b7cb51",
|
"7a1ccf5ce5a08e380f590de0c02776623b85a61ae67cfb6a017317e505b7cb51",
|
||||||
"a000000000000000000000000000000000000000000000000000000000000001",
|
"a000000000000000000000000000000000000000000000000000000000000001",
|
||||||
"⁰⁴⁵₀₁₂",
|
"⁰⁴⁵₀₁₂",
|
||||||
"AqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2+xmGnjIMPMqqJGmjdYAYZUDUyEEUO3/evHUaO40LePeR91VlMVZ7I+nKJPkaUiKZ3cQiQnA86Uwti2IxepmzOFN",
|
"AqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2+xmGnjIMPMqqJGmjdYAYZUDUyEEUO3/evHUaO40LePeR91VlMVZ7I+nKJPkaUiKZ3cQiQnA86Uwti2IxepmzOFN",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
func TestEncrypt012(t *testing.T) {
|
func TestCrypt012(t *testing.T) {
|
||||||
assertEncrypt(t,
|
assertCrypt(t,
|
||||||
"aa971537d741089885a0b48f2730a125e15b36033d089d4537a4e1204e76b39e",
|
"aa971537d741089885a0b48f2730a125e15b36033d089d4537a4e1204e76b39e",
|
||||||
"b000000000000000000000000000000000000000000000000000000000000002",
|
"b000000000000000000000000000000000000000000000000000000000000002",
|
||||||
"A Peer-to-Peer Electronic Cash System",
|
"A Peer-to-Peer Electronic Cash System",
|
||||||
"ArAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACyuqG6RycuPyDPtwxzTcuMQu+is3N5XuWTlvCjligVaVBRydexaylXbsX592MEd3/Jt13BNL/GlpYpGDvLS4Tt/+2s9FX/16e/RDc+czdwXglc4DdSHiq+O06BvvXYfEQOPw=",
|
"ArAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACyuqG6RycuPyDPtwxzTcuMQu+is3N5XuWTlvCjligVaVBRydexaylXbsX592MEd3/Jt13BNL/GlpYpGDvLS4Tt/+2s9FX/16e/RDc+czdwXglc4DdSHiq+O06BvvXYfEQOPw=",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
func TestEncrypt013(t *testing.T) {
|
func TestCrypt013(t *testing.T) {
|
||||||
assertEncrypt(t,
|
assertCrypt(t,
|
||||||
"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
|
"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
|
||||||
"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.",
|
"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…
x
Reference in New Issue
Block a user