Browse Source

Switch xchachapoly to hkdfchachapoly (#135)

* Switch from xchachapoly to hkdfchachapoly
pull/1782/head
Dev Ojha 7 years ago
committed by Ismail Khoffi
parent
commit
fed8807a32
5 changed files with 246 additions and 342 deletions
  1. +2
    -1
      Gopkg.lock
  2. +105
    -0
      hkdfchacha20poly1305/hkdfchachapoly.go
  3. +139
    -0
      hkdfchacha20poly1305/hkdfchachapoly_test.go
  4. +0
    -238
      xchacha20poly1305/xchachapoly.go
  5. +0
    -103
      xchacha20poly1305/xchachapoly_test.go

+ 2
- 1
Gopkg.lock View File

@ -166,6 +166,7 @@
"bcrypt", "bcrypt",
"blowfish", "blowfish",
"chacha20poly1305", "chacha20poly1305",
"hkdf",
"internal/chacha20", "internal/chacha20",
"nacl/secretbox", "nacl/secretbox",
"openpgp/armor", "openpgp/armor",
@ -186,6 +187,6 @@
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
analyzer-version = 1 analyzer-version = 1
inputs-digest = "1731880d432026bbd22865582cf7c7dd6d5c65618149de586e30ce156d338c01"
inputs-digest = "684746f2b651a722ee32eae056b437baf7a2213d68ddbfcefe04a1c3df27c93b"
solver-name = "gps-cdcl" solver-name = "gps-cdcl"
solver-version = 1 solver-version = 1

+ 105
- 0
hkdfchacha20poly1305/hkdfchachapoly.go View File

@ -0,0 +1,105 @@
// Package hkdfchacha20poly1305 creates an AEAD using hkdf, chacha20, and poly1305
// When sealing and opening, the hkdf is used to obtain the nonce and subkey for
// chacha20. Other than the change for the how the subkey and nonce for chacha
// are obtained, this is the same as chacha20poly1305
package hkdfchacha20poly1305
import (
"crypto/cipher"
"crypto/sha256"
"errors"
"io"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/hkdf"
)
type hkdfchacha20poly1305 struct {
key [KeySize]byte
}
const (
// KeySize is the size of the key used by this AEAD, in bytes.
KeySize = 32
// NonceSize is the size of the nonce used with this AEAD, in bytes.
NonceSize = 24
// TagSize is the size added from poly1305
TagSize = 16
// MaxPlaintextSize is the max size that can be passed into a single call of Seal
MaxPlaintextSize = (1 << 38) - 64
// MaxCiphertextSize is the max size that can be passed into a single call of Open,
// this differs from plaintext size due to the tag
MaxCiphertextSize = (1 << 38) - 48
// HkdfInfo is the parameter used internally for Hkdf's info parameter.
HkdfInfo = "TENDERMINT_SECRET_CONNECTION_FRAME_KEY_DERIVE"
)
//New xChaChapoly1305 AEAD with 24 byte nonces
func New(key []byte) (cipher.AEAD, error) {
if len(key) != KeySize {
return nil, errors.New("chacha20poly1305: bad key length")
}
ret := new(hkdfchacha20poly1305)
copy(ret.key[:], key)
return ret, nil
}
func (c *hkdfchacha20poly1305) NonceSize() int {
return NonceSize
}
func (c *hkdfchacha20poly1305) Overhead() int {
return TagSize
}
func (c *hkdfchacha20poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
if len(nonce) != NonceSize {
panic("hkdfchacha20poly1305: bad nonce length passed to Seal")
}
if uint64(len(plaintext)) > MaxPlaintextSize {
panic("hkdfchacha20poly1305: plaintext too large")
}
subKey, chachaNonce := getSubkeyAndChachaNonceFromHkdf(&c.key, &nonce)
aead, err := chacha20poly1305.New(subKey[:])
if err != nil {
panic("hkdfchacha20poly1305: failed to initialize chacha20poly1305")
}
return aead.Seal(dst, chachaNonce[:], plaintext, additionalData)
}
func (c *hkdfchacha20poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
if len(nonce) != NonceSize {
return nil, errors.New("hkdfchacha20poly1305: bad nonce length passed to Open")
}
if uint64(len(ciphertext)) > MaxCiphertextSize {
return nil, errors.New("hkdfchacha20poly1305: ciphertext too large")
}
subKey, chachaNonce := getSubkeyAndChachaNonceFromHkdf(&c.key, &nonce)
aead, err := chacha20poly1305.New(subKey[:])
if err != nil {
panic("hkdfchacha20poly1305: failed to initialize chacha20poly1305")
}
return aead.Open(dst, chachaNonce[:], ciphertext, additionalData)
}
func getSubkeyAndChachaNonceFromHkdf(cKey *[32]byte, nonce *[]byte) (
subKey [KeySize]byte, chachaNonce [chacha20poly1305.NonceSize]byte) {
hash := sha256.New
hkdf := hkdf.New(hash, (*cKey)[:], *nonce, []byte(HkdfInfo))
_, err := io.ReadFull(hkdf, subKey[:])
if err != nil {
panic("hkdfchacha20poly1305: failed to read subkey from hkdf")
}
_, err = io.ReadFull(hkdf, chachaNonce[:])
if err != nil {
panic("hkdfchacha20poly1305: failed to read chachaNonce from hkdf")
}
return
}

+ 139
- 0
hkdfchacha20poly1305/hkdfchachapoly_test.go View File

@ -0,0 +1,139 @@
package hkdfchacha20poly1305
import (
"bytes"
cr "crypto/rand"
"encoding/hex"
mr "math/rand"
"testing"
"github.com/stretchr/testify/assert"
)
// Test that a test vector we generated is valid. (Ensures backwards
// compatability)
func TestVector(t *testing.T) {
key, _ := hex.DecodeString("56f8de45d3c294c7675bcaf457bdd4b71c380b9b2408ce9412b348d0f08b69ee")
aead, err := New(key[:])
if err != nil {
t.Fatal(err)
}
cts := []string{"e20a8bf42c535ac30125cfc52031577f0b",
"657695b37ba30f67b25860d90a6f1d00d8",
"e9aa6f3b7f625d957fd50f05bcdf20d014",
"8a00b3b5a6014e0d2033bebc5935086245",
"aadd74867b923879e6866ea9e03c009039",
"fc59773c2c864ee3b4cc971876b3c7bed4",
"caec14e3a9a52ce1a2682c6737defa4752",
"0b89511ffe490d2049d6950494ee51f919",
"7de854ea71f43ca35167a07566c769083d",
"cd477327f4ea4765c71e311c5fec1edbfb"}
for i := 0; i < 10; i++ {
ct, _ := hex.DecodeString(cts[i])
byteArr := []byte{byte(i)}
nonce := make([]byte, 24, 24)
nonce[0] = byteArr[0]
plaintext, err := aead.Open(nil, nonce, ct, byteArr)
if err != nil {
t.Errorf("%dth Open failed", i)
continue
}
assert.Equal(t, byteArr, plaintext)
}
}
// The following test is taken from
// https://github.com/golang/crypto/blob/master/chacha20poly1305/chacha20poly1305_test.go#L69
// It requires the below copyright notice, where "this source code" refers to the following function.
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found at the bottom of this file.
func TestRandom(t *testing.T) {
// Some random tests to verify Open(Seal) == Plaintext
for i := 0; i < 256; i++ {
var nonce [24]byte
var key [32]byte
al := mr.Intn(128)
pl := mr.Intn(16384)
ad := make([]byte, al)
plaintext := make([]byte, pl)
cr.Read(key[:])
cr.Read(nonce[:])
cr.Read(ad)
cr.Read(plaintext)
aead, err := New(key[:])
if err != nil {
t.Fatal(err)
}
ct := aead.Seal(nil, nonce[:], plaintext, ad)
plaintext2, err := aead.Open(nil, nonce[:], ct, ad)
if err != nil {
t.Errorf("Random #%d: Open failed", i)
continue
}
if !bytes.Equal(plaintext, plaintext2) {
t.Errorf("Random #%d: plaintext's don't match: got %x vs %x", i, plaintext2, plaintext)
continue
}
if len(ad) > 0 {
alterAdIdx := mr.Intn(len(ad))
ad[alterAdIdx] ^= 0x80
if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
t.Errorf("Random #%d: Open was successful after altering additional data", i)
}
ad[alterAdIdx] ^= 0x80
}
alterNonceIdx := mr.Intn(aead.NonceSize())
nonce[alterNonceIdx] ^= 0x80
if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
t.Errorf("Random #%d: Open was successful after altering nonce", i)
}
nonce[alterNonceIdx] ^= 0x80
alterCtIdx := mr.Intn(len(ct))
ct[alterCtIdx] ^= 0x80
if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
t.Errorf("Random #%d: Open was successful after altering ciphertext", i)
}
ct[alterCtIdx] ^= 0x80
}
}
// AFOREMENTIONED LICENCE
// Copyright (c) 2009 The Go Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 0
- 238
xchacha20poly1305/xchachapoly.go View File

@ -1,238 +0,0 @@
package xchacha20poly1305
import (
"crypto/cipher"
"encoding/binary"
"errors"
"fmt"
"golang.org/x/crypto/chacha20poly1305"
)
var sigma = [4]uint32{0x61707865, 0x3320646e, 0x79622d32, 0x6b206574}
type xchacha20poly1305 struct {
key [KeySize]byte
}
const (
// KeySize is the size of the key used by this AEAD, in bytes.
KeySize = 32
// NonceSize is the size of the nonce used with this AEAD, in bytes.
NonceSize = 24
)
//New xChaChapoly1305 AEAD with 24 byte nonces
func New(key []byte) (cipher.AEAD, error) {
if len(key) != KeySize {
return nil, errors.New("chacha20poly1305: bad key length")
}
ret := new(xchacha20poly1305)
copy(ret.key[:], key)
return ret, nil
}
func (c *xchacha20poly1305) NonceSize() int {
return NonceSize
}
func (c *xchacha20poly1305) Overhead() int {
return 16
}
func (c *xchacha20poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
if len(nonce) != NonceSize {
panic("xchacha20poly1305: bad nonce length passed to Seal")
}
if uint64(len(plaintext)) > (1<<38)-64 {
panic("xchacha20poly1305: plaintext too large")
}
var subKey [KeySize]byte
var hNonce [16]byte
var subNonce [chacha20poly1305.NonceSize]byte
copy(hNonce[:], nonce[:16])
HChaCha20(&subKey, &hNonce, &c.key)
chacha20poly1305, _ := chacha20poly1305.New(subKey[:])
copy(subNonce[4:], nonce[16:])
return chacha20poly1305.Seal(dst, subNonce[:], plaintext, additionalData)
}
func (c *xchacha20poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
if len(nonce) != NonceSize {
return nil, fmt.Errorf("xchacha20poly1305: bad nonce length passed to Open")
}
if uint64(len(ciphertext)) > (1<<38)-48 {
return nil, fmt.Errorf("xchacha20poly1305: ciphertext too large")
}
var subKey [KeySize]byte
var hNonce [16]byte
var subNonce [chacha20poly1305.NonceSize]byte
copy(hNonce[:], nonce[:16])
HChaCha20(&subKey, &hNonce, &c.key)
chacha20poly1305, _ := chacha20poly1305.New(subKey[:])
copy(subNonce[4:], nonce[16:])
return chacha20poly1305.Open(dst, subNonce[:], ciphertext, additionalData)
}
// The MIT License (MIT)
// Copyright (c) 2016 Andreas Auernhammer
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// HChaCha20 generates 32 pseudo-random bytes from a 128 bit nonce and a 256 bit secret key.
// It can be used as a key-derivation-function (KDF).
func HChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) { hChaCha20Generic(out, nonce, key) }
func hChaCha20Generic(out *[32]byte, nonce *[16]byte, key *[32]byte) {
v00 := sigma[0]
v01 := sigma[1]
v02 := sigma[2]
v03 := sigma[3]
v04 := binary.LittleEndian.Uint32(key[0:])
v05 := binary.LittleEndian.Uint32(key[4:])
v06 := binary.LittleEndian.Uint32(key[8:])
v07 := binary.LittleEndian.Uint32(key[12:])
v08 := binary.LittleEndian.Uint32(key[16:])
v09 := binary.LittleEndian.Uint32(key[20:])
v10 := binary.LittleEndian.Uint32(key[24:])
v11 := binary.LittleEndian.Uint32(key[28:])
v12 := binary.LittleEndian.Uint32(nonce[0:])
v13 := binary.LittleEndian.Uint32(nonce[4:])
v14 := binary.LittleEndian.Uint32(nonce[8:])
v15 := binary.LittleEndian.Uint32(nonce[12:])
for i := 0; i < 20; i += 2 {
v00 += v04
v12 ^= v00
v12 = (v12 << 16) | (v12 >> 16)
v08 += v12
v04 ^= v08
v04 = (v04 << 12) | (v04 >> 20)
v00 += v04
v12 ^= v00
v12 = (v12 << 8) | (v12 >> 24)
v08 += v12
v04 ^= v08
v04 = (v04 << 7) | (v04 >> 25)
v01 += v05
v13 ^= v01
v13 = (v13 << 16) | (v13 >> 16)
v09 += v13
v05 ^= v09
v05 = (v05 << 12) | (v05 >> 20)
v01 += v05
v13 ^= v01
v13 = (v13 << 8) | (v13 >> 24)
v09 += v13
v05 ^= v09
v05 = (v05 << 7) | (v05 >> 25)
v02 += v06
v14 ^= v02
v14 = (v14 << 16) | (v14 >> 16)
v10 += v14
v06 ^= v10
v06 = (v06 << 12) | (v06 >> 20)
v02 += v06
v14 ^= v02
v14 = (v14 << 8) | (v14 >> 24)
v10 += v14
v06 ^= v10
v06 = (v06 << 7) | (v06 >> 25)
v03 += v07
v15 ^= v03
v15 = (v15 << 16) | (v15 >> 16)
v11 += v15
v07 ^= v11
v07 = (v07 << 12) | (v07 >> 20)
v03 += v07
v15 ^= v03
v15 = (v15 << 8) | (v15 >> 24)
v11 += v15
v07 ^= v11
v07 = (v07 << 7) | (v07 >> 25)
v00 += v05
v15 ^= v00
v15 = (v15 << 16) | (v15 >> 16)
v10 += v15
v05 ^= v10
v05 = (v05 << 12) | (v05 >> 20)
v00 += v05
v15 ^= v00
v15 = (v15 << 8) | (v15 >> 24)
v10 += v15
v05 ^= v10
v05 = (v05 << 7) | (v05 >> 25)
v01 += v06
v12 ^= v01
v12 = (v12 << 16) | (v12 >> 16)
v11 += v12
v06 ^= v11
v06 = (v06 << 12) | (v06 >> 20)
v01 += v06
v12 ^= v01
v12 = (v12 << 8) | (v12 >> 24)
v11 += v12
v06 ^= v11
v06 = (v06 << 7) | (v06 >> 25)
v02 += v07
v13 ^= v02
v13 = (v13 << 16) | (v13 >> 16)
v08 += v13
v07 ^= v08
v07 = (v07 << 12) | (v07 >> 20)
v02 += v07
v13 ^= v02
v13 = (v13 << 8) | (v13 >> 24)
v08 += v13
v07 ^= v08
v07 = (v07 << 7) | (v07 >> 25)
v03 += v04
v14 ^= v03
v14 = (v14 << 16) | (v14 >> 16)
v09 += v14
v04 ^= v09
v04 = (v04 << 12) | (v04 >> 20)
v03 += v04
v14 ^= v03
v14 = (v14 << 8) | (v14 >> 24)
v09 += v14
v04 ^= v09
v04 = (v04 << 7) | (v04 >> 25)
}
binary.LittleEndian.PutUint32(out[0:], v00)
binary.LittleEndian.PutUint32(out[4:], v01)
binary.LittleEndian.PutUint32(out[8:], v02)
binary.LittleEndian.PutUint32(out[12:], v03)
binary.LittleEndian.PutUint32(out[16:], v12)
binary.LittleEndian.PutUint32(out[20:], v13)
binary.LittleEndian.PutUint32(out[24:], v14)
binary.LittleEndian.PutUint32(out[28:], v15)
}

+ 0
- 103
xchacha20poly1305/xchachapoly_test.go View File

@ -1,103 +0,0 @@
package xchacha20poly1305
import (
"bytes"
"encoding/hex"
"testing"
)
func toHex(bits []byte) string {
return hex.EncodeToString(bits)
}
func fromHex(bits string) []byte {
b, err := hex.DecodeString(bits)
if err != nil {
panic(err)
}
return b
}
func TestHChaCha20(t *testing.T) {
for i, v := range hChaCha20Vectors {
var key [32]byte
var nonce [16]byte
copy(key[:], v.key)
copy(nonce[:], v.nonce)
HChaCha20(&key, &nonce, &key)
if !bytes.Equal(key[:], v.keystream) {
t.Errorf("Test %d: keystream mismatch:\n \t got: %s\n \t want: %s", i, toHex(key[:]), toHex(v.keystream))
}
}
}
var hChaCha20Vectors = []struct {
key, nonce, keystream []byte
}{
{
fromHex("0000000000000000000000000000000000000000000000000000000000000000"),
fromHex("000000000000000000000000000000000000000000000000"),
fromHex("1140704c328d1d5d0e30086cdf209dbd6a43b8f41518a11cc387b669b2ee6586"),
},
{
fromHex("8000000000000000000000000000000000000000000000000000000000000000"),
fromHex("000000000000000000000000000000000000000000000000"),
fromHex("7d266a7fd808cae4c02a0a70dcbfbcc250dae65ce3eae7fc210f54cc8f77df86"),
},
{
fromHex("0000000000000000000000000000000000000000000000000000000000000001"),
fromHex("000000000000000000000000000000000000000000000002"),
fromHex("e0c77ff931bb9163a5460c02ac281c2b53d792b1c43fea817e9ad275ae546963"),
},
{
fromHex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"),
fromHex("000102030405060708090a0b0c0d0e0f1011121314151617"),
fromHex("51e3ff45a895675c4b33b46c64f4a9ace110d34df6a2ceab486372bacbd3eff6"),
},
{
fromHex("24f11cce8a1b3d61e441561a696c1c1b7e173d084fd4812425435a8896a013dc"),
fromHex("d9660c5900ae19ddad28d6e06e45fe5e"),
fromHex("5966b3eec3bff1189f831f06afe4d4e3be97fa9235ec8c20d08acfbbb4e851e3"),
},
}
func TestVectors(t *testing.T) {
for i, v := range vectors {
if len(v.plaintext) == 0 {
v.plaintext = make([]byte, len(v.ciphertext))
}
var nonce [24]byte
copy(nonce[:], v.nonce)
aead, err := New(v.key)
if err != nil {
t.Error(err)
}
dst := aead.Seal(nil, nonce[:], v.plaintext, v.ad)
if !bytes.Equal(dst, v.ciphertext) {
t.Errorf("Test %d: ciphertext mismatch:\n \t got: %s\n \t want: %s", i, toHex(dst), toHex(v.ciphertext))
}
open, err := aead.Open(nil, nonce[:], dst, v.ad)
if err != nil {
t.Error(err)
}
if !bytes.Equal(open, v.plaintext) {
t.Errorf("Test %d: plaintext mismatch:\n \t got: %s\n \t want: %s", i, string(open), string(v.plaintext))
}
}
}
var vectors = []struct {
key, nonce, ad, plaintext, ciphertext []byte
}{
{
[]byte{0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f},
[]byte{0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b},
[]byte{0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7},
[]byte("Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."),
[]byte{0x45, 0x3c, 0x06, 0x93, 0xa7, 0x40, 0x7f, 0x04, 0xff, 0x4c, 0x56, 0xae, 0xdb, 0x17, 0xa3, 0xc0, 0xa1, 0xaf, 0xff, 0x01, 0x17, 0x49, 0x30, 0xfc, 0x22, 0x28, 0x7c, 0x33, 0xdb, 0xcf, 0x0a, 0xc8, 0xb8, 0x9a, 0xd9, 0x29, 0x53, 0x0a, 0x1b, 0xb3, 0xab, 0x5e, 0x69, 0xf2, 0x4c, 0x7f, 0x60, 0x70, 0xc8, 0xf8, 0x40, 0xc9, 0xab, 0xb4, 0xf6, 0x9f, 0xbf, 0xc8, 0xa7, 0xff, 0x51, 0x26, 0xfa, 0xee, 0xbb, 0xb5, 0x58, 0x05, 0xee, 0x9c, 0x1c, 0xf2, 0xce, 0x5a, 0x57, 0x26, 0x32, 0x87, 0xae, 0xc5, 0x78, 0x0f, 0x04, 0xec, 0x32, 0x4c, 0x35, 0x14, 0x12, 0x2c, 0xfc, 0x32, 0x31, 0xfc, 0x1a, 0x8b, 0x71, 0x8a, 0x62, 0x86, 0x37, 0x30, 0xa2, 0x70, 0x2b, 0xb7, 0x63, 0x66, 0x11, 0x6b, 0xed, 0x09, 0xe0, 0xfd, 0x5c, 0x6d, 0x84, 0xb6, 0xb0, 0xc1, 0xab, 0xaf, 0x24, 0x9d, 0x5d, 0xd0, 0xf7, 0xf5, 0xa7, 0xea},
},
}

Loading…
Cancel
Save