109 lines
2.7 KiB
Go
109 lines
2.7 KiB
Go
|
|
package ownwire_sdk_test
|
||
|
|
|
||
|
|
import (
|
||
|
|
"crypto/hmac"
|
||
|
|
"crypto/sha256"
|
||
|
|
"encoding/hex"
|
||
|
|
|
||
|
|
. "github.com/onsi/ginkgo/v2"
|
||
|
|
. "github.com/onsi/gomega"
|
||
|
|
|
||
|
|
sdk "ownwire.net/ownwire-sdk"
|
||
|
|
)
|
||
|
|
|
||
|
|
func derive_nonce_reference(shared_key [32]byte, session_uuid_bytes [16]byte, salt16 [16]byte, seq_num uint64, is_response bool) [12]byte {
|
||
|
|
seq_be8 := [8]byte{}
|
||
|
|
for i := 7; i >= 0; i-- {
|
||
|
|
seq_be8[i] = byte(seq_num & 0xff)
|
||
|
|
seq_num >>= 8
|
||
|
|
}
|
||
|
|
|
||
|
|
flag := byte(0)
|
||
|
|
if is_response {
|
||
|
|
flag = 1
|
||
|
|
}
|
||
|
|
|
||
|
|
mac := hmac.New(sha256.New, shared_key[:])
|
||
|
|
mac.Write([]byte("ownwire/v1:gcm-nonce"))
|
||
|
|
mac.Write(session_uuid_bytes[:])
|
||
|
|
mac.Write(salt16[:])
|
||
|
|
mac.Write(seq_be8[:])
|
||
|
|
mac.Write([]byte{flag})
|
||
|
|
|
||
|
|
sum := mac.Sum(nil)
|
||
|
|
|
||
|
|
var iv [12]byte
|
||
|
|
copy(iv[:], sum[:12])
|
||
|
|
return iv
|
||
|
|
}
|
||
|
|
|
||
|
|
var _ = Describe("Crypto", func() {
|
||
|
|
It("parses UUID bytes", func() {
|
||
|
|
uuid_str := "cb653f53-6f7d-4aeb-ba0d-d2b17c290d8a"
|
||
|
|
uuid_bytes, err := sdk.ParseUUIDBytes(uuid_str)
|
||
|
|
Expect(err).To(BeNil())
|
||
|
|
Expect(hex.EncodeToString(uuid_bytes[:])).To(Equal("cb653f536f7d4aebba0dd2b17c290d8a"))
|
||
|
|
})
|
||
|
|
|
||
|
|
It("derives nonce exactly as reference implementation", func() {
|
||
|
|
var shared_key [32]byte
|
||
|
|
for i := 0; i < 32; i++ {
|
||
|
|
shared_key[i] = byte(i)
|
||
|
|
}
|
||
|
|
|
||
|
|
uuid_bytes, err := sdk.ParseUUIDBytes("cb653f53-6f7d-4aeb-ba0d-d2b17c290d8a")
|
||
|
|
Expect(err).To(BeNil())
|
||
|
|
|
||
|
|
var salt16 [16]byte
|
||
|
|
for i := 0; i < 16; i++ {
|
||
|
|
salt16[i] = byte(0xa0 + i)
|
||
|
|
}
|
||
|
|
|
||
|
|
seq_num := uint64(0x0102030405060708)
|
||
|
|
|
||
|
|
got := sdk.DeriveNonce(shared_key, uuid_bytes, salt16, seq_num, false)
|
||
|
|
want := derive_nonce_reference(shared_key, uuid_bytes, salt16, seq_num, false)
|
||
|
|
|
||
|
|
Expect(got).To(Equal(want))
|
||
|
|
})
|
||
|
|
|
||
|
|
It("encrypts and decrypts roundtrip", func() {
|
||
|
|
var shared_key [32]byte
|
||
|
|
for i := 0; i < 32; i++ {
|
||
|
|
shared_key[i] = byte(0x55 ^ i)
|
||
|
|
}
|
||
|
|
|
||
|
|
uuid_bytes, err := sdk.ParseUUIDBytes("cb653f53-6f7d-4aeb-ba0d-d2b17c290d8a")
|
||
|
|
Expect(err).To(BeNil())
|
||
|
|
|
||
|
|
plain_text := []byte("hello ownwire")
|
||
|
|
seq_num := uint64(123)
|
||
|
|
|
||
|
|
enc, err := sdk.EncryptAESGCM(shared_key, uuid_bytes, plain_text, seq_num, false)
|
||
|
|
Expect(err).To(BeNil())
|
||
|
|
Expect(enc.ContentB64).ToNot(BeEmpty())
|
||
|
|
Expect(enc.SaltHex).To(HaveLen(32)) // 16 bytes hex
|
||
|
|
|
||
|
|
out, err := sdk.DecryptAESGCM(shared_key, uuid_bytes, enc.ContentB64, enc.SaltHex, seq_num, false)
|
||
|
|
Expect(err).To(BeNil())
|
||
|
|
Expect(string(out)).To(Equal(string(plain_text)))
|
||
|
|
})
|
||
|
|
|
||
|
|
It("fails decrypt if seq changes", func() {
|
||
|
|
var shared_key [32]byte
|
||
|
|
for i := 0; i < 32; i++ {
|
||
|
|
shared_key[i] = byte(0x11 + i)
|
||
|
|
}
|
||
|
|
|
||
|
|
uuid_bytes, err := sdk.ParseUUIDBytes("cb653f53-6f7d-4aeb-ba0d-d2b17c290d8a")
|
||
|
|
Expect(err).To(BeNil())
|
||
|
|
|
||
|
|
enc, err := sdk.EncryptAESGCM(shared_key, uuid_bytes, []byte("hello"), 1, false)
|
||
|
|
Expect(err).To(BeNil())
|
||
|
|
|
||
|
|
_, err = sdk.DecryptAESGCM(shared_key, uuid_bytes, enc.ContentB64, enc.SaltHex, 2, false)
|
||
|
|
Expect(err).ToNot(BeNil())
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|