diff --git a/Package.swift b/Package.swift index 04b795686..cb1ff6c3e 100644 --- a/Package.swift +++ b/Package.swift @@ -171,7 +171,7 @@ let package = Package( "Key Agreement/ECDH.swift.gyb", "Signatures/ECDSA.swift.gyb", "Signatures/MLDSA.swift.gyb", - "Signatures/BoringSSL/MLDSA_boring.swift.gyb", + "Signatures/BoringSSL/MLDSA+externalMu_boring.swift.gyb", "KEM/MLKEM.swift.gyb", "KEM/BoringSSL/MLKEM_boring.swift.gyb", ], @@ -188,7 +188,8 @@ let package = Package( .product(name: "SwiftASN1", package: "swift-asn1"), ], exclude: privacyManifestExclude + [ - "CMakeLists.txt" + "CMakeLists.txt", + "MLDSA/MLDSA+externalMu.swift.gyb", ], resources: privacyManifestResource, swiftSettings: swiftSettings @@ -207,7 +208,8 @@ let package = Package( "CCryptoBoringSSLShims", ], exclude: privacyManifestExclude + [ - "CMakeLists.txt" + "CMakeLists.txt", + "MLDSA/BoringSSLMLDSA.swift.gyb", ], resources: privacyManifestResource ), diff --git a/Sources/Crypto/CMakeLists.txt b/Sources/Crypto/CMakeLists.txt index 1b560e783..f8bc0fc52 100644 --- a/Sources/Crypto/CMakeLists.txt +++ b/Sources/Crypto/CMakeLists.txt @@ -91,14 +91,13 @@ add_library(Crypto "Signatures/BoringSSL/ECDSASignature_boring.swift" "Signatures/BoringSSL/ECDSA_boring.swift" "Signatures/BoringSSL/EdDSA_boring.swift" - "Signatures/BoringSSL/MLDSA_boring.swift" + "Signatures/BoringSSL/MLDSA+externalMu_boring.swift" "Signatures/BoringSSL/MLDSA_wrapper.swift" "Signatures/ECDSA.swift" "Signatures/Ed25519.swift" "Signatures/MLDSA.swift" "Signatures/Signature.swift" "Util/BoringSSL/CryptoKitErrors_boring.swift" - "Util/BoringSSL/Optional+withUnsafeBytes_boring.swift" "Util/BoringSSL/RNG_boring.swift" "Util/BoringSSL/SafeCompare_boring.swift" "Util/BoringSSL/Zeroization_boring.swift" diff --git a/Sources/Crypto/Docs.docc/index.md b/Sources/Crypto/Docs.docc/index.md index 931857786..3991f7928 100644 --- a/Sources/Crypto/Docs.docc/index.md +++ b/Sources/Crypto/Docs.docc/index.md @@ -39,11 +39,25 @@ Swift Crypto is built on top of [BoringSSL](https://boringssl.googlesource.com/b - ``P256`` - ``SharedSecret`` - ``HPKE`` +- ``MLDSA65`` +- ``MLDSA87`` ### Key derivation functions - ``HKDF`` +### Key encapsulation mechanisms (KEM) + +- ``KEM`` +- ``MLKEM768`` +- ``MLKEM1024`` +- ``XWingMLKEM768X25519`` + +### KEM keys + +- ``KEMPrivateKey`` +- ``KEMPublicKey`` + ### Errors - ``CryptoKitError`` diff --git a/Sources/Crypto/Signatures/BoringSSL/MLDSA+externalMu_boring.swift b/Sources/Crypto/Signatures/BoringSSL/MLDSA+externalMu_boring.swift new file mode 100644 index 000000000..73060c04d --- /dev/null +++ b/Sources/Crypto/Signatures/BoringSSL/MLDSA+externalMu_boring.swift @@ -0,0 +1,145 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2025 Apple Inc. and the SwiftCrypto project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftCrypto project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +// MARK: - Generated file, do NOT edit +// any edits of this file WILL be overwritten and thus discarded +// see section `gyb` in `README` for details. + +#if (!CRYPTO_IN_SWIFTPM) || CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) +extension MLDSA65.PublicKey { + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameter data: The message to prehash. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + package func prehash_boring(for data: D) throws -> Data { + try self.boringSSLKey.prehash_boring(for: data) + } + + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameters: + /// - data: The message to prehash. + /// - context: The context of the message. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + package func prehash_boring(for data: D, context: C) throws -> Data { + try self.boringSSLKey.prehash_boring(for: data, context: context) + } + + private var boringSSLKey: OpenSSLMLDSAPublicKeyImpl { + get throws { + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION + try OpenSSLMLDSAPublicKeyImpl(rawRepresentation: self.rawRepresentation) + #else + self.impl + #endif + } + } +} + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) +extension MLDSA65.PrivateKey { + /// Generate a signature for the prehashed message representative (a.k.a. "external mu"). + /// + /// > Note: The message representative should be obtained via calls to ``MLDSA87/PublicKey/prehash(for:context:)``. + /// + /// - Parameter mu: The prehashed message representative (a.k.a. "external mu"). + /// + /// - Returns: The signature of the prehashed message representative. + package func signature_boring(forPrehashedMessageRepresentative mu: some DataProtocol) throws -> Data { + try self.boringSSLKey.signature_boring(forPrehashedMessageRepresentative: mu) + } + + private var boringSSLKey: OpenSSLMLDSAPrivateKeyImpl { + get throws { + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION + try OpenSSLMLDSAPrivateKeyImpl( + seedRepresentation: self.seedRepresentation, + publicKey: nil + ) + #else + self.impl + #endif + } + } +} + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) +extension MLDSA87.PublicKey { + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameter data: The message to prehash. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + package func prehash_boring(for data: D) throws -> Data { + try self.boringSSLKey.prehash_boring(for: data) + } + + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameters: + /// - data: The message to prehash. + /// - context: The context of the message. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + package func prehash_boring(for data: D, context: C) throws -> Data { + try self.boringSSLKey.prehash_boring(for: data, context: context) + } + + private var boringSSLKey: OpenSSLMLDSAPublicKeyImpl { + get throws { + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION + try OpenSSLMLDSAPublicKeyImpl(rawRepresentation: self.rawRepresentation) + #else + self.impl + #endif + } + } +} + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) +extension MLDSA87.PrivateKey { + /// Generate a signature for the prehashed message representative (a.k.a. "external mu"). + /// + /// > Note: The message representative should be obtained via calls to ``MLDSA87/PublicKey/prehash(for:context:)``. + /// + /// - Parameter mu: The prehashed message representative (a.k.a. "external mu"). + /// + /// - Returns: The signature of the prehashed message representative. + package func signature_boring(forPrehashedMessageRepresentative mu: some DataProtocol) throws -> Data { + try self.boringSSLKey.signature_boring(forPrehashedMessageRepresentative: mu) + } + + private var boringSSLKey: OpenSSLMLDSAPrivateKeyImpl { + get throws { + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION + try OpenSSLMLDSAPrivateKeyImpl( + seedRepresentation: self.seedRepresentation, + publicKey: nil + ) + #else + self.impl + #endif + } + } +} +#endif // Linux or !SwiftPM diff --git a/Sources/Crypto/Signatures/BoringSSL/MLDSA+externalMu_boring.swift.gyb b/Sources/Crypto/Signatures/BoringSSL/MLDSA+externalMu_boring.swift.gyb new file mode 100644 index 000000000..1752561f6 --- /dev/null +++ b/Sources/Crypto/Signatures/BoringSSL/MLDSA+externalMu_boring.swift.gyb @@ -0,0 +1,90 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2025 Apple Inc. and the SwiftCrypto project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftCrypto project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +// MARK: - Generated file, do NOT edit +// any edits of this file WILL be overwritten and thus discarded +// see section `gyb` in `README` for details. + +#if (!CRYPTO_IN_SWIFTPM) || CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif +%{ + parameter_sets = ["65", "87"] +}% +% for parameter_set in parameter_sets: + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) +extension MLDSA${parameter_set}.PublicKey { + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameter data: The message to prehash. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + package func prehash_boring(for data: D) throws -> Data { + try self.boringSSLKey.prehash_boring(for: data) + } + + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameters: + /// - data: The message to prehash. + /// - context: The context of the message. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + package func prehash_boring(for data: D, context: C) throws -> Data { + try self.boringSSLKey.prehash_boring(for: data, context: context) + } + + private var boringSSLKey: OpenSSLMLDSAPublicKeyImpl { + get throws { + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION + try OpenSSLMLDSAPublicKeyImpl(rawRepresentation: self.rawRepresentation) + #else + self.impl + #endif + } + } +} + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) +extension MLDSA${parameter_set}.PrivateKey { + /// Generate a signature for the prehashed message representative (a.k.a. "external mu"). + /// + /// > Note: The message representative should be obtained via calls to ``MLDSA87/PublicKey/prehash(for:context:)``. + /// + /// - Parameter mu: The prehashed message representative (a.k.a. "external mu"). + /// + /// - Returns: The signature of the prehashed message representative. + package func signature_boring(forPrehashedMessageRepresentative mu: some DataProtocol) throws -> Data { + try self.boringSSLKey.signature_boring(forPrehashedMessageRepresentative: mu) + } + + private var boringSSLKey: OpenSSLMLDSAPrivateKeyImpl { + get throws { + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION + try OpenSSLMLDSAPrivateKeyImpl( + seedRepresentation: self.seedRepresentation, + publicKey: nil + ) + #else + self.impl + #endif + } + } +} +% end +#endif // Linux or !SwiftPM diff --git a/Sources/Crypto/Signatures/BoringSSL/MLDSA_boring.swift b/Sources/Crypto/Signatures/BoringSSL/MLDSA_boring.swift deleted file mode 100644 index 05150b49f..000000000 --- a/Sources/Crypto/Signatures/BoringSSL/MLDSA_boring.swift +++ /dev/null @@ -1,645 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the SwiftCrypto open source project -// -// Copyright (c) 2024 Apple Inc. and the SwiftCrypto project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of SwiftCrypto project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -#if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API -@_exported import CryptoKit -#else -@_implementationOnly import CCryptoBoringSSL -#if canImport(FoundationEssentials) -import FoundationEssentials -#else -import Foundation -#endif - -// MARK: - Generated file, do NOT edit -// any edits of this file WILL be overwritten and thus discarded -// see section `gyb` in `README` for details. - -@_implementationOnly import CCryptoBoringSSL -#if canImport(FoundationEssentials) -import FoundationEssentials -#else -import Foundation -#endif - -@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) -extension MLDSA65 { - /// A ML-DSA-65 private key. - struct InternalPrivateKey: @unchecked Sendable { - private var backing: Backing - - /// Initialize a ML-DSA-65 private key from a random seed. - init() throws { - self.backing = try Backing() - } - - /// Initialize a ML-DSA-65 private key from a seed. - /// - /// - Parameter seedRepresentation: The seed to use to generate the private key. - /// - /// - Throws: `CryptoKitError.incorrectKeySize` if the seed is not 32 bytes long. - init(seedRepresentation: some DataProtocol) throws { - self.backing = try Backing(seedRepresentation: seedRepresentation) - } - - /// The seed from which this private key was generated. - var seedRepresentation: Data { - self.backing.seed - } - - /// The public key associated with this private key. - var publicKey: InternalPublicKey { - self.backing.publicKey - } - - /// Generate a signature for the given data. - /// - /// - Parameter data: The message to sign. - /// - /// - Returns: The signature of the message. - func signature(for data: D) throws -> Data { - let context: Data? = nil - return try self.backing.signature(for: data, context: context) - } - - /// Generate a signature for the given data. - /// - /// - Parameters: - /// - data: The message to sign. - /// - context: The context to use for the signature. - /// - /// - Returns: The signature of the message. - func signature(for data: D, context: C) throws -> Data { - try self.backing.signature(for: data, context: context) - } - - /// The size of the private key in bytes. - static let byteCount = Backing.byteCount - - fileprivate final class Backing { - fileprivate var key: MLDSA65_private_key - var seed: Data - - /// Initialize a ML-DSA-65 private key from a random seed. - init() throws { - // We have to initialize all members before `self` is captured by the closure - self.key = .init() - self.seed = Data() - - self.seed = try withUnsafeTemporaryAllocation( - of: UInt8.self, - capacity: MLDSA.seedByteCount - ) { seedPtr in - try withUnsafeTemporaryAllocation( - of: UInt8.self, - capacity: MLDSA65.InternalPublicKey.Backing.byteCount - ) { publicKeyPtr in - guard - CCryptoBoringSSL_MLDSA65_generate_key( - publicKeyPtr.baseAddress, - seedPtr.baseAddress, - &self.key - ) == 1 - else { - throw CryptoKitError.internalBoringSSLError() - } - - return Data(bytes: seedPtr.baseAddress!, count: MLDSA.seedByteCount) - } - } - } - - /// Initialize a ML-DSA-65 private key from a seed. - /// - /// - Parameter seedRepresentation: The seed to use to generate the private key. - /// - /// - Throws: `CryptoKitError.incorrectKeySize` if the seed is not 32 bytes long. - init(seedRepresentation: some DataProtocol) throws { - guard seedRepresentation.count == MLDSA.seedByteCount else { - throw CryptoKitError.incorrectKeySize - } - - self.key = .init() - self.seed = Data(seedRepresentation) - - guard - self.seed.withUnsafeBytes({ seedPtr in - CCryptoBoringSSL_MLDSA65_private_key_from_seed( - &self.key, - seedPtr.baseAddress, - MLDSA.seedByteCount - ) - }) == 1 - else { - throw CryptoKitError.internalBoringSSLError() - } - } - - /// The public key associated with this private key. - var publicKey: InternalPublicKey { - InternalPublicKey(privateKeyBacking: self) - } - - /// Generate a signature for the given data. - /// - /// - Parameters: - /// - data: The message to sign. - /// - context: The context to use for the signature. - /// - /// - Returns: The signature of the message. - func signature(for data: D, context: C?) throws -> Data { - var signature = Data(repeating: 0, count: MLDSA65.signatureByteCount) - - let rc: CInt = signature.withUnsafeMutableBytes { signaturePtr in - let bytes: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data) - return bytes.withUnsafeBytes { dataPtr in - context.withUnsafeBytes { contextPtr in - CCryptoBoringSSL_MLDSA65_sign( - signaturePtr.baseAddress, - &self.key, - dataPtr.baseAddress, - dataPtr.count, - contextPtr.baseAddress, - contextPtr.count - ) - } - } - } - - guard rc == 1 else { - throw CryptoKitError.internalBoringSSLError() - } - - return signature - } - - /// The size of the private key in bytes. - static let byteCount = Int(MLDSA65_PRIVATE_KEY_BYTES) - } - } -} - -@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) -extension MLDSA65 { - /// A ML-DSA-65 public key. - struct InternalPublicKey: @unchecked Sendable { - private var backing: Backing - - fileprivate init(privateKeyBacking: InternalPrivateKey.Backing) { - self.backing = Backing(privateKeyBacking: privateKeyBacking) - } - - /// Initialize a ML-DSA-65 public key from a raw representation. - /// - /// - Parameter rawRepresentation: The public key bytes. - /// - /// - Throws: `CryptoKitError.incorrectKeySize` if the raw representation is not the correct size. - init(rawRepresentation: some DataProtocol) throws { - self.backing = try Backing(rawRepresentation: rawRepresentation) - } - - /// The raw binary representation of the public key. - var rawRepresentation: Data { - self.backing.rawRepresentation - } - - /// Verify a signature for the given data. - /// - /// - Parameters: - /// - signature: The signature to verify. - /// - data: The message to verify the signature against. - /// - /// - Returns: `true` if the signature is valid, `false` otherwise. - func isValidSignature(_ signature: S, for data: D) -> Bool { - let context: Data? = nil - return self.backing.isValidSignature(signature, for: data, context: context) - } - - /// Verify a signature for the given data. - /// - /// - Parameters: - /// - signature: The signature to verify. - /// - data: The message to verify the signature against. - /// - context: The context to use for the signature verification. - /// - /// - Returns: `true` if the signature is valid, `false` otherwise. - func isValidSignature( - _ signature: S, - for data: D, - context: C - ) -> Bool { - self.backing.isValidSignature(signature, for: data, context: context) - } - - /// The size of the public key in bytes. - static let byteCount = Backing.byteCount - - fileprivate final class Backing { - private var key: MLDSA65_public_key - - init(privateKeyBacking: InternalPrivateKey.Backing) { - self.key = .init() - CCryptoBoringSSL_MLDSA65_public_from_private(&self.key, &privateKeyBacking.key) - } - - /// Initialize a ML-DSA-65 public key from a raw representation. - /// - /// - Parameter rawRepresentation: The public key bytes. - /// - /// - Throws: `CryptoKitError.incorrectKeySize` if the raw representation is not the correct size. - init(rawRepresentation: some DataProtocol) throws { - guard rawRepresentation.count == MLDSA65.InternalPublicKey.Backing.byteCount else { - throw CryptoKitError.incorrectKeySize - } - - self.key = .init() - - let bytes: ContiguousBytes = - rawRepresentation.regions.count == 1 - ? rawRepresentation.regions.first! - : Array(rawRepresentation) - try bytes.withUnsafeBytes { rawBuffer in - try rawBuffer.withMemoryRebound(to: UInt8.self) { buffer in - var cbs = CBS(data: buffer.baseAddress, len: buffer.count) - guard CCryptoBoringSSL_MLDSA65_parse_public_key(&self.key, &cbs) == 1 else { - throw CryptoKitError.internalBoringSSLError() - } - } - } - } - - /// The raw binary representation of the public key. - var rawRepresentation: Data { - var cbb = CBB() - // The following BoringSSL functions can only fail on allocation failure, which we define as impossible. - CCryptoBoringSSL_CBB_init(&cbb, MLDSA65.InternalPublicKey.Backing.byteCount) - defer { CCryptoBoringSSL_CBB_cleanup(&cbb) } - CCryptoBoringSSL_MLDSA65_marshal_public_key(&cbb, &self.key) - return Data(bytes: CCryptoBoringSSL_CBB_data(&cbb), count: CCryptoBoringSSL_CBB_len(&cbb)) - } - - /// Verify a signature for the given data. - /// - /// - Parameters: - /// - signature: The signature to verify. - /// - data: The message to verify the signature against. - /// - context: The context to use for the signature verification. - /// - /// - Returns: `true` if the signature is valid, `false` otherwise. - func isValidSignature( - _ signature: S, - for data: D, - context: C? - ) -> Bool { - let signatureBytes: ContiguousBytes = - signature.regions.count == 1 ? signature.regions.first! : Array(signature) - return signatureBytes.withUnsafeBytes { signaturePtr in - let dataBytes: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data) - let rc: CInt = dataBytes.withUnsafeBytes { dataPtr in - context.withUnsafeBytes { contextPtr in - CCryptoBoringSSL_MLDSA65_verify( - &self.key, - signaturePtr.baseAddress, - signaturePtr.count, - dataPtr.baseAddress, - dataPtr.count, - contextPtr.baseAddress, - contextPtr.count - ) - } - } - return rc == 1 - } - } - - /// The size of the public key in bytes. - static let byteCount = Int(MLDSA65_PUBLIC_KEY_BYTES) - } - } -} - -@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) -extension MLDSA65 { - /// The size of the signature in bytes. - private static let signatureByteCount = Int(MLDSA65_SIGNATURE_BYTES) -} - -@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) -extension MLDSA87 { - /// A ML-DSA-87 private key. - struct InternalPrivateKey: @unchecked Sendable { - private var backing: Backing - - /// Initialize a ML-DSA-87 private key from a random seed. - init() throws { - self.backing = try Backing() - } - - /// Initialize a ML-DSA-87 private key from a seed. - /// - /// - Parameter seedRepresentation: The seed to use to generate the private key. - /// - /// - Throws: `CryptoKitError.incorrectKeySize` if the seed is not 32 bytes long. - init(seedRepresentation: some DataProtocol) throws { - self.backing = try Backing(seedRepresentation: seedRepresentation) - } - - /// The seed from which this private key was generated. - var seedRepresentation: Data { - self.backing.seed - } - - /// The public key associated with this private key. - var publicKey: InternalPublicKey { - self.backing.publicKey - } - - /// Generate a signature for the given data. - /// - /// - Parameter data: The message to sign. - /// - /// - Returns: The signature of the message. - func signature(for data: D) throws -> Data { - let context: Data? = nil - return try self.backing.signature(for: data, context: context) - } - - /// Generate a signature for the given data. - /// - /// - Parameters: - /// - data: The message to sign. - /// - context: The context to use for the signature. - /// - /// - Returns: The signature of the message. - func signature(for data: D, context: C) throws -> Data { - try self.backing.signature(for: data, context: context) - } - - /// The size of the private key in bytes. - static let byteCount = Backing.byteCount - - fileprivate final class Backing { - fileprivate var key: MLDSA87_private_key - var seed: Data - - /// Initialize a ML-DSA-87 private key from a random seed. - init() throws { - // We have to initialize all members before `self` is captured by the closure - self.key = .init() - self.seed = Data() - - self.seed = try withUnsafeTemporaryAllocation( - of: UInt8.self, - capacity: MLDSA.seedByteCount - ) { seedPtr in - try withUnsafeTemporaryAllocation( - of: UInt8.self, - capacity: MLDSA87.InternalPublicKey.Backing.byteCount - ) { publicKeyPtr in - guard - CCryptoBoringSSL_MLDSA87_generate_key( - publicKeyPtr.baseAddress, - seedPtr.baseAddress, - &self.key - ) == 1 - else { - throw CryptoKitError.internalBoringSSLError() - } - - return Data(bytes: seedPtr.baseAddress!, count: MLDSA.seedByteCount) - } - } - } - - /// Initialize a ML-DSA-87 private key from a seed. - /// - /// - Parameter seedRepresentation: The seed to use to generate the private key. - /// - /// - Throws: `CryptoKitError.incorrectKeySize` if the seed is not 32 bytes long. - init(seedRepresentation: some DataProtocol) throws { - guard seedRepresentation.count == MLDSA.seedByteCount else { - throw CryptoKitError.incorrectKeySize - } - - self.key = .init() - self.seed = Data(seedRepresentation) - - guard - self.seed.withUnsafeBytes({ seedPtr in - CCryptoBoringSSL_MLDSA87_private_key_from_seed( - &self.key, - seedPtr.baseAddress, - MLDSA.seedByteCount - ) - }) == 1 - else { - throw CryptoKitError.internalBoringSSLError() - } - } - - /// The public key associated with this private key. - var publicKey: InternalPublicKey { - InternalPublicKey(privateKeyBacking: self) - } - - /// Generate a signature for the given data. - /// - /// - Parameters: - /// - data: The message to sign. - /// - context: The context to use for the signature. - /// - /// - Returns: The signature of the message. - func signature(for data: D, context: C?) throws -> Data { - var signature = Data(repeating: 0, count: MLDSA87.signatureByteCount) - - let rc: CInt = signature.withUnsafeMutableBytes { signaturePtr in - let bytes: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data) - return bytes.withUnsafeBytes { dataPtr in - context.withUnsafeBytes { contextPtr in - CCryptoBoringSSL_MLDSA87_sign( - signaturePtr.baseAddress, - &self.key, - dataPtr.baseAddress, - dataPtr.count, - contextPtr.baseAddress, - contextPtr.count - ) - } - } - } - - guard rc == 1 else { - throw CryptoKitError.internalBoringSSLError() - } - - return signature - } - - /// The size of the private key in bytes. - static let byteCount = Int(MLDSA87_PRIVATE_KEY_BYTES) - } - } -} - -@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) -extension MLDSA87 { - /// A ML-DSA-87 public key. - struct InternalPublicKey: @unchecked Sendable { - private var backing: Backing - - fileprivate init(privateKeyBacking: InternalPrivateKey.Backing) { - self.backing = Backing(privateKeyBacking: privateKeyBacking) - } - - /// Initialize a ML-DSA-87 public key from a raw representation. - /// - /// - Parameter rawRepresentation: The public key bytes. - /// - /// - Throws: `CryptoKitError.incorrectKeySize` if the raw representation is not the correct size. - init(rawRepresentation: some DataProtocol) throws { - self.backing = try Backing(rawRepresentation: rawRepresentation) - } - - /// The raw binary representation of the public key. - var rawRepresentation: Data { - self.backing.rawRepresentation - } - - /// Verify a signature for the given data. - /// - /// - Parameters: - /// - signature: The signature to verify. - /// - data: The message to verify the signature against. - /// - /// - Returns: `true` if the signature is valid, `false` otherwise. - func isValidSignature(_ signature: S, for data: D) -> Bool { - let context: Data? = nil - return self.backing.isValidSignature(signature, for: data, context: context) - } - - /// Verify a signature for the given data. - /// - /// - Parameters: - /// - signature: The signature to verify. - /// - data: The message to verify the signature against. - /// - context: The context to use for the signature verification. - /// - /// - Returns: `true` if the signature is valid, `false` otherwise. - func isValidSignature( - _ signature: S, - for data: D, - context: C - ) -> Bool { - self.backing.isValidSignature(signature, for: data, context: context) - } - - /// The size of the public key in bytes. - static let byteCount = Backing.byteCount - - fileprivate final class Backing { - private var key: MLDSA87_public_key - - init(privateKeyBacking: InternalPrivateKey.Backing) { - self.key = .init() - CCryptoBoringSSL_MLDSA87_public_from_private(&self.key, &privateKeyBacking.key) - } - - /// Initialize a ML-DSA-87 public key from a raw representation. - /// - /// - Parameter rawRepresentation: The public key bytes. - /// - /// - Throws: `CryptoKitError.incorrectKeySize` if the raw representation is not the correct size. - init(rawRepresentation: some DataProtocol) throws { - guard rawRepresentation.count == MLDSA87.InternalPublicKey.Backing.byteCount else { - throw CryptoKitError.incorrectKeySize - } - - self.key = .init() - - let bytes: ContiguousBytes = - rawRepresentation.regions.count == 1 - ? rawRepresentation.regions.first! - : Array(rawRepresentation) - try bytes.withUnsafeBytes { rawBuffer in - try rawBuffer.withMemoryRebound(to: UInt8.self) { buffer in - var cbs = CBS(data: buffer.baseAddress, len: buffer.count) - guard CCryptoBoringSSL_MLDSA87_parse_public_key(&self.key, &cbs) == 1 else { - throw CryptoKitError.internalBoringSSLError() - } - } - } - } - - /// The raw binary representation of the public key. - var rawRepresentation: Data { - var cbb = CBB() - // The following BoringSSL functions can only fail on allocation failure, which we define as impossible. - CCryptoBoringSSL_CBB_init(&cbb, MLDSA87.InternalPublicKey.Backing.byteCount) - defer { CCryptoBoringSSL_CBB_cleanup(&cbb) } - CCryptoBoringSSL_MLDSA87_marshal_public_key(&cbb, &self.key) - return Data(bytes: CCryptoBoringSSL_CBB_data(&cbb), count: CCryptoBoringSSL_CBB_len(&cbb)) - } - - /// Verify a signature for the given data. - /// - /// - Parameters: - /// - signature: The signature to verify. - /// - data: The message to verify the signature against. - /// - context: The context to use for the signature verification. - /// - /// - Returns: `true` if the signature is valid, `false` otherwise. - func isValidSignature( - _ signature: S, - for data: D, - context: C? - ) -> Bool { - let signatureBytes: ContiguousBytes = - signature.regions.count == 1 ? signature.regions.first! : Array(signature) - return signatureBytes.withUnsafeBytes { signaturePtr in - let dataBytes: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data) - let rc: CInt = dataBytes.withUnsafeBytes { dataPtr in - context.withUnsafeBytes { contextPtr in - CCryptoBoringSSL_MLDSA87_verify( - &self.key, - signaturePtr.baseAddress, - signaturePtr.count, - dataPtr.baseAddress, - dataPtr.count, - contextPtr.baseAddress, - contextPtr.count - ) - } - } - return rc == 1 - } - } - - /// The size of the public key in bytes. - static let byteCount = Int(MLDSA87_PUBLIC_KEY_BYTES) - } - } -} - -@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) -extension MLDSA87 { - /// The size of the signature in bytes. - private static let signatureByteCount = Int(MLDSA87_SIGNATURE_BYTES) -} - -enum MLDSA { - /// The size of the seed in bytes. - static let seedByteCount = 32 -} - -#endif // CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API diff --git a/Sources/Crypto/Signatures/BoringSSL/MLDSA_wrapper.swift b/Sources/Crypto/Signatures/BoringSSL/MLDSA_wrapper.swift index a02db5076..1ab91610e 100644 --- a/Sources/Crypto/Signatures/BoringSSL/MLDSA_wrapper.swift +++ b/Sources/Crypto/Signatures/BoringSSL/MLDSA_wrapper.swift @@ -16,6 +16,7 @@ @_exported import CryptoKit #else @_implementationOnly import CCryptoBoringSSL +import CryptoBoringWrapper #if canImport(FoundationEssentials) import FoundationEssentials #else @@ -34,6 +35,8 @@ protocol BoringSSLBackedMLDSAPrivateKey: Sendable { func signature(for data: D, context: C) throws -> Data + func signature(forPrehashedMessageRepresentative mu: some DataProtocol) throws -> Data + var publicKey: AssociatedPublicKey { get } var seedRepresentation: Data { get } @@ -48,6 +51,10 @@ protocol BoringSSLBackedMLDSAPublicKey: Sendable { func isValidSignature(_: S, for data: D, context: C) -> Bool var rawRepresentation: Data { get } + + func prehash(for data: D) throws -> Data + + func prehash(for data: D, context: C) throws -> Data } @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) @@ -59,27 +66,27 @@ protocol BoringSSLBackedMLDSAParameters { @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) extension MLDSA65: BoringSSLBackedMLDSAParameters { - typealias BackingPrivateKey = MLDSA65.InternalPrivateKey - typealias BackingPublicKey = MLDSA65.InternalPublicKey + typealias BackingPrivateKey = BoringSSLMLDSA65.InternalPrivateKey + typealias BackingPublicKey = BoringSSLMLDSA65.InternalPublicKey } @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) extension MLDSA87: BoringSSLBackedMLDSAParameters { - typealias BackingPrivateKey = MLDSA87.InternalPrivateKey - typealias BackingPublicKey = MLDSA87.InternalPublicKey + typealias BackingPrivateKey = BoringSSLMLDSA87.InternalPrivateKey + typealias BackingPublicKey = BoringSSLMLDSA87.InternalPublicKey } @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) -extension MLDSA65.InternalPrivateKey: BoringSSLBackedMLDSAPrivateKey {} +extension BoringSSLMLDSA65.InternalPrivateKey: BoringSSLBackedMLDSAPrivateKey {} @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) -extension MLDSA65.InternalPublicKey: BoringSSLBackedMLDSAPublicKey {} +extension BoringSSLMLDSA65.InternalPublicKey: BoringSSLBackedMLDSAPublicKey {} @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) -extension MLDSA87.InternalPrivateKey: BoringSSLBackedMLDSAPrivateKey {} +extension BoringSSLMLDSA87.InternalPrivateKey: BoringSSLBackedMLDSAPrivateKey {} @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) -extension MLDSA87.InternalPublicKey: BoringSSLBackedMLDSAPublicKey {} +extension BoringSSLMLDSA87.InternalPublicKey: BoringSSLBackedMLDSAPublicKey {} @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) struct OpenSSLMLDSAPrivateKeyImpl { @@ -117,6 +124,10 @@ struct OpenSSLMLDSAPrivateKeyImpl { try self.backing.signature(for: data, context: context) } + func signature_boring(forPrehashedMessageRepresentative mu: some DataProtocol) throws -> Data { + try self.backing.signature(forPrehashedMessageRepresentative: mu) + } + var publicKey: OpenSSLMLDSAPublicKeyImpl { .init(backing: self.backing.publicKey) } @@ -135,7 +146,7 @@ struct OpenSSLMLDSAPrivateKeyImpl { } static var seedSize: Int { - MLDSA.seedByteCount + BoringSSLMLDSA.seedByteCount } } @@ -169,6 +180,14 @@ struct OpenSSLMLDSAPublicKeyImpl { var rawRepresentation: Data { self.backing.rawRepresentation } + + func prehash_boring(for data: D) throws -> Data { + try self.backing.prehash(for: data) + } + + func prehash_boring(for data: D, context: C) throws -> Data { + try self.backing.prehash(for: data, context: context) + } } #endif // CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API diff --git a/Sources/Crypto/Signatures/MLDSA.swift b/Sources/Crypto/Signatures/MLDSA.swift index b3f2cce07..17c7a78dd 100644 --- a/Sources/Crypto/Signatures/MLDSA.swift +++ b/Sources/Crypto/Signatures/MLDSA.swift @@ -11,6 +11,11 @@ // SPDX-License-Identifier: Apache-2.0 // //===----------------------------------------------------------------------===// + +// MARK: - Generated file, do NOT edit +// any edits of this file WILL be overwritten and thus discarded +// see section `gyb` in `README` for details. + #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else @@ -32,7 +37,6 @@ typealias MLDSAPublicKeyImpl = OpenSSLMLDSAPublicKeyImpl typealias MLDSAPrivateKeyImpl = OpenSSLMLDSAPrivateKeyImpl #endif - /// The MLDSA65 Digital Signature Algorithm @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) public enum MLDSA65: Sendable {} @@ -47,7 +51,7 @@ extension MLDSA65 { internal init(_ impl: MLDSAPublicKeyImpl) { self.impl = impl } - + /// Parses a public key from a serialized representation. /// /// - Parameter rawRepresentation: The public key, in the FIPS 204 standard serialization format. @@ -144,7 +148,7 @@ extension MLDSA65 { public func signature(for data: D, context: C) throws -> Data { return try impl.signature(for: data, context: context) } - + /// The associated public key. public var publicKey: PublicKey { get { @@ -180,7 +184,6 @@ extension MLDSA65 { } } - /// The MLDSA87 Digital Signature Algorithm @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) public enum MLDSA87: Sendable {} @@ -195,7 +198,7 @@ extension MLDSA87 { internal init(_ impl: MLDSAPublicKeyImpl) { self.impl = impl } - + /// Parses a public key from a serialized representation. /// /// - Parameter rawRepresentation: The public key, in the FIPS 204 standard serialization format. @@ -292,7 +295,7 @@ extension MLDSA87 { public func signature(for data: D, context: C) throws -> Data { return try impl.signature(for: data, context: context) } - + /// The associated public key. public var publicKey: PublicKey { get { @@ -328,5 +331,4 @@ extension MLDSA87 { } } - -#endif // Linux or !SwiftPM +#endif // Linux or !SwiftPM diff --git a/Sources/Crypto/Signatures/MLDSA.swift.gyb b/Sources/Crypto/Signatures/MLDSA.swift.gyb index 5378503a1..368850957 100644 --- a/Sources/Crypto/Signatures/MLDSA.swift.gyb +++ b/Sources/Crypto/Signatures/MLDSA.swift.gyb @@ -11,6 +11,11 @@ // SPDX-License-Identifier: Apache-2.0 // //===----------------------------------------------------------------------===// + +// MARK: - Generated file, do NOT edit +// any edits of this file WILL be overwritten and thus discarded +// see section `gyb` in `README` for details. + #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else @@ -24,10 +29,14 @@ public import Foundation }% #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) typealias MLDSAPublicKeyImpl = CorecryptoMLDSAPublicKeyImpl +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) typealias MLDSAPrivateKeyImpl = CorecryptoMLDSAPrivateKeyImpl #else +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) typealias MLDSAPublicKeyImpl = OpenSSLMLDSAPublicKeyImpl +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) typealias MLDSAPrivateKeyImpl = OpenSSLMLDSAPrivateKeyImpl #endif @@ -35,15 +44,21 @@ typealias MLDSAPrivateKeyImpl = OpenSSLMLDSAPrivateKeyImpl %{ NAME = MLDSA_VARIANT["name"] }% - /// The ${NAME} Digital Signature Algorithm +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) public enum ${NAME}: Sendable {} +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) extension ${NAME} { /// The public key for ${NAME}. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) public struct PublicKey: Sendable { var impl: MLDSAPublicKeyImpl<${NAME}> - + + internal init(_ impl: MLDSAPublicKeyImpl<${NAME}>) { + self.impl = impl + } + /// Parses a public key from a serialized representation. /// /// - Parameter rawRepresentation: The public key, in the FIPS 204 standard serialization format. @@ -82,6 +97,7 @@ extension ${NAME} { } /// The private key for ${NAME}. + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) public struct PrivateKey: Signer, Sendable { internal let impl: MLDSAPrivateKeyImpl<${NAME}> @@ -139,7 +155,7 @@ extension ${NAME} { public func signature(for data: D, context: C) throws -> Data { return try impl.signature(for: data, context: context) } - + /// The associated public key. public var publicKey: PublicKey { get { @@ -176,5 +192,4 @@ extension ${NAME} { } % end - -#endif // Linux or !SwiftPM +#endif // Linux or !SwiftPM diff --git a/Sources/CryptoBoringWrapper/CMakeLists.txt b/Sources/CryptoBoringWrapper/CMakeLists.txt index 980f33c4d..201a0357d 100644 --- a/Sources/CryptoBoringWrapper/CMakeLists.txt +++ b/Sources/CryptoBoringWrapper/CMakeLists.txt @@ -17,8 +17,10 @@ add_library(CryptoBoringWrapper STATIC "CryptoKitErrors_boring.swift" "EC/EllipticCurve.swift" "EC/EllipticCurvePoint.swift" + "MLDSA/BoringSSLMLDSA.swift" "Util/ArbitraryPrecisionInteger.swift" "Util/FiniteFieldArithmeticContext.swift" + "Util/Optional+withUnsafeBytes.swift" "Util/RandomBytes.swift" ) diff --git a/Sources/CryptoBoringWrapper/MLDSA/BoringSSLMLDSA.swift b/Sources/CryptoBoringWrapper/MLDSA/BoringSSLMLDSA.swift new file mode 100644 index 000000000..7791bc977 --- /dev/null +++ b/Sources/CryptoBoringWrapper/MLDSA/BoringSSLMLDSA.swift @@ -0,0 +1,1245 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2024 Apple Inc. and the SwiftCrypto project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftCrypto project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +@_implementationOnly import CCryptoBoringSSL + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + +// MARK: - Generated file, do NOT edit +// any edits of this file WILL be overwritten and thus discarded +// see section `gyb` in `README` for details. + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) +package enum BoringSSLMLDSA65: Sendable {} + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) +extension BoringSSLMLDSA65 { + /// A ML-DSA-65 private key. + package struct InternalPrivateKey: @unchecked Sendable { + private var backing: Backing + + /// Initialize a ML-DSA-65 private key from a random seed. + package init() throws { + self.backing = try Backing() + } + + /// Initialize a ML-DSA-65 private key from a seed. + /// + /// - Parameter seedRepresentation: The seed to use to generate the private key. + /// + /// - Throws: `CryptoBoringWrapperError.incorrectKeySize` if the seed is not 32 bytes long. + package init(seedRepresentation: some DataProtocol) throws { + self.backing = try Backing(seedRepresentation: seedRepresentation) + } + + /// The seed from which this private key was generated. + package var seedRepresentation: Data { + self.backing.seed + } + + /// The public key associated with this private key. + package var publicKey: InternalPublicKey { + self.backing.publicKey + } + + /// Generate a signature for the given data. + /// + /// - Parameter data: The message to sign. + /// + /// - Returns: The signature of the message. + package func signature(for data: D) throws -> Data { + let context: Data? = nil + return try self.backing.signature(for: data, context: context) + } + + /// Generate a signature for the given data. + /// + /// - Parameters: + /// - data: The message to sign. + /// - context: The context to use for the signature. + /// + /// - Returns: The signature of the message. + package func signature(for data: D, context: C) throws -> Data { + try self.backing.signature(for: data, context: context) + } + + /// Generate a signature for the prehashed message representative (a.k.a. "external mu"). + /// + /// > Note: The message representative should be obtained via calls to ``BoringSSLMLDSA65/PublicKey/prehash(for:context:)``. + /// + /// - Parameter mu: The prehashed message representative (a.k.a. "external mu"). + /// + /// - Returns: The signature of the prehashed message representative. + package func signature(forPrehashedMessageRepresentative mu: some DataProtocol) throws -> Data { + try self.backing.signature(forPrehashedMessageRepresentative: mu) + } + + /// The size of the private key in bytes. + static let byteCount = Backing.byteCount + + fileprivate final class Backing { + fileprivate var key: MLDSA65_private_key + var seed: Data + + /// Initialize a ML-DSA-65 private key from a random seed. + init() throws { + // We have to initialize all members before `self` is captured by the closure + self.key = .init() + self.seed = Data() + + self.seed = try withUnsafeTemporaryAllocation( + of: UInt8.self, + capacity: BoringSSLMLDSA.seedByteCount + ) { seedPtr in + try withUnsafeTemporaryAllocation( + of: UInt8.self, + capacity: BoringSSLMLDSA65.InternalPublicKey.Backing.byteCount + ) { publicKeyPtr in + guard + CCryptoBoringSSL_MLDSA65_generate_key( + publicKeyPtr.baseAddress, + seedPtr.baseAddress, + &self.key + ) == 1 + else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + + return Data(bytes: seedPtr.baseAddress!, count: BoringSSLMLDSA.seedByteCount) + } + } + } + + /// Initialize a ML-DSA-65 private key from a seed. + /// + /// - Parameter seedRepresentation: The seed to use to generate the private key. + /// + /// - Throws: `CryptoBoringWrapperError.incorrectKeySize` if the seed is not 32 bytes long. + init(seedRepresentation: some DataProtocol) throws { + guard seedRepresentation.count == BoringSSLMLDSA.seedByteCount else { + throw CryptoBoringWrapperError.incorrectKeySize + } + + self.key = .init() + self.seed = Data(seedRepresentation) + + guard + self.seed.withUnsafeBytes({ seedPtr in + CCryptoBoringSSL_MLDSA65_private_key_from_seed( + &self.key, + seedPtr.baseAddress, + BoringSSLMLDSA.seedByteCount + ) + }) == 1 + else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + } + + /// The public key associated with this private key. + var publicKey: InternalPublicKey { + InternalPublicKey(privateKeyBacking: self) + } + + /// Generate a signature for the given data. + /// + /// - Parameters: + /// - data: The message to sign. + /// - context: The context to use for the signature. + /// + /// - Returns: The signature of the message. + func signature(for data: D, context: C?) throws -> Data { + var signature = Data(repeating: 0, count: BoringSSLMLDSA65.signatureByteCount) + + let rc: CInt = signature.withUnsafeMutableBytes { signaturePtr in + let bytes: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data) + return bytes.withUnsafeBytes { dataPtr in + context.withUnsafeBytes { contextPtr in + CCryptoBoringSSL_MLDSA65_sign( + signaturePtr.baseAddress, + &self.key, + dataPtr.baseAddress, + dataPtr.count, + contextPtr.baseAddress, + contextPtr.count + ) + } + } + } + + guard rc == 1 else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + + return signature + } + + /// Generate a signature for the prehashed message representative (a.k.a. "external mu"). + /// + /// > Note: The message representative should be obtained via calls to ``BoringSSLMLDSA65/PublicKey/prehash(for:context:)``. + /// + /// - Parameter mu: The prehashed message representative (a.k.a. "external mu"). + /// + /// - Returns: The signature of the prehashed message representative. + func signature(forPrehashedMessageRepresentative mu: some DataProtocol) throws -> Data { + guard mu.count == BoringSSLMLDSA.muByteCount else { + throw CryptoBoringWrapperError.incorrectParameterSize + } + + var signature = Data(repeating: 0, count: BoringSSLMLDSA65.signatureByteCount) + + let rc: CInt = signature.withUnsafeMutableBytes { signaturePtr in + let muBytes: ContiguousBytes = mu.regions.count == 1 ? mu.regions.first! : Array(mu) + return muBytes.withUnsafeBytes { muPtr in + CCryptoBoringSSL_MLDSA65_sign_message_representative( + signaturePtr.baseAddress, + &self.key, + muPtr.baseAddress + ) + } + } + + guard rc == 1 else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + + return signature + } + + /// The size of the private key in bytes. + static let byteCount = Int(MLDSA65_PRIVATE_KEY_BYTES) + } + } +} + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) +extension BoringSSLMLDSA65 { + /// A ML-DSA-65 public key. + package struct InternalPublicKey: @unchecked Sendable { + private var backing: Backing + + fileprivate init(privateKeyBacking: InternalPrivateKey.Backing) { + self.backing = Backing(privateKeyBacking: privateKeyBacking) + } + + /// Initialize a ML-DSA-65 public key from a raw representation. + /// + /// - Parameter rawRepresentation: The public key bytes. + /// + /// - Throws: `CryptoBoringWrapperError.incorrectKeySize` if the raw representation is not the correct size. + package init(rawRepresentation: some DataProtocol) throws { + self.backing = try Backing(rawRepresentation: rawRepresentation) + } + + /// The raw binary representation of the public key. + package var rawRepresentation: Data { + self.backing.rawRepresentation + } + + /// Verify a signature for the given data. + /// + /// - Parameters: + /// - signature: The signature to verify. + /// - data: The message to verify the signature against. + /// + /// - Returns: `true` if the signature is valid, `false` otherwise. + package func isValidSignature(_ signature: S, for data: D) -> Bool { + let context: Data? = nil + return self.backing.isValidSignature(signature, for: data, context: context) + } + + /// Verify a signature for the given data. + /// + /// - Parameters: + /// - signature: The signature to verify. + /// - data: The message to verify the signature against. + /// - context: The context to use for the signature verification. + /// + /// - Returns: `true` if the signature is valid, `false` otherwise. + package func isValidSignature( + _ signature: S, + for data: D, + context: C + ) -> Bool { + self.backing.isValidSignature(signature, for: data, context: context) + } + + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameter data: The message to prehash. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + package func prehash(for data: D) throws -> Data { + let context: Data? = nil + return try self.backing.prehash(for: data, context: context) + } + + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameters: + /// - data: The message to prehash. + /// - context: The context of the message. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + package func prehash(for data: D, context: C) throws -> Data { + try self.backing.prehash(for: data, context: context) + } + + /// The size of the public key in bytes. + static let byteCount = Backing.byteCount + + fileprivate final class Backing { + private var key: MLDSA65_public_key + + init(privateKeyBacking: InternalPrivateKey.Backing) { + self.key = .init() + CCryptoBoringSSL_MLDSA65_public_from_private(&self.key, &privateKeyBacking.key) + } + + /// Initialize a ML-DSA-65 public key from a raw representation. + /// + /// - Parameter rawRepresentation: The public key bytes. + /// + /// - Throws: `CryptoBoringWrapperError.incorrectKeySize` if the raw representation is not the correct size. + init(rawRepresentation: some DataProtocol) throws { + guard rawRepresentation.count == BoringSSLMLDSA65.InternalPublicKey.Backing.byteCount else { + throw CryptoBoringWrapperError.incorrectKeySize + } + + self.key = .init() + + let bytes: ContiguousBytes = + rawRepresentation.regions.count == 1 + ? rawRepresentation.regions.first! + : Array(rawRepresentation) + try bytes.withUnsafeBytes { rawBuffer in + try rawBuffer.withMemoryRebound(to: UInt8.self) { buffer in + var cbs = CBS(data: buffer.baseAddress, len: buffer.count) + guard CCryptoBoringSSL_MLDSA65_parse_public_key(&self.key, &cbs) == 1 else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + } + } + } + + /// The raw binary representation of the public key. + var rawRepresentation: Data { + var cbb = CBB() + // The following BoringSSL functions can only fail on allocation failure, which we define as impossible. + CCryptoBoringSSL_CBB_init(&cbb, BoringSSLMLDSA65.InternalPublicKey.Backing.byteCount) + defer { CCryptoBoringSSL_CBB_cleanup(&cbb) } + CCryptoBoringSSL_MLDSA65_marshal_public_key(&cbb, &self.key) + return Data(bytes: CCryptoBoringSSL_CBB_data(&cbb), count: CCryptoBoringSSL_CBB_len(&cbb)) + } + + /// Verify a signature for the given data. + /// + /// - Parameters: + /// - signature: The signature to verify. + /// - data: The message to verify the signature against. + /// - context: The context to use for the signature verification. + /// + /// - Returns: `true` if the signature is valid, `false` otherwise. + func isValidSignature( + _ signature: S, + for data: D, + context: C? + ) -> Bool { + let signatureBytes: ContiguousBytes = + signature.regions.count == 1 ? signature.regions.first! : Array(signature) + return signatureBytes.withUnsafeBytes { signaturePtr in + let dataBytes: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data) + let rc: CInt = dataBytes.withUnsafeBytes { dataPtr in + context.withUnsafeBytes { contextPtr in + CCryptoBoringSSL_MLDSA65_verify( + &self.key, + signaturePtr.baseAddress, + signaturePtr.count, + dataPtr.baseAddress, + dataPtr.count, + contextPtr.baseAddress, + contextPtr.count + ) + } + } + return rc == 1 + } + } + + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameters: + /// - data: The message to prehash. + /// - context: The context of the message. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + func prehash(for data: D, context: C?) throws -> Data { + var mu = Data(repeating: 0, count: BoringSSLMLDSA.muByteCount) + + let dataBytes: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data) + let rc: CInt = mu.withUnsafeMutableBytes { muPtr in + dataBytes.withUnsafeBytes { dataPtr in + context.withUnsafeBytes { contextPtr in + var prehash = MLDSA65_prehash() + let rc = CCryptoBoringSSL_MLDSA65_prehash_init( + &prehash, + &key, + contextPtr.baseAddress, + contextPtr.count + ) + CCryptoBoringSSL_MLDSA65_prehash_update(&prehash, dataPtr.baseAddress, dataPtr.count) + CCryptoBoringSSL_MLDSA65_prehash_finalize(muPtr.baseAddress, &prehash) + return rc + } + } + } + + guard rc == 1 else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + + return mu + } + + /// The size of the public key in bytes. + static let byteCount = Int(MLDSA65_PUBLIC_KEY_BYTES) + } + } +} + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) +extension BoringSSLMLDSA65 { + /// The size of the signature in bytes. + private static let signatureByteCount = Int(MLDSA65_SIGNATURE_BYTES) +} + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) +package enum BoringSSLMLDSA87: Sendable {} + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) +extension BoringSSLMLDSA87 { + /// A ML-DSA-87 private key. + package struct InternalPrivateKey: @unchecked Sendable { + private var backing: Backing + + /// Initialize a ML-DSA-87 private key from a random seed. + package init() throws { + self.backing = try Backing() + } + + /// Initialize a ML-DSA-87 private key from a seed. + /// + /// - Parameter seedRepresentation: The seed to use to generate the private key. + /// + /// - Throws: `CryptoBoringWrapperError.incorrectKeySize` if the seed is not 32 bytes long. + package init(seedRepresentation: some DataProtocol) throws { + self.backing = try Backing(seedRepresentation: seedRepresentation) + } + + /// The seed from which this private key was generated. + package var seedRepresentation: Data { + self.backing.seed + } + + /// The public key associated with this private key. + package var publicKey: InternalPublicKey { + self.backing.publicKey + } + + /// Generate a signature for the given data. + /// + /// - Parameter data: The message to sign. + /// + /// - Returns: The signature of the message. + package func signature(for data: D) throws -> Data { + let context: Data? = nil + return try self.backing.signature(for: data, context: context) + } + + /// Generate a signature for the given data. + /// + /// - Parameters: + /// - data: The message to sign. + /// - context: The context to use for the signature. + /// + /// - Returns: The signature of the message. + package func signature(for data: D, context: C) throws -> Data { + try self.backing.signature(for: data, context: context) + } + + /// Generate a signature for the prehashed message representative (a.k.a. "external mu"). + /// + /// > Note: The message representative should be obtained via calls to ``BoringSSLMLDSA87/PublicKey/prehash(for:context:)``. + /// + /// - Parameter mu: The prehashed message representative (a.k.a. "external mu"). + /// + /// - Returns: The signature of the prehashed message representative. + package func signature(forPrehashedMessageRepresentative mu: some DataProtocol) throws -> Data { + try self.backing.signature(forPrehashedMessageRepresentative: mu) + } + + /// The size of the private key in bytes. + static let byteCount = Backing.byteCount + + fileprivate final class Backing { + fileprivate var key: MLDSA87_private_key + var seed: Data + + /// Initialize a ML-DSA-87 private key from a random seed. + init() throws { + // We have to initialize all members before `self` is captured by the closure + self.key = .init() + self.seed = Data() + + self.seed = try withUnsafeTemporaryAllocation( + of: UInt8.self, + capacity: BoringSSLMLDSA.seedByteCount + ) { seedPtr in + try withUnsafeTemporaryAllocation( + of: UInt8.self, + capacity: BoringSSLMLDSA87.InternalPublicKey.Backing.byteCount + ) { publicKeyPtr in + guard + CCryptoBoringSSL_MLDSA87_generate_key( + publicKeyPtr.baseAddress, + seedPtr.baseAddress, + &self.key + ) == 1 + else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + + return Data(bytes: seedPtr.baseAddress!, count: BoringSSLMLDSA.seedByteCount) + } + } + } + + /// Initialize a ML-DSA-87 private key from a seed. + /// + /// - Parameter seedRepresentation: The seed to use to generate the private key. + /// + /// - Throws: `CryptoBoringWrapperError.incorrectKeySize` if the seed is not 32 bytes long. + init(seedRepresentation: some DataProtocol) throws { + guard seedRepresentation.count == BoringSSLMLDSA.seedByteCount else { + throw CryptoBoringWrapperError.incorrectKeySize + } + + self.key = .init() + self.seed = Data(seedRepresentation) + + guard + self.seed.withUnsafeBytes({ seedPtr in + CCryptoBoringSSL_MLDSA87_private_key_from_seed( + &self.key, + seedPtr.baseAddress, + BoringSSLMLDSA.seedByteCount + ) + }) == 1 + else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + } + + /// The public key associated with this private key. + var publicKey: InternalPublicKey { + InternalPublicKey(privateKeyBacking: self) + } + + /// Generate a signature for the given data. + /// + /// - Parameters: + /// - data: The message to sign. + /// - context: The context to use for the signature. + /// + /// - Returns: The signature of the message. + func signature(for data: D, context: C?) throws -> Data { + var signature = Data(repeating: 0, count: BoringSSLMLDSA87.signatureByteCount) + + let rc: CInt = signature.withUnsafeMutableBytes { signaturePtr in + let bytes: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data) + return bytes.withUnsafeBytes { dataPtr in + context.withUnsafeBytes { contextPtr in + CCryptoBoringSSL_MLDSA87_sign( + signaturePtr.baseAddress, + &self.key, + dataPtr.baseAddress, + dataPtr.count, + contextPtr.baseAddress, + contextPtr.count + ) + } + } + } + + guard rc == 1 else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + + return signature + } + + /// Generate a signature for the prehashed message representative (a.k.a. "external mu"). + /// + /// > Note: The message representative should be obtained via calls to ``BoringSSLMLDSA87/PublicKey/prehash(for:context:)``. + /// + /// - Parameter mu: The prehashed message representative (a.k.a. "external mu"). + /// + /// - Returns: The signature of the prehashed message representative. + func signature(forPrehashedMessageRepresentative mu: some DataProtocol) throws -> Data { + guard mu.count == BoringSSLMLDSA.muByteCount else { + throw CryptoBoringWrapperError.incorrectParameterSize + } + + var signature = Data(repeating: 0, count: BoringSSLMLDSA87.signatureByteCount) + + let rc: CInt = signature.withUnsafeMutableBytes { signaturePtr in + let muBytes: ContiguousBytes = mu.regions.count == 1 ? mu.regions.first! : Array(mu) + return muBytes.withUnsafeBytes { muPtr in + CCryptoBoringSSL_MLDSA87_sign_message_representative( + signaturePtr.baseAddress, + &self.key, + muPtr.baseAddress + ) + } + } + + guard rc == 1 else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + + return signature + } + + /// The size of the private key in bytes. + static let byteCount = Int(MLDSA87_PRIVATE_KEY_BYTES) + } + } +} + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) +extension BoringSSLMLDSA87 { + /// A ML-DSA-87 public key. + package struct InternalPublicKey: @unchecked Sendable { + private var backing: Backing + + fileprivate init(privateKeyBacking: InternalPrivateKey.Backing) { + self.backing = Backing(privateKeyBacking: privateKeyBacking) + } + + /// Initialize a ML-DSA-87 public key from a raw representation. + /// + /// - Parameter rawRepresentation: The public key bytes. + /// + /// - Throws: `CryptoBoringWrapperError.incorrectKeySize` if the raw representation is not the correct size. + package init(rawRepresentation: some DataProtocol) throws { + self.backing = try Backing(rawRepresentation: rawRepresentation) + } + + /// The raw binary representation of the public key. + package var rawRepresentation: Data { + self.backing.rawRepresentation + } + + /// Verify a signature for the given data. + /// + /// - Parameters: + /// - signature: The signature to verify. + /// - data: The message to verify the signature against. + /// + /// - Returns: `true` if the signature is valid, `false` otherwise. + package func isValidSignature(_ signature: S, for data: D) -> Bool { + let context: Data? = nil + return self.backing.isValidSignature(signature, for: data, context: context) + } + + /// Verify a signature for the given data. + /// + /// - Parameters: + /// - signature: The signature to verify. + /// - data: The message to verify the signature against. + /// - context: The context to use for the signature verification. + /// + /// - Returns: `true` if the signature is valid, `false` otherwise. + package func isValidSignature( + _ signature: S, + for data: D, + context: C + ) -> Bool { + self.backing.isValidSignature(signature, for: data, context: context) + } + + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameter data: The message to prehash. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + package func prehash(for data: D) throws -> Data { + let context: Data? = nil + return try self.backing.prehash(for: data, context: context) + } + + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameters: + /// - data: The message to prehash. + /// - context: The context of the message. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + package func prehash(for data: D, context: C) throws -> Data { + try self.backing.prehash(for: data, context: context) + } + + /// The size of the public key in bytes. + static let byteCount = Backing.byteCount + + fileprivate final class Backing { + private var key: MLDSA87_public_key + + init(privateKeyBacking: InternalPrivateKey.Backing) { + self.key = .init() + CCryptoBoringSSL_MLDSA87_public_from_private(&self.key, &privateKeyBacking.key) + } + + /// Initialize a ML-DSA-87 public key from a raw representation. + /// + /// - Parameter rawRepresentation: The public key bytes. + /// + /// - Throws: `CryptoBoringWrapperError.incorrectKeySize` if the raw representation is not the correct size. + init(rawRepresentation: some DataProtocol) throws { + guard rawRepresentation.count == BoringSSLMLDSA87.InternalPublicKey.Backing.byteCount else { + throw CryptoBoringWrapperError.incorrectKeySize + } + + self.key = .init() + + let bytes: ContiguousBytes = + rawRepresentation.regions.count == 1 + ? rawRepresentation.regions.first! + : Array(rawRepresentation) + try bytes.withUnsafeBytes { rawBuffer in + try rawBuffer.withMemoryRebound(to: UInt8.self) { buffer in + var cbs = CBS(data: buffer.baseAddress, len: buffer.count) + guard CCryptoBoringSSL_MLDSA87_parse_public_key(&self.key, &cbs) == 1 else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + } + } + } + + /// The raw binary representation of the public key. + var rawRepresentation: Data { + var cbb = CBB() + // The following BoringSSL functions can only fail on allocation failure, which we define as impossible. + CCryptoBoringSSL_CBB_init(&cbb, BoringSSLMLDSA87.InternalPublicKey.Backing.byteCount) + defer { CCryptoBoringSSL_CBB_cleanup(&cbb) } + CCryptoBoringSSL_MLDSA87_marshal_public_key(&cbb, &self.key) + return Data(bytes: CCryptoBoringSSL_CBB_data(&cbb), count: CCryptoBoringSSL_CBB_len(&cbb)) + } + + /// Verify a signature for the given data. + /// + /// - Parameters: + /// - signature: The signature to verify. + /// - data: The message to verify the signature against. + /// - context: The context to use for the signature verification. + /// + /// - Returns: `true` if the signature is valid, `false` otherwise. + func isValidSignature( + _ signature: S, + for data: D, + context: C? + ) -> Bool { + let signatureBytes: ContiguousBytes = + signature.regions.count == 1 ? signature.regions.first! : Array(signature) + return signatureBytes.withUnsafeBytes { signaturePtr in + let dataBytes: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data) + let rc: CInt = dataBytes.withUnsafeBytes { dataPtr in + context.withUnsafeBytes { contextPtr in + CCryptoBoringSSL_MLDSA87_verify( + &self.key, + signaturePtr.baseAddress, + signaturePtr.count, + dataPtr.baseAddress, + dataPtr.count, + contextPtr.baseAddress, + contextPtr.count + ) + } + } + return rc == 1 + } + } + + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameters: + /// - data: The message to prehash. + /// - context: The context of the message. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + func prehash(for data: D, context: C?) throws -> Data { + var mu = Data(repeating: 0, count: BoringSSLMLDSA.muByteCount) + + let dataBytes: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data) + let rc: CInt = mu.withUnsafeMutableBytes { muPtr in + dataBytes.withUnsafeBytes { dataPtr in + context.withUnsafeBytes { contextPtr in + var prehash = MLDSA87_prehash() + let rc = CCryptoBoringSSL_MLDSA87_prehash_init( + &prehash, + &key, + contextPtr.baseAddress, + contextPtr.count + ) + CCryptoBoringSSL_MLDSA87_prehash_update(&prehash, dataPtr.baseAddress, dataPtr.count) + CCryptoBoringSSL_MLDSA87_prehash_finalize(muPtr.baseAddress, &prehash) + return rc + } + } + } + + guard rc == 1 else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + + return mu + } + + /// The size of the public key in bytes. + static let byteCount = Int(MLDSA87_PUBLIC_KEY_BYTES) + } + } +} + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) +extension BoringSSLMLDSA87 { + /// The size of the signature in bytes. + private static let signatureByteCount = Int(MLDSA87_SIGNATURE_BYTES) +} + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) +package enum BoringSSLMLDSA44: Sendable {} + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) +extension BoringSSLMLDSA44 { + /// A ML-DSA-44 private key. + package struct InternalPrivateKey: @unchecked Sendable { + private var backing: Backing + + /// Initialize a ML-DSA-44 private key from a random seed. + package init() throws { + self.backing = try Backing() + } + + /// Initialize a ML-DSA-44 private key from a seed. + /// + /// - Parameter seedRepresentation: The seed to use to generate the private key. + /// + /// - Throws: `CryptoBoringWrapperError.incorrectKeySize` if the seed is not 32 bytes long. + package init(seedRepresentation: some DataProtocol) throws { + self.backing = try Backing(seedRepresentation: seedRepresentation) + } + + /// The seed from which this private key was generated. + package var seedRepresentation: Data { + self.backing.seed + } + + /// The public key associated with this private key. + package var publicKey: InternalPublicKey { + self.backing.publicKey + } + + /// Generate a signature for the given data. + /// + /// - Parameter data: The message to sign. + /// + /// - Returns: The signature of the message. + package func signature(for data: D) throws -> Data { + let context: Data? = nil + return try self.backing.signature(for: data, context: context) + } + + /// Generate a signature for the given data. + /// + /// - Parameters: + /// - data: The message to sign. + /// - context: The context to use for the signature. + /// + /// - Returns: The signature of the message. + package func signature(for data: D, context: C) throws -> Data { + try self.backing.signature(for: data, context: context) + } + + /// Generate a signature for the prehashed message representative (a.k.a. "external mu"). + /// + /// > Note: The message representative should be obtained via calls to ``BoringSSLMLDSA44/PublicKey/prehash(for:context:)``. + /// + /// - Parameter mu: The prehashed message representative (a.k.a. "external mu"). + /// + /// - Returns: The signature of the prehashed message representative. + package func signature(forPrehashedMessageRepresentative mu: some DataProtocol) throws -> Data { + try self.backing.signature(forPrehashedMessageRepresentative: mu) + } + + /// The size of the private key in bytes. + static let byteCount = Backing.byteCount + + fileprivate final class Backing { + fileprivate var key: MLDSA44_private_key + var seed: Data + + /// Initialize a ML-DSA-44 private key from a random seed. + init() throws { + // We have to initialize all members before `self` is captured by the closure + self.key = .init() + self.seed = Data() + + self.seed = try withUnsafeTemporaryAllocation( + of: UInt8.self, + capacity: BoringSSLMLDSA.seedByteCount + ) { seedPtr in + try withUnsafeTemporaryAllocation( + of: UInt8.self, + capacity: BoringSSLMLDSA44.InternalPublicKey.Backing.byteCount + ) { publicKeyPtr in + guard + CCryptoBoringSSL_MLDSA44_generate_key( + publicKeyPtr.baseAddress, + seedPtr.baseAddress, + &self.key + ) == 1 + else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + + return Data(bytes: seedPtr.baseAddress!, count: BoringSSLMLDSA.seedByteCount) + } + } + } + + /// Initialize a ML-DSA-44 private key from a seed. + /// + /// - Parameter seedRepresentation: The seed to use to generate the private key. + /// + /// - Throws: `CryptoBoringWrapperError.incorrectKeySize` if the seed is not 32 bytes long. + init(seedRepresentation: some DataProtocol) throws { + guard seedRepresentation.count == BoringSSLMLDSA.seedByteCount else { + throw CryptoBoringWrapperError.incorrectKeySize + } + + self.key = .init() + self.seed = Data(seedRepresentation) + + guard + self.seed.withUnsafeBytes({ seedPtr in + CCryptoBoringSSL_MLDSA44_private_key_from_seed( + &self.key, + seedPtr.baseAddress, + BoringSSLMLDSA.seedByteCount + ) + }) == 1 + else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + } + + /// The public key associated with this private key. + var publicKey: InternalPublicKey { + InternalPublicKey(privateKeyBacking: self) + } + + /// Generate a signature for the given data. + /// + /// - Parameters: + /// - data: The message to sign. + /// - context: The context to use for the signature. + /// + /// - Returns: The signature of the message. + func signature(for data: D, context: C?) throws -> Data { + var signature = Data(repeating: 0, count: BoringSSLMLDSA44.signatureByteCount) + + let rc: CInt = signature.withUnsafeMutableBytes { signaturePtr in + let bytes: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data) + return bytes.withUnsafeBytes { dataPtr in + context.withUnsafeBytes { contextPtr in + CCryptoBoringSSL_MLDSA44_sign( + signaturePtr.baseAddress, + &self.key, + dataPtr.baseAddress, + dataPtr.count, + contextPtr.baseAddress, + contextPtr.count + ) + } + } + } + + guard rc == 1 else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + + return signature + } + + /// Generate a signature for the prehashed message representative (a.k.a. "external mu"). + /// + /// > Note: The message representative should be obtained via calls to ``BoringSSLMLDSA44/PublicKey/prehash(for:context:)``. + /// + /// - Parameter mu: The prehashed message representative (a.k.a. "external mu"). + /// + /// - Returns: The signature of the prehashed message representative. + func signature(forPrehashedMessageRepresentative mu: some DataProtocol) throws -> Data { + guard mu.count == BoringSSLMLDSA.muByteCount else { + throw CryptoBoringWrapperError.incorrectParameterSize + } + + var signature = Data(repeating: 0, count: BoringSSLMLDSA44.signatureByteCount) + + let rc: CInt = signature.withUnsafeMutableBytes { signaturePtr in + let muBytes: ContiguousBytes = mu.regions.count == 1 ? mu.regions.first! : Array(mu) + return muBytes.withUnsafeBytes { muPtr in + CCryptoBoringSSL_MLDSA44_sign_message_representative( + signaturePtr.baseAddress, + &self.key, + muPtr.baseAddress + ) + } + } + + guard rc == 1 else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + + return signature + } + + /// The size of the private key in bytes. + static let byteCount = Int(MLDSA44_PRIVATE_KEY_BYTES) + } + } +} + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) +extension BoringSSLMLDSA44 { + /// A ML-DSA-44 public key. + package struct InternalPublicKey: @unchecked Sendable { + private var backing: Backing + + fileprivate init(privateKeyBacking: InternalPrivateKey.Backing) { + self.backing = Backing(privateKeyBacking: privateKeyBacking) + } + + /// Initialize a ML-DSA-44 public key from a raw representation. + /// + /// - Parameter rawRepresentation: The public key bytes. + /// + /// - Throws: `CryptoBoringWrapperError.incorrectKeySize` if the raw representation is not the correct size. + package init(rawRepresentation: some DataProtocol) throws { + self.backing = try Backing(rawRepresentation: rawRepresentation) + } + + /// The raw binary representation of the public key. + package var rawRepresentation: Data { + self.backing.rawRepresentation + } + + /// Verify a signature for the given data. + /// + /// - Parameters: + /// - signature: The signature to verify. + /// - data: The message to verify the signature against. + /// + /// - Returns: `true` if the signature is valid, `false` otherwise. + package func isValidSignature(_ signature: S, for data: D) -> Bool { + let context: Data? = nil + return self.backing.isValidSignature(signature, for: data, context: context) + } + + /// Verify a signature for the given data. + /// + /// - Parameters: + /// - signature: The signature to verify. + /// - data: The message to verify the signature against. + /// - context: The context to use for the signature verification. + /// + /// - Returns: `true` if the signature is valid, `false` otherwise. + package func isValidSignature( + _ signature: S, + for data: D, + context: C + ) -> Bool { + self.backing.isValidSignature(signature, for: data, context: context) + } + + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameter data: The message to prehash. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + package func prehash(for data: D) throws -> Data { + let context: Data? = nil + return try self.backing.prehash(for: data, context: context) + } + + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameters: + /// - data: The message to prehash. + /// - context: The context of the message. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + package func prehash(for data: D, context: C) throws -> Data { + try self.backing.prehash(for: data, context: context) + } + + /// The size of the public key in bytes. + static let byteCount = Backing.byteCount + + fileprivate final class Backing { + private var key: MLDSA44_public_key + + init(privateKeyBacking: InternalPrivateKey.Backing) { + self.key = .init() + CCryptoBoringSSL_MLDSA44_public_from_private(&self.key, &privateKeyBacking.key) + } + + /// Initialize a ML-DSA-44 public key from a raw representation. + /// + /// - Parameter rawRepresentation: The public key bytes. + /// + /// - Throws: `CryptoBoringWrapperError.incorrectKeySize` if the raw representation is not the correct size. + init(rawRepresentation: some DataProtocol) throws { + guard rawRepresentation.count == BoringSSLMLDSA44.InternalPublicKey.Backing.byteCount else { + throw CryptoBoringWrapperError.incorrectKeySize + } + + self.key = .init() + + let bytes: ContiguousBytes = + rawRepresentation.regions.count == 1 + ? rawRepresentation.regions.first! + : Array(rawRepresentation) + try bytes.withUnsafeBytes { rawBuffer in + try rawBuffer.withMemoryRebound(to: UInt8.self) { buffer in + var cbs = CBS(data: buffer.baseAddress, len: buffer.count) + guard CCryptoBoringSSL_MLDSA44_parse_public_key(&self.key, &cbs) == 1 else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + } + } + } + + /// The raw binary representation of the public key. + var rawRepresentation: Data { + var cbb = CBB() + // The following BoringSSL functions can only fail on allocation failure, which we define as impossible. + CCryptoBoringSSL_CBB_init(&cbb, BoringSSLMLDSA44.InternalPublicKey.Backing.byteCount) + defer { CCryptoBoringSSL_CBB_cleanup(&cbb) } + CCryptoBoringSSL_MLDSA44_marshal_public_key(&cbb, &self.key) + return Data(bytes: CCryptoBoringSSL_CBB_data(&cbb), count: CCryptoBoringSSL_CBB_len(&cbb)) + } + + /// Verify a signature for the given data. + /// + /// - Parameters: + /// - signature: The signature to verify. + /// - data: The message to verify the signature against. + /// - context: The context to use for the signature verification. + /// + /// - Returns: `true` if the signature is valid, `false` otherwise. + func isValidSignature( + _ signature: S, + for data: D, + context: C? + ) -> Bool { + let signatureBytes: ContiguousBytes = + signature.regions.count == 1 ? signature.regions.first! : Array(signature) + return signatureBytes.withUnsafeBytes { signaturePtr in + let dataBytes: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data) + let rc: CInt = dataBytes.withUnsafeBytes { dataPtr in + context.withUnsafeBytes { contextPtr in + CCryptoBoringSSL_MLDSA44_verify( + &self.key, + signaturePtr.baseAddress, + signaturePtr.count, + dataPtr.baseAddress, + dataPtr.count, + contextPtr.baseAddress, + contextPtr.count + ) + } + } + return rc == 1 + } + } + + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameters: + /// - data: The message to prehash. + /// - context: The context of the message. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + func prehash(for data: D, context: C?) throws -> Data { + var mu = Data(repeating: 0, count: BoringSSLMLDSA.muByteCount) + + let dataBytes: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data) + let rc: CInt = mu.withUnsafeMutableBytes { muPtr in + dataBytes.withUnsafeBytes { dataPtr in + context.withUnsafeBytes { contextPtr in + var prehash = MLDSA44_prehash() + let rc = CCryptoBoringSSL_MLDSA44_prehash_init( + &prehash, + &key, + contextPtr.baseAddress, + contextPtr.count + ) + CCryptoBoringSSL_MLDSA44_prehash_update(&prehash, dataPtr.baseAddress, dataPtr.count) + CCryptoBoringSSL_MLDSA44_prehash_finalize(muPtr.baseAddress, &prehash) + return rc + } + } + } + + guard rc == 1 else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + + return mu + } + + /// The size of the public key in bytes. + static let byteCount = Int(MLDSA44_PUBLIC_KEY_BYTES) + } + } +} + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) +extension BoringSSLMLDSA44 { + /// The size of the signature in bytes. + private static let signatureByteCount = Int(MLDSA44_SIGNATURE_BYTES) +} + +package enum BoringSSLMLDSA { + /// The size of the seed in bytes. + package static let seedByteCount = 32 + + /// The size of the "mu" value in bytes. + fileprivate static let muByteCount = 64 +} diff --git a/Sources/Crypto/Signatures/BoringSSL/MLDSA_boring.swift.gyb b/Sources/CryptoBoringWrapper/MLDSA/BoringSSLMLDSA.swift.gyb similarity index 59% rename from Sources/Crypto/Signatures/BoringSSL/MLDSA_boring.swift.gyb rename to Sources/CryptoBoringWrapper/MLDSA/BoringSSLMLDSA.swift.gyb index 27aaa2396..a9a527a28 100644 --- a/Sources/Crypto/Signatures/BoringSSL/MLDSA_boring.swift.gyb +++ b/Sources/CryptoBoringWrapper/MLDSA/BoringSSLMLDSA.swift.gyb @@ -12,10 +12,8 @@ // //===----------------------------------------------------------------------===// -#if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API -@_exported import CryptoKit -#else @_implementationOnly import CCryptoBoringSSL + #if canImport(FoundationEssentials) import FoundationEssentials #else @@ -25,26 +23,22 @@ import Foundation // MARK: - Generated file, do NOT edit // any edits of this file WILL be overwritten and thus discarded // see section `gyb` in `README` for details. - -@_implementationOnly import CCryptoBoringSSL -#if canImport(FoundationEssentials) -import FoundationEssentials -#else -import Foundation -#endif %{ - parameter_sets = ["65", "87"] + parameter_sets = ["65", "87", "44"] }% % for parameter_set in parameter_sets: @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) -extension MLDSA${parameter_set} { +package enum BoringSSLMLDSA${parameter_set}: Sendable {} + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) +extension BoringSSLMLDSA${parameter_set} { /// A ML-DSA-${parameter_set} private key. - struct InternalPrivateKey: @unchecked Sendable { + package struct InternalPrivateKey: @unchecked Sendable { private var backing: Backing /// Initialize a ML-DSA-${parameter_set} private key from a random seed. - init() throws { + package init() throws { self.backing = try Backing() } @@ -52,18 +46,18 @@ extension MLDSA${parameter_set} { /// /// - Parameter seedRepresentation: The seed to use to generate the private key. /// - /// - Throws: `CryptoKitError.incorrectKeySize` if the seed is not 32 bytes long. - init(seedRepresentation: some DataProtocol) throws { + /// - Throws: `CryptoBoringWrapperError.incorrectKeySize` if the seed is not 32 bytes long. + package init(seedRepresentation: some DataProtocol) throws { self.backing = try Backing(seedRepresentation: seedRepresentation) } /// The seed from which this private key was generated. - var seedRepresentation: Data { + package var seedRepresentation: Data { self.backing.seed } /// The public key associated with this private key. - var publicKey: InternalPublicKey { + package var publicKey: InternalPublicKey { self.backing.publicKey } @@ -72,7 +66,7 @@ extension MLDSA${parameter_set} { /// - Parameter data: The message to sign. /// /// - Returns: The signature of the message. - func signature(for data: D) throws -> Data { + package func signature(for data: D) throws -> Data { let context: Data? = nil return try self.backing.signature(for: data, context: context) } @@ -84,10 +78,21 @@ extension MLDSA${parameter_set} { /// - context: The context to use for the signature. /// /// - Returns: The signature of the message. - func signature(for data: D, context: C) throws -> Data { + package func signature(for data: D, context: C) throws -> Data { try self.backing.signature(for: data, context: context) } + /// Generate a signature for the prehashed message representative (a.k.a. "external mu"). + /// + /// > Note: The message representative should be obtained via calls to ``BoringSSLMLDSA${parameter_set}/PublicKey/prehash(for:context:)``. + /// + /// - Parameter mu: The prehashed message representative (a.k.a. "external mu"). + /// + /// - Returns: The signature of the prehashed message representative. + package func signature(forPrehashedMessageRepresentative mu: some DataProtocol) throws -> Data { + try self.backing.signature(forPrehashedMessageRepresentative: mu) + } + /// The size of the private key in bytes. static let byteCount = Backing.byteCount @@ -103,11 +108,11 @@ extension MLDSA${parameter_set} { self.seed = try withUnsafeTemporaryAllocation( of: UInt8.self, - capacity: MLDSA.seedByteCount + capacity: BoringSSLMLDSA.seedByteCount ) { seedPtr in try withUnsafeTemporaryAllocation( of: UInt8.self, - capacity: MLDSA${parameter_set}.InternalPublicKey.Backing.byteCount + capacity: BoringSSLMLDSA${parameter_set}.InternalPublicKey.Backing.byteCount ) { publicKeyPtr in guard CCryptoBoringSSL_MLDSA${parameter_set}_generate_key( @@ -116,10 +121,10 @@ extension MLDSA${parameter_set} { &self.key ) == 1 else { - throw CryptoKitError.internalBoringSSLError() + throw CryptoBoringWrapperError.internalBoringSSLError() } - return Data(bytes: seedPtr.baseAddress!, count: MLDSA.seedByteCount) + return Data(bytes: seedPtr.baseAddress!, count: BoringSSLMLDSA.seedByteCount) } } } @@ -128,10 +133,10 @@ extension MLDSA${parameter_set} { /// /// - Parameter seedRepresentation: The seed to use to generate the private key. /// - /// - Throws: `CryptoKitError.incorrectKeySize` if the seed is not 32 bytes long. + /// - Throws: `CryptoBoringWrapperError.incorrectKeySize` if the seed is not 32 bytes long. init(seedRepresentation: some DataProtocol) throws { - guard seedRepresentation.count == MLDSA.seedByteCount else { - throw CryptoKitError.incorrectKeySize + guard seedRepresentation.count == BoringSSLMLDSA.seedByteCount else { + throw CryptoBoringWrapperError.incorrectKeySize } self.key = .init() @@ -142,11 +147,11 @@ extension MLDSA${parameter_set} { CCryptoBoringSSL_MLDSA${parameter_set}_private_key_from_seed( &self.key, seedPtr.baseAddress, - MLDSA.seedByteCount + BoringSSLMLDSA.seedByteCount ) }) == 1 else { - throw CryptoKitError.internalBoringSSLError() + throw CryptoBoringWrapperError.internalBoringSSLError() } } @@ -163,7 +168,7 @@ extension MLDSA${parameter_set} { /// /// - Returns: The signature of the message. func signature(for data: D, context: C?) throws -> Data { - var signature = Data(repeating: 0, count: MLDSA${parameter_set}.signatureByteCount) + var signature = Data(repeating: 0, count: BoringSSLMLDSA${parameter_set}.signatureByteCount) let rc: CInt = signature.withUnsafeMutableBytes { signaturePtr in let bytes: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data) @@ -182,7 +187,39 @@ extension MLDSA${parameter_set} { } guard rc == 1 else { - throw CryptoKitError.internalBoringSSLError() + throw CryptoBoringWrapperError.internalBoringSSLError() + } + + return signature + } + + /// Generate a signature for the prehashed message representative (a.k.a. "external mu"). + /// + /// > Note: The message representative should be obtained via calls to ``BoringSSLMLDSA${parameter_set}/PublicKey/prehash(for:context:)``. + /// + /// - Parameter mu: The prehashed message representative (a.k.a. "external mu"). + /// + /// - Returns: The signature of the prehashed message representative. + func signature(forPrehashedMessageRepresentative mu: some DataProtocol) throws -> Data { + guard mu.count == BoringSSLMLDSA.muByteCount else { + throw CryptoBoringWrapperError.incorrectParameterSize + } + + var signature = Data(repeating: 0, count: BoringSSLMLDSA${parameter_set}.signatureByteCount) + + let rc: CInt = signature.withUnsafeMutableBytes { signaturePtr in + let muBytes: ContiguousBytes = mu.regions.count == 1 ? mu.regions.first! : Array(mu) + return muBytes.withUnsafeBytes { muPtr in + CCryptoBoringSSL_MLDSA${parameter_set}_sign_message_representative( + signaturePtr.baseAddress, + &self.key, + muPtr.baseAddress + ) + } + } + + guard rc == 1 else { + throw CryptoBoringWrapperError.internalBoringSSLError() } return signature @@ -195,9 +232,9 @@ extension MLDSA${parameter_set} { } @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) -extension MLDSA${parameter_set} { +extension BoringSSLMLDSA${parameter_set} { /// A ML-DSA-${parameter_set} public key. - struct InternalPublicKey: @unchecked Sendable { + package struct InternalPublicKey: @unchecked Sendable { private var backing: Backing fileprivate init(privateKeyBacking: InternalPrivateKey.Backing) { @@ -208,13 +245,13 @@ extension MLDSA${parameter_set} { /// /// - Parameter rawRepresentation: The public key bytes. /// - /// - Throws: `CryptoKitError.incorrectKeySize` if the raw representation is not the correct size. - init(rawRepresentation: some DataProtocol) throws { + /// - Throws: `CryptoBoringWrapperError.incorrectKeySize` if the raw representation is not the correct size. + package init(rawRepresentation: some DataProtocol) throws { self.backing = try Backing(rawRepresentation: rawRepresentation) } /// The raw binary representation of the public key. - var rawRepresentation: Data { + package var rawRepresentation: Data { self.backing.rawRepresentation } @@ -225,7 +262,7 @@ extension MLDSA${parameter_set} { /// - data: The message to verify the signature against. /// /// - Returns: `true` if the signature is valid, `false` otherwise. - func isValidSignature(_ signature: S, for data: D) -> Bool { + package func isValidSignature(_ signature: S, for data: D) -> Bool { let context: Data? = nil return self.backing.isValidSignature(signature, for: data, context: context) } @@ -238,7 +275,7 @@ extension MLDSA${parameter_set} { /// - context: The context to use for the signature verification. /// /// - Returns: `true` if the signature is valid, `false` otherwise. - func isValidSignature( + package func isValidSignature( _ signature: S, for data: D, context: C @@ -246,6 +283,27 @@ extension MLDSA${parameter_set} { self.backing.isValidSignature(signature, for: data, context: context) } + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameter data: The message to prehash. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + package func prehash(for data: D) throws -> Data { + let context: Data? = nil + return try self.backing.prehash(for: data, context: context) + } + + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameters: + /// - data: The message to prehash. + /// - context: The context of the message. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + package func prehash(for data: D, context: C) throws -> Data { + try self.backing.prehash(for: data, context: context) + } + /// The size of the public key in bytes. static let byteCount = Backing.byteCount @@ -261,10 +319,10 @@ extension MLDSA${parameter_set} { /// /// - Parameter rawRepresentation: The public key bytes. /// - /// - Throws: `CryptoKitError.incorrectKeySize` if the raw representation is not the correct size. + /// - Throws: `CryptoBoringWrapperError.incorrectKeySize` if the raw representation is not the correct size. init(rawRepresentation: some DataProtocol) throws { - guard rawRepresentation.count == MLDSA${parameter_set}.InternalPublicKey.Backing.byteCount else { - throw CryptoKitError.incorrectKeySize + guard rawRepresentation.count == BoringSSLMLDSA${parameter_set}.InternalPublicKey.Backing.byteCount else { + throw CryptoBoringWrapperError.incorrectKeySize } self.key = .init() @@ -277,7 +335,7 @@ extension MLDSA${parameter_set} { try rawBuffer.withMemoryRebound(to: UInt8.self) { buffer in var cbs = CBS(data: buffer.baseAddress, len: buffer.count) guard CCryptoBoringSSL_MLDSA${parameter_set}_parse_public_key(&self.key, &cbs) == 1 else { - throw CryptoKitError.internalBoringSSLError() + throw CryptoBoringWrapperError.internalBoringSSLError() } } } @@ -287,7 +345,7 @@ extension MLDSA${parameter_set} { var rawRepresentation: Data { var cbb = CBB() // The following BoringSSL functions can only fail on allocation failure, which we define as impossible. - CCryptoBoringSSL_CBB_init(&cbb, MLDSA${parameter_set}.InternalPublicKey.Backing.byteCount) + CCryptoBoringSSL_CBB_init(&cbb, BoringSSLMLDSA${parameter_set}.InternalPublicKey.Backing.byteCount) defer { CCryptoBoringSSL_CBB_cleanup(&cbb) } CCryptoBoringSSL_MLDSA${parameter_set}_marshal_public_key(&cbb, &self.key) return Data(bytes: CCryptoBoringSSL_CBB_data(&cbb), count: CCryptoBoringSSL_CBB_len(&cbb)) @@ -327,6 +385,41 @@ extension MLDSA${parameter_set} { } } + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameters: + /// - data: The message to prehash. + /// - context: The context of the message. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + func prehash(for data: D, context: C?) throws -> Data { + var mu = Data(repeating: 0, count: BoringSSLMLDSA.muByteCount) + + let dataBytes: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data) + let rc: CInt = mu.withUnsafeMutableBytes { muPtr in + dataBytes.withUnsafeBytes { dataPtr in + context.withUnsafeBytes { contextPtr in + var prehash = MLDSA${parameter_set}_prehash() + let rc = CCryptoBoringSSL_MLDSA${parameter_set}_prehash_init( + &prehash, + &key, + contextPtr.baseAddress, + contextPtr.count + ) + CCryptoBoringSSL_MLDSA${parameter_set}_prehash_update(&prehash, dataPtr.baseAddress, dataPtr.count) + CCryptoBoringSSL_MLDSA${parameter_set}_prehash_finalize(muPtr.baseAddress, &prehash) + return rc + } + } + } + + guard rc == 1 else { + throw CryptoBoringWrapperError.internalBoringSSLError() + } + + return mu + } + /// The size of the public key in bytes. static let byteCount = Int(MLDSA${parameter_set}_PUBLIC_KEY_BYTES) } @@ -334,15 +427,16 @@ extension MLDSA${parameter_set} { } @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) -extension MLDSA${parameter_set} { +extension BoringSSLMLDSA${parameter_set} { /// The size of the signature in bytes. private static let signatureByteCount = Int(MLDSA${parameter_set}_SIGNATURE_BYTES) } % end -enum MLDSA { +package enum BoringSSLMLDSA { /// The size of the seed in bytes. - static let seedByteCount = 32 -} + package static let seedByteCount = 32 -#endif // CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + /// The size of the "mu" value in bytes. + fileprivate static let muByteCount = 64 +} diff --git a/Sources/Crypto/Util/BoringSSL/Optional+withUnsafeBytes_boring.swift b/Sources/CryptoBoringWrapper/Util/Optional+withUnsafeBytes.swift similarity index 86% rename from Sources/Crypto/Util/BoringSSL/Optional+withUnsafeBytes_boring.swift rename to Sources/CryptoBoringWrapper/Util/Optional+withUnsafeBytes.swift index 8d8aabaca..e06f884fa 100644 --- a/Sources/Crypto/Util/BoringSSL/Optional+withUnsafeBytes_boring.swift +++ b/Sources/CryptoBoringWrapper/Util/Optional+withUnsafeBytes.swift @@ -19,7 +19,9 @@ import Foundation #endif extension Optional where Wrapped: DataProtocol { - func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ReturnValue) rethrows -> ReturnValue { + package func withUnsafeBytes( + _ body: (UnsafeRawBufferPointer) throws -> ReturnValue + ) rethrows -> ReturnValue { if let self { let bytes: ContiguousBytes = self.regions.count == 1 ? self.regions.first! : Array(self) return try bytes.withUnsafeBytes { try body($0) } diff --git a/Sources/CryptoExtras/CMakeLists.txt b/Sources/CryptoExtras/CMakeLists.txt index e08107032..548590991 100644 --- a/Sources/CryptoExtras/CMakeLists.txt +++ b/Sources/CryptoExtras/CMakeLists.txt @@ -47,6 +47,8 @@ add_library(CryptoExtras "Key Derivation/PBKDF2/PBKDF2.swift" "Key Derivation/Scrypt/BoringSSL/Scrypt_boring.swift" "Key Derivation/Scrypt/Scrypt.swift" + "MLDSA/MLDSA+externalMu.swift" + "MLDSA/MLDSA44.swift" "OPRFs/OPRF.swift" "OPRFs/OPRFClient.swift" "OPRFs/OPRFServer.swift" diff --git a/Sources/CryptoExtras/Docs.docc/index.md b/Sources/CryptoExtras/Docs.docc/index.md index 9bda3af77..aa5059661 100644 --- a/Sources/CryptoExtras/Docs.docc/index.md +++ b/Sources/CryptoExtras/Docs.docc/index.md @@ -15,6 +15,7 @@ Provides additional cryptographic APIs that are not available in CryptoKit (and ### Public key cryptography - ``_RSA`` +- ``MLDSA44`` ### Key derivation functions diff --git a/Sources/CryptoExtras/MLDSA/MLDSA+externalMu.swift b/Sources/CryptoExtras/MLDSA/MLDSA+externalMu.swift new file mode 100644 index 000000000..a9f593c82 --- /dev/null +++ b/Sources/CryptoExtras/MLDSA/MLDSA+externalMu.swift @@ -0,0 +1,182 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2025 Apple Inc. and the SwiftCrypto project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftCrypto project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +// MARK: - Generated file, do NOT edit +// any edits of this file WILL be overwritten and thus discarded +// see section `gyb` in `README` for details. + +import Crypto +import CryptoBoringWrapper + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + +@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, macCatalyst 26.0, visionOS 26.0, *) +extension MLDSA65.PrivateKey { + /// Generate a signature for the prehashed message representative (a.k.a. "external mu"). + /// + /// > Note: The message representative should be obtained via calls to ``MLDSA65/PublicKey/prehash(for:context:)``. + /// + /// - Parameter mu: The prehashed message representative (a.k.a. "external mu"). + /// + /// - Returns: The signature of the prehashed message representative. + public func signature(forPrehashedMessageRepresentative mu: some DataProtocol) throws -> Data { + #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + try BoringSSLMLDSA65.InternalPrivateKey(seedRepresentation: self.seedRepresentation) + .signature(forPrehashedMessageRepresentative: mu) + #else + try self.signature_boring(forPrehashedMessageRepresentative: mu) + #endif + } +} + +@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, macCatalyst 26.0, visionOS 26.0, *) +extension MLDSA65.PublicKey { + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameter data: The message to prehash. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + public func prehash(for data: D) throws -> Data { + #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + try BoringSSLMLDSA65.InternalPublicKey(rawRepresentation: self.rawRepresentation) + .prehash(for: data) + #else + try self.prehash_boring(for: data) + #endif + } + + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameters: + /// - data: The message to prehash. + /// - context: The context of the message. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + public func prehash(for data: D, context: C) throws -> Data { + #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + try BoringSSLMLDSA65.InternalPublicKey(rawRepresentation: self.rawRepresentation) + .prehash(for: data, context: context) + #else + try self.prehash_boring(for: data, context: context) + #endif + } +} + +@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, macCatalyst 26.0, visionOS 26.0, *) +extension MLDSA87.PrivateKey { + /// Generate a signature for the prehashed message representative (a.k.a. "external mu"). + /// + /// > Note: The message representative should be obtained via calls to ``MLDSA87/PublicKey/prehash(for:context:)``. + /// + /// - Parameter mu: The prehashed message representative (a.k.a. "external mu"). + /// + /// - Returns: The signature of the prehashed message representative. + public func signature(forPrehashedMessageRepresentative mu: some DataProtocol) throws -> Data { + #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + try BoringSSLMLDSA87.InternalPrivateKey(seedRepresentation: self.seedRepresentation) + .signature(forPrehashedMessageRepresentative: mu) + #else + try self.signature_boring(forPrehashedMessageRepresentative: mu) + #endif + } +} + +@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, macCatalyst 26.0, visionOS 26.0, *) +extension MLDSA87.PublicKey { + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameter data: The message to prehash. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + public func prehash(for data: D) throws -> Data { + #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + try BoringSSLMLDSA87.InternalPublicKey(rawRepresentation: self.rawRepresentation) + .prehash(for: data) + #else + try self.prehash_boring(for: data) + #endif + } + + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameters: + /// - data: The message to prehash. + /// - context: The context of the message. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + public func prehash(for data: D, context: C) throws -> Data { + #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + try BoringSSLMLDSA87.InternalPublicKey(rawRepresentation: self.rawRepresentation) + .prehash(for: data, context: context) + #else + try self.prehash_boring(for: data, context: context) + #endif + } +} + +@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, macCatalyst 26.0, visionOS 26.0, *) +extension MLDSA44.PrivateKey { + /// Generate a signature for the prehashed message representative (a.k.a. "external mu"). + /// + /// > Note: The message representative should be obtained via calls to ``MLDSA44/PublicKey/prehash(for:context:)``. + /// + /// - Parameter mu: The prehashed message representative (a.k.a. "external mu"). + /// + /// - Returns: The signature of the prehashed message representative. + public func signature(forPrehashedMessageRepresentative mu: some DataProtocol) throws -> Data { + #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + try BoringSSLMLDSA44.InternalPrivateKey(seedRepresentation: self.seedRepresentation) + .signature(forPrehashedMessageRepresentative: mu) + #else + try self.signature_boring(forPrehashedMessageRepresentative: mu) + #endif + } +} + +@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, macCatalyst 26.0, visionOS 26.0, *) +extension MLDSA44.PublicKey { + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameter data: The message to prehash. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + public func prehash(for data: D) throws -> Data { + #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + try BoringSSLMLDSA44.InternalPublicKey(rawRepresentation: self.rawRepresentation) + .prehash(for: data) + #else + try self.prehash_boring(for: data) + #endif + } + + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameters: + /// - data: The message to prehash. + /// - context: The context of the message. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + public func prehash(for data: D, context: C) throws -> Data { + #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + try BoringSSLMLDSA44.InternalPublicKey(rawRepresentation: self.rawRepresentation) + .prehash(for: data, context: context) + #else + try self.prehash_boring(for: data, context: context) + #endif + } +} diff --git a/Sources/CryptoExtras/MLDSA/MLDSA+externalMu.swift.gyb b/Sources/CryptoExtras/MLDSA/MLDSA+externalMu.swift.gyb new file mode 100644 index 000000000..83a500245 --- /dev/null +++ b/Sources/CryptoExtras/MLDSA/MLDSA+externalMu.swift.gyb @@ -0,0 +1,83 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2025 Apple Inc. and the SwiftCrypto project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftCrypto project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +// MARK: - Generated file, do NOT edit +// any edits of this file WILL be overwritten and thus discarded +// see section `gyb` in `README` for details. + +import Crypto +import CryptoBoringWrapper + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif +%{ + parameter_sets = ["65", "87", "44"] +}% +% for parameter_set in parameter_sets: + +@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, macCatalyst 26.0, visionOS 26.0, *) +extension MLDSA${parameter_set}.PrivateKey { + /// Generate a signature for the prehashed message representative (a.k.a. "external mu"). + /// + /// > Note: The message representative should be obtained via calls to ``MLDSA${parameter_set}/PublicKey/prehash(for:context:)``. + /// + /// - Parameter mu: The prehashed message representative (a.k.a. "external mu"). + /// + /// - Returns: The signature of the prehashed message representative. + public func signature(forPrehashedMessageRepresentative mu: some DataProtocol) throws -> Data { + #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + try BoringSSLMLDSA${parameter_set}.InternalPrivateKey(seedRepresentation: self.seedRepresentation) + .signature(forPrehashedMessageRepresentative: mu) + #else + try self.signature_boring(forPrehashedMessageRepresentative: mu) + #endif + } +} + +@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, macCatalyst 26.0, visionOS 26.0, *) +extension MLDSA${parameter_set}.PublicKey { + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameter data: The message to prehash. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + public func prehash(for data: D) throws -> Data { + #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + try BoringSSLMLDSA${parameter_set}.InternalPublicKey(rawRepresentation: self.rawRepresentation) + .prehash(for: data) + #else + try self.prehash_boring(for: data) + #endif + } + + /// Generate a prehashed message representative (a.k.a. "external mu") for the given message. + /// + /// - Parameters: + /// - data: The message to prehash. + /// - context: The context of the message. + /// + /// - Returns: The prehashed message representative (a.k.a. "external mu"). + public func prehash(for data: D, context: C) throws -> Data { + #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + try BoringSSLMLDSA${parameter_set}.InternalPublicKey(rawRepresentation: self.rawRepresentation) + .prehash(for: data, context: context) + #else + try self.prehash_boring(for: data, context: context) + #endif + } +} +% end diff --git a/Sources/CryptoExtras/MLDSA/MLDSA44.swift b/Sources/CryptoExtras/MLDSA/MLDSA44.swift new file mode 100644 index 000000000..a8a92b2f5 --- /dev/null +++ b/Sources/CryptoExtras/MLDSA/MLDSA44.swift @@ -0,0 +1,171 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2025 Apple Inc. and the SwiftCrypto project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftCrypto project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Crypto +import CryptoBoringWrapper + +#if canImport(FoundationEssentials) +import FoundationEssentials +#else +import Foundation +#endif + +/// The MLDSA44 Digital Signature Algorithm +@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, macCatalyst 26.0, visionOS 26.0, *) +public enum MLDSA44: Sendable {} + +@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, macCatalyst 26.0, visionOS 26.0, *) +extension MLDSA44 { + /// The public key for MLDSA44. + @available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, macCatalyst 26.0, visionOS 26.0, *) + public struct PublicKey: Sendable { + private let impl: BoringSSLMLDSA44.InternalPublicKey + + fileprivate init(_ impl: BoringSSLMLDSA44.InternalPublicKey) { + self.impl = impl + } + + /// Parses a public key from a serialized representation. + /// + /// - Parameter rawRepresentation: The public key, in the FIPS 204 standard serialization format. + /// - Returns: The deserialized public key. + public init(rawRepresentation: D) throws { + self.impl = try BoringSSLMLDSA44.InternalPublicKey(rawRepresentation: rawRepresentation) + } + + /// A serialized representation of the public key. + /// + /// This property provides a representation of the public key in the FIPS 204 standard serialization format. + public var rawRepresentation: Data { + get { + return self.impl.rawRepresentation + } + } + + /// Verifies a MLDSA44 signature. + /// - Parameters: + /// - signature: The MLDSA44 signature to verify. + /// - data: The signed data. + /// - Returns: `true` if the signature is valid, `false` otherwise. + public func isValidSignature(_ signature: S, for data: D) -> Bool { + return self.impl.isValidSignature(signature, for: data) + } + + /// Verifies a MLDSA44 signature, in a specific context. + /// - Parameters: + /// - signature: The MLDSA44 signature to verify. + /// - data: The signed data. + /// - context: Context for the signature. + /// - Returns: `true` if the signature is valid in the specified context, `false` otherwise. + public func isValidSignature(_ signature: S, for data: D, context: C) -> Bool { + return self.impl.isValidSignature(signature, for: data, context: context) + } + } + + /// The private key for MLDSA44. + @available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, macCatalyst 26.0, visionOS 26.0, *) + public struct PrivateKey: Sendable { + private let impl: BoringSSLMLDSA44.InternalPrivateKey + private let publicKeyHash: SHA3_256Digest + + /// Initializes a new random private key. + public init() throws { + self.impl = try BoringSSLMLDSA44.InternalPrivateKey() + self.publicKeyHash = SHA3_256.hash(data: self.impl.publicKey.rawRepresentation) + } + + /// Initializes a private key from the seed representation. + /// + /// - Parameter seedRepresentation: The seed representation of the private key. This parameter needs to be 32 bytes long. + /// - Parameter publicKey: The public key associated with the secret key. + /// + /// This initializer implements the `ML-DSA.KeyGen_internal` algorithm (Algorithm 16) of FIPS 204. + /// + /// If a public key is provided, a consistency check is performed between it and the derived public key. + public init(seedRepresentation: D, publicKey: MLDSA44.PublicKey?) throws { + self.impl = try BoringSSLMLDSA44.InternalPrivateKey(seedRepresentation: seedRepresentation) + if let publicKey { + guard self.impl.publicKey.rawRepresentation == publicKey.rawRepresentation else { + throw CryptoError.authenticationFailure + } + } + self.publicKeyHash = SHA3_256.hash(data: self.impl.publicKey.rawRepresentation) + } + + /// The seed representation of the private key. + /// + /// The seed representation is 32 bytes long, and is the parameter + /// for the `ML-DSA.KeyGen_internal` algorithm (Algorithm 16) of FIPS 204. + public var seedRepresentation: Data { + self.impl.seedRepresentation + } + + /// Generates a MLDSA65 signature. + /// - Parameters: + /// - data: The data to sign. + /// - Returns: The MLDSA65 signature. + /// This method throws if CryptoKit encounters an error producing the signature. + public func signature(for data: D) throws -> Data { + try impl.signature(for: data) + } + + /// Generates a MLDSA65 signature, with context. + /// - Parameters: + /// - data: The data to sign. + /// - context: Context for the signature. + /// - Returns: The MLDSA65 signature. + /// This method throws if CryptoKit encounters an error producing the signature. + public func signature(for data: D, context: C) throws -> Data { + try impl.signature(for: data, context: context) + } + + /// The associated public key. + public var publicKey: PublicKey { + PublicKey(self.impl.publicKey) + } + + /// Initializes a private key from an integrity-checked data representation. + /// + /// - Parameter integrityCheckedRepresentation: The integrity-checked data representation of the private key. + /// The parameter needs to be 64 bytes long, and contain the seed and a hash of the public key. + public init(integrityCheckedRepresentation: D) throws { + let seedSize = BoringSSLMLDSA.seedByteCount + guard integrityCheckedRepresentation.count == seedSize + 32 else { + throw CryptoError.incorrectParameterSize + } + + let seed = Data(integrityCheckedRepresentation).subdata(in: 0..: Decodable { - let testVectors: [Vector] - } - - @available(iOS 19.0, macOS 16.0, watchOS 12.0, tvOS 19.0, visionOS 3.0, *) - private func nistTest( - jsonName: String, - file: StaticString = #filePath, - line: UInt = #line, - testFunction: (Vector) throws -> Void - ) throws { - var fileURL = URL(fileURLWithPath: "\(#filePath)") - for _ in 0..<2 { - fileURL.deleteLastPathComponent() - } - fileURL = fileURL.appendingPathComponent("CryptoExtrasVectors", isDirectory: true) - fileURL = fileURL.appendingPathComponent("\(jsonName).json", isDirectory: false) - - let data = try Data(contentsOf: fileURL) - - let testFile = try JSONDecoder().decode(NISTTestFile.self, from: data) - - for vector in testFile.testVectors { - try testFunction(vector) - } - } - func testMLDSA65WycheproofVerifyFile() throws { guard #available(iOS 19.0, macOS 16.0, watchOS 12.0, tvOS 19.0, visionOS 3.0, *) else { throw XCTSkip("MLDSA is only available on iOS 19.0+, macOS 16.0+, watchOS 12.0+, tvOS 19.0+, visionOS 3.0+") @@ -356,3 +322,131 @@ extension MLDSA87.PublicKey { } #endif // SDK has MLDSA + +@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, macCatalyst 26.0, visionOS 26.0, *) +final class MLDSAExternalMuTests: XCTestCase { + func testMLDSA65PrehashedSigning() throws { + let message = "Hello, world!".data(using: .utf8)! + let context = "ctx".data(using: .utf8)! + + let key = try MLDSA65.PrivateKey() + let publicKey = key.publicKey + + let mu = try publicKey.prehash(for: message, context: context) + + let muSignature = try key.signature(forPrehashedMessageRepresentative: mu) + XCTAssertTrue(publicKey.isValidSignature(muSignature, for: message, context: context)) + } + + func testMLDSA87PrehashedSigning() throws { + let message = "Hello, world!".data(using: .utf8)! + let context = "ctx".data(using: .utf8)! + + let key = try MLDSA87.PrivateKey() + let publicKey = key.publicKey + + let mu = try publicKey.prehash(for: message, context: context) + + let muSignature = try key.signature(forPrehashedMessageRepresentative: mu) + XCTAssertTrue(publicKey.isValidSignature(muSignature, for: message, context: context)) + } + + func testMLDSA44PrehashedSigning() throws { + let message = "Hello, world!".data(using: .utf8)! + let context = "ctx".data(using: .utf8)! + + let key = try MLDSA44.PrivateKey() + let publicKey = key.publicKey + + let mu = try publicKey.prehash(for: message, context: context) + + let muSignature = try key.signature(forPrehashedMessageRepresentative: mu) + XCTAssertTrue(publicKey.isValidSignature(muSignature, for: message, context: context)) + } +} + +@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, macCatalyst 26.0, visionOS 26.0, *) +final class MLDSA44Tests: XCTestCase { + func testMLDSA44() throws { + let privateKey = try MLDSA44.PrivateKey() + let publicKey = privateKey.publicKey + + // Test Public Key Serialization + try XCTAssert( + publicKey.rawRepresentation + == MLDSA44.PublicKey(rawRepresentation: publicKey.rawRepresentation).rawRepresentation + ) + + // Test Private Key serialization + try XCTAssert( + privateKey.seedRepresentation + == MLDSA44.PrivateKey(seedRepresentation: privateKey.seedRepresentation, publicKey: publicKey) + .seedRepresentation + ) + try XCTAssert( + privateKey.integrityCheckedRepresentation + == MLDSA44.PrivateKey(integrityCheckedRepresentation: privateKey.integrityCheckedRepresentation) + .integrityCheckedRepresentation + ) + + // Test signing without a context + let message = Data("ML-DSA test message".utf8) + let signature = try privateKey.signature(for: message) + XCTAssertNotNil(signature) + let isValid = publicKey.isValidSignature(signature, for: message) + XCTAssertTrue(isValid) + + // Test signing with a context + let context = Data("ML-DSA test context".utf8) + let signatureWithContext = try privateKey.signature(for: message, context: context) + let isValidWithContext = publicKey.isValidSignature(signatureWithContext, for: message, context: context) + XCTAssertTrue(isValidWithContext) + + // Check that invalid signatures (mismatching contexts) fail + XCTAssertFalse(publicKey.isValidSignature(signature, for: message, context: context)) + XCTAssertFalse(publicKey.isValidSignature(signatureWithContext, for: message)) + } + + func testMLDSA44NISTKeyGenFile() throws { + try nistTest(jsonName: "mldsa_nist_keygen_44_tests") { (testVector: NISTKeyGenTestVector) in + let seed = try Data(hexString: testVector.seed) + let publicKey = try MLDSA44.PublicKey(rawRepresentation: Data(hexString: testVector.pub)) + + let expectedkey = try MLDSA44.PrivateKey(seedRepresentation: seed, publicKey: nil).publicKey + XCTAssertEqual(publicKey.rawRepresentation, expectedkey.rawRepresentation) + } + } +} + +private struct NISTKeyGenTestVector: Decodable { + let seed: String + let pub: String + let priv: String +} + +private struct NISTTestFile: Decodable { + let testVectors: [Vector] +} + +@available(iOS 19.0, macOS 16.0, watchOS 12.0, tvOS 19.0, visionOS 3.0, *) +private func nistTest( + jsonName: String, + file: StaticString = #filePath, + line: UInt = #line, + testFunction: (Vector) throws -> Void +) throws { + var fileURL = URL(fileURLWithPath: "\(#filePath)") + for _ in 0..<2 { + fileURL.deleteLastPathComponent() + } + fileURL = fileURL.appendingPathComponent("CryptoExtrasVectors", isDirectory: true) + fileURL = fileURL.appendingPathComponent("\(jsonName).json", isDirectory: false) + + let data = try Data(contentsOf: fileURL) + + let testFile = try JSONDecoder().decode(NISTTestFile.self, from: data) + + for vector in testFile.testVectors { + try testFunction(vector) + } +} diff --git a/Tests/CryptoExtrasVectors/mldsa_nist_keygen_44_tests.json b/Tests/CryptoExtrasVectors/mldsa_nist_keygen_44_tests.json new file mode 100644 index 000000000..2446e1969 --- /dev/null +++ b/Tests/CryptoExtrasVectors/mldsa_nist_keygen_44_tests.json @@ -0,0 +1,129 @@ +{ + "testVectors": [ + { + "seed": "d71361c000f9a7bc99dfb425bcb6bb27c32c36ab444ff3708b2d93b4e66d5b5b", + "pub": "b845fa2881407a59183071629b08223128116014fb58ff6bb4c8c9fe19cf5b0bd77b16648a344ffe486bc3e3cb5fab9abc4cc2f1c34901692bec5d290d815a6cdf7e9710a3388247a7e0371615507a572c9835e6737bf30b92a796fff3a10a730c7b550924eb1fb6d56195f02de6d3746f9f330bebe990c90c4d676ad415f4268d2d6b548a8bcdf27fdd467e6749c0f87b71e85c2797694772bba88d4f1ac06c7c0e91786472cd76353708d6bbc5c28e9db891c3940e879052d30c8fd10965cbb8ee1bd79b060d37fb839098552aabdd3a57ab1c6a82b0911d1cf148654aa5613b07014b21e4a1182b4a5501671d112f5975fb0c8a2ac45d575dc42f48977ff37fff421db27c45e79f8a9472007023df0b64205cd9f57c02ce9d1f61f2ae24f7139f5641984ee8df783b9ea43e997c6e19d09e062afca56e4f76aaab8f66600fc78f6ab4f6785690d185816ee35a939458b60324eefc60e64b11fa0d20317acb6cb29aa03c775f151672952689fa4f8f838329cb9e6dc9945b6c7ade4e7b663578f87d3935f2a1522097ad5042a0d990a628510b6103cb242cd8a3afc1a5ada52331f4df461bc1da51d1d224094e7abed3d87d98f0d817084780ee80370f397631ecb75d4264b6b5e2e66c0586b5fb743516399165837a0fdff7c6134f033bfa69c1b2416965c6e578592f40e258cb6dfb29fb8e0f54355b6e24a65f67abae3193d007115cc0b9ff94cb911a93b1a76c0e7662f5e2b20139e0159ed929cb932d4895f89a02e55c59df2dbb8f6e5dd7d5b1f3cec37b4a9166b381c5440e23e67368cde0a29d59aa05a3c9be24a4dc8dd75be30e82bc635d36aac66de880c6701a987d7e05f0f2ff287828bec30595089d8ab9aa390ed719caa6e576cdbbe9b184a322e5e2dabb69c23cc696d54fc32ff57001b6b64e2a837f3062d85aeb50b3510f7edfc34df38e083d4d9b94ffab0de15d73d9af30b9f31cc4f41c9c24f2d618b2a7c3c4bdfb745d52d3eb54589c8bda8ac05dad14ec744505575a0988eec651c1715439fdfb29923380a43c1a66a86c982a841f11820a6a0e1e2f2fff5108ecae51a6aabc9b949226d228ff84c4e5e5d63114d80359c4931e612dced1838b7d066ac9182cecfa223a21a4c8e155aefa780373bcc15098aee40c033af22f8e7c67a0d2526da7475e830308c04aed9d32bccc72e719ee70a8d13f09ac11e26ea237d5cc8f98b5ae0e54f933bd0507942ed900d056fd32f8e6e81777912fd482746029b71cce3ba69b8fc2d03eb441027c387bc2f95031a0ae7052215eb24b9ea8fb0a961b0f80bfa80d0d6257c1c22b508c5d31b97fcdfe1d1766e8a9c8771932dd598adb7e717743f45fc571f21e4a516249f81d747f15329790f0f70a0b8e461a4edf50504af03f30ddf8a8818e38761e1681d6ddef0b1dd326b2ec228ce48570f285b49d29d7c2ef37866d5446df82b8e43b34cb248962a21a9a3946159740f8aee8e6a16a4eb2b42d143fe2612e05ef4b5e646d813248444556a2a8bf92ce10badecb6b8a40b080dd42d53346fefcc4b9b40b1e4998991ec753c95aa2f2a506f311e710b0f1d36c1dca6644ee6d1d4ae9cea5666ef4b3e888dbdbb95a77ecfe1e8b477de7cb07639d682d53020ec14ea6c7dd7e715389d10938429fab8a068a1466a4cd891359f8074e0f5a142add731b87878d985e4fa6ecb3b73d298553418273e9503aa84092c080e5f2902f90f5c59944d24ca0271d11d0d6734606d039550a37fca2b735850e63f540f2f06b79144b5c4ed2c700bb51c33d265b3d037389c99efd597642d829db1eb58643cfcd07f4dec60b8f727d97bd7c4b59bda1", + "priv": "b845fa2881407a59183071629b08223128116014fb58ff6bb4c8c9fe19cf5b0b28965f58d99ee0de7bfb7840f59f65414289e259e05e8a18d47ec06d7900284ad7a0ae404b93b0498945abd07638920fc7f8c21282031eeceb0cc48d71fb0ad1ce84b9b3ddd1b3a2a40a15d5bfe0be23fc4757137c6c8feb7f8055677ba4af2814242a9aa0108a4046981800042130a2340a99140e01a931e1300a1a136959486601c111140451244206e4c848a1b80008b2490121901224500b03306112025c144912b30049424d14470e93342023898803238ca1c081cc0411184545c1c20862465210300d52b870a3b004dca20d4a386511214a01b48c43104809461253068161348618a00920878ca0168682208e41188e52a29021014652184521a42902132ed0b8248816710ab245818480942670dc126c1bc16c4016246236008996108b124e1b3721639225e1c88ca346285a32111c856110b9295990910493651cc40063b810c1184a52900c1cb20d2380090a4021d4188043a64d0835060c98650cb4890c8661d804601c0789d09231d8081100881119294e48b661d8962dd2b04121a1051226860349301a246d99483001200c5cc2840128900a298850340e1804640ab409d1968413196dd91031133689808025e300294a06304448109b429008858902a20d13461110120edc380c4c468842c004131490c1462248b82d48c80d64346c02348109136842a4211b26511c302a6328688b18609c922c22c9000031058ac08c894430d0142189424c4c106400b64dd2a8246306308bb4289b246ec34262a0c00c0c2050e112444a360ad122320c36014836804c00400c16090c9569a19829110602cc982d08c811498445d20290014942c1062e42c000a2902d9810711a054008058608b2651b36441109402107100b3306d298858148088034305c3844dc266eca947004394a442841e1464898862d113621221509530826213088da94804a80601425321295641b02120a92241a4422230624cc12818922600036608a32820cc368a1a04d5b286a110744589851c42831a4064ec0c831cab46810b669132488840462a3c421e0920523303050b6105942285c3005102286cc260e2226450003928a242c9c024e40b20400148c4a086e4ba6444cc84924464d4946821aa4690a316e21408c22964949044618b72c12b864d1c851c3366e5c2008e3328513198564c2019a4822111300a4b64960c06d1944628c086119b761021570ea732640984f68143d81ab95763698dcd02625a980994c19b7d47626409370f87e45d39b76b6762f7827b2779ba44046f54228a7b1bac9d8e751ac2dd248521564f81d028ad671db6a3a5ba3a9463597a519ae0c1da435fef7c421cde7f8483782c4334993192923796ddc3e5803f6c86cf2b37e39bbcb53775e5e9d2bdc65e84395b01af95c4398503a7e63a8d72f7e9bc58d7655cb86bafa0b679d1d791f09703f6e4b333a51dfb14b36eac9a3582f795afcecc2a67a42c7bc43b447b73ec8b73c9164b2d9ef4f767d607e975738e1d871fe61b7bb9570516959b9e7d8155455fc91590d84bd430921568b718241ccd09c989ddf75f529c4f15818969216ec0fbabec64f184832bfe428be44849c42f13ca84a5ce6ca628044cb9da3fd9cb5ebc776ba0e12683b98253b213b286593779eacc489c985beb734a7506791be1a6e6e03b557cfef2069091b846cdbbb72d36a89fd5c45c7c7fbfaed956ec9d8f3025225d6fef811b1cc3c6705f056388bbfbba1febf4d072281dc792ada048216d60d0d6044150909b70a4753336f4d5d3fdb8baca62946db28332ec778a76f96577baf868bf896773c200637495098b0f7749835915348d37641ec74d5a78e565d8100627468b0b5d05c2937618a98e5a12476533ce9d699e0f4f23b3127ee1f8d3cde3851a94beb8d606d836d47a9f090581b660ff8ec31e3ef4e45db530dd88a44d48f84b6bd3ced53be935efbce9930dd83bd004f086a064b3260b7211ba0547cb29e56603b922597b31a30a5ffcbd8a7ad2f3f05372dcea89048c683164410e56a6e5b7ec2ea6e3b0176db1ebf9258c97c297e752a202b1b4b02da47235873cc3e4a376b655779afffc9aef2e30b62be022aeff5e34b468507c921a00f1110b2cc1bc99b2ee295a31eac141ab4d87edc11d7fee22a07d69b7008c107ebe47dd2f215d074868c8b929a3df6614b5e95cc8a52f96c57e1697cee332f45b1980e6ca372574bdb51825654e6ff31d8e755cfc15b602f46a0570d3bd0d65e43bde8f792e7d526b3d362fe33638b3870e1c29e1030d3dba43c3d0db6e5f501ea3441b709e54fca1fd3e8654b1a9301ad151a36c8fa1513d0229de6e94d02a97b9d6f2ee8078a5d1228e783b8fdac46102a19600c531276b447f3900ef8f723fc3b60d7bbbbda9f949e4460b2f1103315ace25b3497ca4b4674a6052304609a16e90d064b0f6a9eae8d8e4be38e3b781f9173654e876ce6cc00578171fd9cfdba5704af2e60cf84fd02ebef19aed140287d195023000568e8d908edeadc76bca3e7fe8fe1f4b201c2a15603c025a61917ae664b4294c57030ac988d7967364a8bb5857e98218c2943e8cf1cb8c719daccfae8f42aa7dde9b4130170068a9cc86b0552d1dd0a48f63ddb8f8a51b3c0e10f3174a87d18f96533cf0c30c0be33d37c25cd3ebf8f5f10e66464ac2fb8260ba8b5bd443abc6ae0d5f89daa42bc71b2a9bc2d10140b117e32f41353fa1ecec9dbccb621350806c4e4ebfc966aa7122d85532728ef26aa40e55e2e1739790ffa157efa4e2a68da1ea4c9e3273cde11db7da47220123c40d50b73b8460cd93cb449783ead8a58f6ff272275a4f6c0437daf3df1d8d9617743d9302e73b715aa7caa139cdd07edad2de7b32b3f11e3c2b6a96d4630632c6d1de21473a287d42b52d210cd4ef808a998148817bb5083bdab3a3354b084c5773970b15da56f755e1f25354efeff89fdf4e307998d623ced32c7b9e942b6816a87a233867e2beb84582fff61af612be6e521ab3005e17d471a5c67a0a927211802ca4a0c2fedc01a12af5e8a6e8ff7bd08883af1d35c2f8570091d9e5fcd9d720644f0a45042443d2fac309fa7a15b2dbba1517ea19407b17caf7e9ea81fbce42f35a0568b2f70b279e12da48858f0e0ff0b5cb3a3012308ab83db627f5439e3a424d83f9e0b7750f2cbd9ca4afb9ba77765a1ea590ce9e37bc7eabe1ef099765ce59ea74c2776eccbceb895997f577733c609b9e853cf03010c5b9cc4e819c041abf11a9b3866502bf6ac0354e6ce1060d5155e6e5a1ac4fadf73080b20569317f9e118b97c78d7273300e329373e5b64a4181c4ac7b8933f82ade505d52cddceba373a54d072bc04a8d83273586615addc4d6f258bfd75ae94efd120f20b721a1cac33ff3446e554d6cfe266b05e4cd44de77d263a9d347f53b282a7bcb1511dd405a67ac05e7233f5ebb40d38f953ac903f90e96f17a6357eb59ed7900ffd82f54c3043d37f7774a0f4defaf65887eb53fe156fb26794982b1249bad27c89c136097aa2268347fc9c5e5be79e23c923215353a850e63748e766" + }, + { + "seed": "ab611f971c44d1b755d289e0fcfee70f0eb5d9fdfb1bc31ca894a75794235af8", + "pub": "d712599a161ecd99ef5b7a04313d5507d612565f03aa9695ed7c2df1cfa18056264432056c416013f5afe21adb7f4a5ceca1ee694e580301b47eb634e18c25bc0c964470bebf10afdf7587bae7fc40d45f94519a661a7cd04f7aa0d95aaee797027d8cbfaee682add859106cca978f5311a3b53a1699691c1e9e8bf9d2118b23bb1b33c5e9a02f968c800560a865fb826289fb34c936c825d1d0a7db566b9f30b8e42e7a9ae8f325e51c197cea36ddb79df537d1f4102dd44c01b59401770bccf4d5e8329121863929c91dfca9c00f862ed7dbf3cf7558f6150b2b05d32f04c1e6e775350248dbfc0aee58681b5b177ca7ba17ed6c7b4ba7302dc0f349564db0d83c7f7d1afe502e27dc465704aa9b7aa50d8f5a14ccc5701fae3505d3ae11ead8142af9ee7991b3b216686126627c60e1769ec805000a6d94076905630d385b7cda7195b54522a81e8855f3d6dac03e686cadf72a9142363770283915034ecdf9047f80a8f9594273c7637b514baf9ace79a7b5d28f8b0efd63a1f8b17301f499fd37e448f5e99f118fc7b2a4bfd7dada350456ce5cc72c5646c57229ecd91082a85efa259e2aaff761f0a101a2269c8b88d94f7a8e4f87e7b9f977396e71bcd2b2bf89ab8b829a345d37fe5240ce7216c64efb01ba8a13700d4bfc9856eace22fb763bec7d1e02f0f49b45f587ba6b3f571be7aebea88557492c6c766d7ae298c7902f346deda494eeac39c8767199252c9ce18825385596956f1f41b2bb534f761fa0c89917077b23f8056aa7405f8ae4271b11df284d4ff50afcdbfdcbe880131b60e3534091a759531fbe6e14d6057bdb458449ae4bcec26580877331359e794e9d75bf454122264ccd13f9349c8250f0bb586b5bb482eb74524fd12f0f61edd28411d072fcec556bbc5569afcef1a62c26078088a77ada5976e6a2a99e98c898e5789de786b45708271957d88594db7fbabe23ddbf1abb12e715bd576483ead259ba6553e95ae65aedb55751532b98f32376baddd2532e5801a255fa6dfc3fccd5fc5110ee231450dda25c4e09b89b577b8589c7179b06d62aba652bd6dad5159e8d7b84a5879f075ebd31ecce03d2f1e508fb229665b4232b905638176afcf232d4c19af38a812c8d2af2776cd444e94d46816ee68e7b5cd5ac1622518682eee88e0f64358181ef35a8ec71e00e70f1222f494615a4fdf7c769cadb0e13b2f15b5eda38809a5960d25090c1513636de242bbd9a79f77cb45c81557d6f1d18991b37becd4cad9707b1f88920b69a55cd52cdac2318287d4b13bc9cba6884f9a38054493bdf91b29c2b7a71e2dd8ecfcdd0259bfff402251ca2c95042af917ebb669ace5b354dc393a10f66847d546c47eda695ad3a985834292e68a024ab6045adf36dd2a58993a292b24837f61711cb59ea8deb103f63f971e385ce0490a0709c890caf57ed86991aca729dbefeb023d4356b7144485b7af45559c3ff722ec8fcd8befbaf41119a24c9b686a79c38356f3fc55720f03ac89b3187cecc0c1247b9f8ddd82b8b415baa6bc7a25e2529c41cc9765c768dc6a555d6114707879e810bc4c7472a07e8b90b0f41b8b4cabd5326b190572b664127e2fb476d88db32d42d50bcd7f637ab2b7d13e0a7d03dfa30b0b81f7d2917c1ca5260bf71230f7342a5bb9f9261d966cdee85f768a9b8b509f094c60e0c08bac278827e84e6590141ff8c10e9b51fbf6e7724030eeb8b59731e47fa3d130407f833455386b66cbc453ff4bacc0467d747a1037ea57b3de4421c3050dc6d33268cabe633c6e54c13d64973751798a99d8c53a2132e5bbbf8b2961b9e800f01940c700e47e4e1", + "priv": "d712599a161ecd99ef5b7a04313d5507d612565f03aa9695ed7c2df1cfa180565499f298e78451161f0e9ff4b862815f3d5d33393af3e677ee2460df7bf3ae6e9a80f12a9b349dfdb9b3d0513bffe620394a80a2883bcc69f1847057ac3bf35e63a72e60d5267d606c524ab847bcbc1113874755248dfbe583a2f0db0fcbc8cd9c9008128028ca962ca038680919464c008acc084d00466093002a541631d9a890d21200dca8515418655c2005c8306a0c01204b040e1c913001418c11448c1a3206cc060c50b27058244593a0119822012426260b402603090e5a12020301611a14010119625222200ca3650b09904ab44d80264d5a366263906d1207320339491036111223041888202421880ac82819806c492629c4922062188993367204220282086558945018b32823335263a20112498c91b428e4c645db82694b021101270a09397220314042a44dd23092913444c134091946200c210d4b36101026111437699440895308204324019b464152b8854286891b11880c41449c3252d2200520320c13948510930582c6480211865b262ccc884444b42dd2088e601062cca66d8344451b3765c8b05180066922040ca40489514424a3404162965019c78160320d9816921220101915016248051c362e59308a13126aa3444610a52900c98c93c4100ba04508170558486201086c183392dcb86dccb08d59906420026014b309104945a3a60012472e1c005203c91003492c1c92811b396613465001294650208462106900255043b8201cc68d981284a03204220821dbc46ca10622a09071e4204d213984d4b44023432401b6001a318289188209067112b131c3b624ca126c90066201036c0825909b3669e28044982428c4b0100b384159986d4b220118382618332e11150c49a8088c48898bc62862864c00461224a681c3c681e0c60d19046e9c9441c1283203466e1321251cb5854aa084a424319cb02048946014b7404814501319691ab00012b12413c880628410c31061d94040db4806898869119289149700c204241cb7058422284b3646130132cb406160140ce0005218010a50228118278013329004269110a32c99b42c5c44464b3632dc468a49b04858884961c44102a2650302624326410b9885a11426481092da962898c8006028104386688342254412102343511c326a09884511c5654010065ba001cc32889922258a1666420611898241120165c3140621446c09450149424c99948c0045022c7b04ef5ad7d8fd0179fc8afda5dcada47cf7926345f6cd5f072625345a613c92d8d1ea7cfd213dfcd7896567194767da47e3b3bed4e3f2cf727c78f7fd63855cc68efea538728cb803830b6fc646a865b93a46cb4ee30968c8a61d913dacc0483cc19dd790acec8e07ba8f5e924d7f024b69552eceb588a94dc2bf11be97a59a8065043525c628b59ce423ccf8293c78363c98662ee883f8b46a243ab2ab8ed3eb24c4afdf383b2e9f0f969d4e6de19b06bf6837abdf751725b97d45389138edb253a78c78de841224fd7cffae2d8d58ff5d9b9ecd9c209f77cca5d167b1a93ec5a9b891e1e4dbf1741da1c4aae7622b15f040da03350cecf2649e309d38a8d5f8aa218900f321557c593dcb83a6a5a2c3c13129a07236e6f50864e4e9e11b840f3d6e6b74acfe7394dad62167e1cae165b9207596b1c7cd3de62558c24723c8f74bb4203f0f5272d0e1e8ec631bec8ed97e9dc58fc843a901329d566a3a230d138b3aac9b29ccdbefdd2a6aaa7ac2c57a4850ab0ae2425d5a1f572848755d8894222f37885c8d7d34c02fa95457c7ebcf1d56b3361daaf75f43399ca1958609bf9654a0e50c6ac0b405014ab252767a8c31f9d23bf9dae2fc6a2548215b9e77f2fd28d049232763109367b1ab9a25dbae50abda2422a8f262a5a19d4035fda91433072e5e1dc927425ea3d1de222230889373ef0d08e475662b6bebdf6c4e48ae442331d9c7fcbca2c41901543aefecb866d132090aa366a358939ef9afa92b533a60444c95bb50ec36dc0478f4511948e34a4a2ee53b439eeca84b53b3470e86413c04822cb6209ffb97c777da0be08f46f629dd64be853901bcf32d1bb5efc5512b0ac678154e98e862a9df088243a1e279c55253aa3666ab343909b2421917faf7fb4ab09a0268d3be0cc9dea933375975ec7e3ac22e50ceb3be9fb3549edf77d129fe75b1d24c2becdbcd734f6a03349a4ebe17323985a9ffe33c1f68e99f321b3eb918c0bde1a53a79906bd5704351693aaf92804310df2a4f05e1eaafc241c308ece274b9b09935d5889ea1f58f580d2cadd2f9f9712279f0fa1f8ae4aea4d993c55ce420ddfe5527fd0abcee4fc8f547f6c335bf6bd6827cca513567cf4744caac1c373b8f8e12e2234b0341f7f3974bd9a2079eb5c48f25cf6400a1299e42f2d77804433e182f8ee02a1966846152fdaa949b9979260d1b511faa2a705a2282e3955b6b906a2faa28fd054a9b78e424fa5895650e60a2b1beb1aa6480ebcc0a01657a8344ab896dfa7c023deda05ef642134a3ab9a783da9ae6d189d2e2c2574ed909d757230885c0155644f25166a0474f618fbb5066446c7091d6e6e7efaa6890d3704f8ff5170f2e2f656fc7825c2f1175a8de14ec4cdc9f7d4d0996199d63c44e53d8adb961335c954462b19b9bf52a67e3de2be220156e4dc9445904fa9cf42c2de922231ff1be379830abf6f00b46a79f470e2a4471730cb8c29a000ebebe10a2789a714f07f432962827af53b321b8f7de08a95d47ffc223df9a44ee0fed3d5bcccd948420e4e8d452af900d49c022a3fb6c0d6887c836393597e40d36613e77ccd8a306a21cf826d20d9f823513815d885eb74f04adeda4051bd0998f7c9a48015bf662d72f6a99aed192c283d936e65f48e9748ecf2e0325518ef4f4b6991bc3956f3954535218b3727a721e31dae286ab9947f13cd59e8f0fe7214dfb1540731049216d8069df7a2be167623684d142f721444204ddbaa851629a43dd9f75554ecac9bc397c497b9097f85403db3f6dd8ae83e4b0de698f5e0aef23fbcc21167e10ef554791f87dc9bd58f8ee82af76b0846510873159a534e129daf7b567bc17d5ee78c9630ea506dbedba84e2d1fc5df9154903038a0fe8ef6c3af6028d71cb2cf593596316a989a28db6cf1814999de9f597abd1abc20a168e2f2fc12f5c3d04d3581e0df9cc2f890c207f97861790e0d1cdc67590deec5c6e9ba3ec2fb2aa3f0f047fe210cbe8eb03cc5f976a74df9ec1086cd4d1a9b8e47888bd77095c305852e02c85f5b9bcdd6ff1c2b79e299ee6352a83ff2a32d5f71e8dcfd05167a9f6de8bce131602a83b998a45fc64ad3163d8b4673defbe8ea91b0a4e181347d60bcb00db9dd6a7d1e2553cb7ddcb62586d5d914e5c4db82ceacb38834f9b2681d81a5d6dced5304496a84e5137982e6da1b215701d50a77e22356bac7734883e19bbd4a619a8730c24c6c45b575b49747cdace2f081545115c2a4b2ad772bdf51ae4a6f2e7152dd076fb2d8f4c068a08720f4f6230578475fc0a5f0b86fa56d01c7e5d1c310a61d947df93ec69fd96a5996a3b65f7" + }, + { + "seed": "e0264f45d58ea02c8738c006caed00f3ed9296e2f6bbf4d158fe71c2983fdf38", + "pub": "8a0ddd293eea646f5a09a0513991ceaf8f5d7d458cf40f7c1f18f6dba8f4c2f885cfea936e806adbe9ae7625c61929e78dc507a78f1fcad6e6a2b242a51f625fa590e86db8e41598bf24a73981203304422e48650f22579a72190bb5ff198dc4c4863ba1c9736dbdc5154c0d0cbdfc9e2fe4a74249c4145d45f92c592543b28798f753ddc21f40720fedb33a5ef135ff8a63a4b5b82485ec02f8922671207581ee5c9337cbac296ffac53a4c4aca973f8128274be29e569ba66cb62fdf1c6f7d3de041520d13180a24ef11a8ce0ada2558dc5c7956f96886942476f8dc24531ac8576f8ee465915c4d2e3caa384043d84ee97e641cb9611b119a3bc19a4b6fb0fc65ab582b3e815e4891bba28b3c3a800e3aabeedde28db45e21e5ff0b39f3fbffac8d58fcebf984ec694d0e04ff8307d9cb20efa6991e9438a2bf11fc28e316cf5463fd72bba4b96704c55c9f9ebe2375f20cc01cb7469ca0f0fb7ebe1b7961d21098e83e6d7e01123dde46fa3dc6a5ae6b534eb902049ba2446fc7b9285ebcfe808cbce653efef932f172a2184354f48f92f0b51b52a7c9b8081ec3b22aab3de727ba9379c0ea44850566a113b47298ed70296b5ab94c7dca097603c907ee7ae9c0f024f978e837250a89cdb3adf0f27b005a0f4e2375f3a533908b2c7f95159ac9f1eb9672ea0e9f346921a16ce6585469eb325690cd1ccabdec1c1026a0c8d8090bb82c9cf5fdc7e1a772a9585ec8dcf8b9215ff2aa56e14a7be604ad84848f589316f57bf074b61749597dcf21a62ae6802eda4afb1ae2804b60d465266f1dbd879c2116b45971a01c340ffbe9db1ec8f9de9673202d585a1f636427fb1ed7b0aa4d430636511234e5543f6bdf737f9ecad4c6e2330d04a3f3a2c95bf2ad97c624e0f7420d91df006572c9fed5104913eb4b204722109495c232511151f13f5d90b4d29cd441694694680e8ca07d70a6855e32b3a69f2c65ecfac6f024d98aec7b864c877117ea3c0b996c6ad47f7d431d526c651ea5565b35d36d7b4be5838115f1470fd19870b4d071e061d22fb7406d458f8d106081680b362010de913d192367856f9fd3f233809796ae483329b11b51c4c24adfce763df4857af3803617bd75a5c3bb8b72ae2a0dcf6089a4b73d8e7ff55c3dbe0ac8e1df953ac350ec9f081c3bbb419cbe99bd752d2408ead2e5bc94312af51efefa21834b8bf4a4428580273875a0d56a77b16d524429e656084dc245f95c8f3091784f5d3ab553b0fbecfc5bf02e2f38705adda936697266634e5aacd8ce0b72b898a14dba1986dc71a5236c64b983eb8bce599d1d8a39d85af5194f08a9874515490c9789c08f0595946c6f5878996938efcadc8fb5abec5a626f80bcc06416fafb650b72eaa995a811dacf45362187de6481567f488ea687c85c49219057ee73b1f29bd7a2f91e4942f84f8fc26cf8d08f52888dfa460eaab41cf76dcd6a44561fbd10825cc233e0eded7e570671a2aca2ef0579314e8887f8cf3e0d2ff4da11b6a520b7354515a8b6101638a3892cbae53cbf2e7ab4ec714e2c12c2e2126d8d2d677b12a51a32bc4f68be70c064cb9974469b336c03fa403d5c787bf11ae0d770f012bb9fca35fbc7bc38334f50632a380ba4cd703ff4b1efba99af3757881aeca48ba56b58ed7a7f7fbc2e01bc8a96486a52c996741ab194e67f53636bb6c7b6aed61c714756ae13bee63078f81b984c816b42691d0233613cb10ac484739ec71723978ef7501bd598146ec34f1b3d738968d176b2199a2537cd4d7c4cf18dddb930db700b40f1de4712057a38dae22ba8177ef49a859f919e282a0c911b38077e989a0ff", + "priv": "8a0ddd293eea646f5a09a0513991ceaf8f5d7d458cf40f7c1f18f6dba8f4c2f8daed2429e22b8341baaec98bfb96e5bbcd87b0b137725294fa461aa5f576e384f4850c85a3e57765ee815679ec01d7c91b973de21b4464d0e83739c584cb7d075d349a52d69c16f8daa0a10608e3407d2cdb74cf3ddee8ded5f9565d531e0fd7d2b4510c12045bc46cdb024dc4b26494c4305c465293a8651bb5809808450ba32524b90920c3001c2861a21650632472d3a46823858480c0611ca285141942db4651a114455bb685cb16208c822012c564da06282283510b4588e342110cc461d830808c266624c93059a6111223845388681a015061061094225100243010914d233742022006c2b42d00c77094840d93a01081c64d982889d8b8058ab4051c090c1b3941424286c8102a83264c2040244a24020946805828400404715a386904848953800112840001b28493328c93243012002ae4182259488dc9488dcb98310c928010a54019426d1a0452c212458926491cb3310046728c442424918123a02053042e5b446194948400b5690a1761831425482265d2328a6084040c8941db38615aa0919a22244c406d59446d02348219140d01b40cc9a86d23480a1c223059a88444268a13c8114840861487902426685b14840140440c124a23878c93008a41044aa000499822482128929b444a10868d02240952882024900594346c1a136154481114200c8cb28c1014629926688a40065b80111b28129b009050488ca2082c139201221546e1408a1c16012192685a48415b006dc9288e93226d13425154b86108b3801ab784c0183099163198142ed10246dab030482620c8b008d99008594811a0a860188391e1904c64a6254aa81104904181c804d1386640c68c583268e034628318624b906549882919041152340e14342c59422e8a42841bc30d021026d9400a0a27889898308926648a8410c1b40c51b08894440520c810c032120885281a8968c1842120a204e2c024dc4684a0a0890a92851c128ddb826919196261984de0a84d48068452b245c842822402001c3250c0828de4c804cc968d8b428608184e03b72101a2112300851b3885a0100d40364511c0259334618bb005d4262980024492888cc3843001061208106620226c0c026842c47122934582466a90928121a124181408e132614896414a368ea0406800392621918d54442c4c369259c20c1a202088c21104242d0b904d43380120076ad3342d0a264a44ce3a2935e913f0aa8fd1fe89b01594667fcc774bb0ec82d1e1e03cbf65dd7effd4dd4dd18c18958e2fb63df54d04e63e0e1f5d72bbde611adccf96118eba603bf134375061a0e1918a6e7acbd137f1b461c7bf52697b851f40543c1ad5387776d89b30e3c947439b2211fa76ab41d3653277569ef2bfe364b01778e05985e91748c3266908f15122cd87dfd937f1537f1cc5de054fae47e9e9fc0b1dafbf312c35452cc2970aeddd096cb7e5cce202a5a6627d154099c45d33dcb6f71868039a0b83ab91fc50615115aec4281d88534904b8b2e28900e7f2574c4f28dfce80d74b6f667158dad9b778f837efb6e05d7ad3a43437b997ea54770544859128a557b898435ee293568b8b4efeaf0758986b5407faba47d112c25b716d2413adb61c8de997b59aabf3623c1c63d6150502c172a05c051078ec00ac827a97708458e44b92c02fb8dc00b717e7ce782fd194fbbaf1fd00416bde8ad8387431315ddf7ee604bd4340b14929c98d4bf11fd6756a806cdd426b09a57c8e4d5a08355ab63ff26a1b27e946dd63429aa4e2418a0af81cfebc2dd57191cf241ed3a8127c2f2a21a6e05a3f9d3675ec22a8978ca28d8baf19eb87e1326f1bd6f66e301ee1ab2b420bd70c32738862dce524bff5de1d45c8f0a8aa412e879a0edfd7fc89925cd997bb37699f4fb56d2fd4e45094090abd6017b2197fbbab1141c90c1cd4992e2347e2115f5c1f31541231ccd9ad5716da9782f085603a4bda84f6e252e20a2ef55bf80657e94e420cc77e0c9413c3865117c7cbc62a443a4a469d1de8ee6403c8fe3a3755743a20e5570462b07e96b97f92b5fd096fabca77b1b6fa724e12e65519436d679b9ecb00a71c4d49c56c74707f36448ecc11d35a767c90c0935d3f15cbb10f8db92deabe6d7bf67007300c2b7ccb57eb8933017e874ac812f303ea30b21713b37be3cdca516387f4bc3f9b9167d78fcb230331152242b85c358798e56072f6c82895691c8cfd275412d49217a14a63930c6cf063f16ab88027975ad5f83779d068874490a7637c9a74c9c973af80b6194ecabd2b9db9a51e66364179d52730f98c2883f2d2599961f16244f0f313a4470bd2424a996609c55ff8a98b6a886273fceb66ec29d1873494ba5afb927cf1383c3933ae0e85aff3ee715f42d0a8f407760c3c76a2a9d1880e4411c5377036ba3ac8f2102554abbf3a5e259ef71a5c21e20f92b272fae361fa8646a84729ae6b07a422a63db49c46343c8c9bb288c1c46caaa58a53072648615eb52ca7750029003912fae77cbf1c2304225376cb9aff6bf9569fb16e84a2f8b3b6622e990e761b8d28e93dc384fce56208c86356e1ea50d5e399d1aee0cdd5ef504a77deda14cd46468ba70358085a7c84ce15af4519bc645f544be18049dbd8aa46d4c7ffac2a9bb3d7a61d239eda5f2e8278f583e99c27b8551e70fbcd6900af24721fd2eeea84d552c79e92bf8dc329dcd87b95fc618fd0c848dcc236e947f0635fecf9ba9a12148796f4cf0121b32c220b370f565c05903688690e476a53a323d9bf221539fe30513a8c7a43d83a0d251b311e6f37fc13b3fc7f7b97de636de5fb21b748db3c2d841702cb1c930b3275ded78b4bea2f9849347d28058473f99aba265569b354b1cf4145616c65f7afa48dd0149413c2987e08af13880b6d65b7974f7b5495d307f172903ea2036bc2c7f76df653ec8b98974d4a552e3f28da26c7f2f4cea758332142e5d7e3e70191220b7c6ae1f59f80cc8dd02ac3ea99e9bacbfe8d98225818d04c318ab302a109d799c0b01da7fbd20c0ba76729b900e09df0bcaceca2762796d8e8d2b4245eb34670f34b14ce3c08ba39a130c43c48f817bee437380f1262de3acf4344a2da9fd586e2a2d582f1ec4cc0ccd5719c51052bdb9c69858757ca80d5bfc2ae1a7eb39a4ebe4eff998c1a4b42d7540de9b170f1c735fbadfd82a4b7e5f9db6453e2119a325339dfb15a1c7f5b4aaec375fbb96aca925042f6688fb6713131883eede8359912e356222edf7637ec9fb357f3ab2b0a525d8ecd50ecb9a5d3b83bb22946e7bdd325aec720f3cb9b5da1bef1ca8c80dd0d4fa4273a0cfdf2c92906a6570da294c853b48465c80a2e215493602a1eb72e1f8d4b23acc963c5b93c18d927077db33c34a310557c57e6cb2a25d4ee4fbf9d16093222b7e371ca95090eb17a3fbf86d9cdaaae129b57e3c6a5aed0476a46957991729dfd7345ac8f3395fe33c4ea42bc82ec5710e9d62d9ad1c59e47f21af867f861db64c8c25aaf22002dcaf05670a8fb7065c48c66edcdeee05260babaa9c6bec8716536dbf832b7700cdd9c" + }, + { + "seed": "912a7661fe0e8ee0e8340cd82ea2c8679375b9dc8c41109d62100689f4eaa919", + "pub": "7e1a382b8034ceb81734504f32bbbef326a0600b043289adc5921fbd1ee1a4bf784cf7a5149d8bf3784c6145c50168437c1ad404e91acecd39f25ded0c2334222bd72806a6c8278725d71abe6d1c3b5c7f7cb255c21713b5d3c178114e48e0248693e18df84c69ff4a580ffa0a87fad3fbf6886e1c6a651643d1689e590154c43f6ec6285ce4d4e1803e2f1a4ee1210d658664f243145472a39b9ecd39ebfe902c61d05d8046bfcbe0674ad05f723aebc765a6d7d90c548fbe098933e647873984c57df940bfd734e4e1d821995993fea8d768490d6da3ce8924148758935616c8d6297bf6db936976efdec7acc4394182fa002e38274e615198218b8b4e9fdbcf7f1f177f3c954cf4a026e732b0d7621ed646130d96ce93464170b128e34b90fda234f5d5257d28b95348571be63217593384521e0412fe2b1691cd3f7181f8a856baf00933f7c8b0634990c595e11d2339ffb6ad012b11ed0302c70eef648e1023180d354bcd9b0c0d5ae9c525684f40cfa2a8572438780c5c8505e456e1ba13810d5cd486cb4e763256a9f15227699705f4f172b1ab25994a6b1cd0d77c291d2f479390816d3787c62b8cbaca908dde4f938bd1c00fe342283245dd6d606b4e396cb37b696c91cb386ca3af6ad98e6e49b66530be77825fc4be2e0677dd2497b249a67f2b5a03aecd8313ed4f1f29af4c58f1509bc2621d9fffed3404d6b0556653e6798c17bb496754b1a8d9d4bc64c805d32f7182d00a95580ea4d18fc5533f8dabadfb4b23fd54faff768ccfcd968408d64bae13d68cb0f4bbaad354e4c0ec760a11c3ad69fe4d692ea328a509a11214f711728cd9ff0c4032002897ed2c4a80f3f40267ea0f29a2662948d472d553c9aee015fc4b3fe554910d6d1c04cc8d90da1b14523a1d55f3b907a715d6aec33c97229a8c6613cdc60d9ecb2903f762b2f03f966666a3cab8487aa708fa24ae72b9a88e7ba514ab99960a31bf68841a5c6c8c164da50c2d3bda2b6b0d62bca98a3f01203cdbb3f5196cd51814e606f6fac34bdfa35097d55998646d14492bbe5b4b2ba5fa7b506ea6b50266f3e3d59cb321bc4c6e69b888db60a90bbab585e965d116651779617eb30e4be0b14ce78bb28c7ad7e796dc7a8698777257a712816461d271f3285e5e6fd2d951f555b66c6daaf5dd0e6db11ad8fcdc7d47ceb54aa25f657feb4af37676c04b48d27cee00b9e48f29dc2424667a3890cc3cf0e39d384ad3f54380c983fd1f8293f3d6e64dd013152aeef3040f6d93fcc23e7de02f5fedddecbbf3c002d19129bf86073d89f4082185ed91a9b90577bba8d54a362fc74bb7e4cf8c001de6e5f303421d5bfae75ccf4ea107d8646f23f2cfb13fe244bf0dc395ca41bc504867305ad20058367b949c5a71833bd2df37f0a9b9baa37692561d4803d8bd839b52e9da448724522f4f6db2189420eb581ead53f3bf1a1807a442af71916616107539dbba2adaaf0eb9a9cc95bcc1cfae818a7c142a88408c531ef5e8b4554902b008ad52d6ae375ef2ac8e565cf5803bcab6d98fdbd55a86016f63eb4e63b57b65daa9e1ae4dd08abe8fe05c9af815b6fc1af33d502e3fcc1871b985aa31e1d6a655c94f4c9c6443c01f6f3ada3c81677b54f9bb66a655b8b2b3c8397f9ca06ec4cb008e040d9ef8ce01ca8d2ea3ef7a2d8ec75d4fd6e5d172f4d7414c45c6caa6c7253013df765685b4a27d2d44ed76b289ed24c7efe0caa329d677dadc18b65af60db0d38670ff3223018bb0bc3be59e69c6141408d56d581185f4cd0079d561cc7d8a2221c65cc3d8341e65b4cbb8429c40a62df806a66c0900773f8597e510c09ad290", + "priv": "7e1a382b8034ceb81734504f32bbbef326a0600b043289adc5921fbd1ee1a4bf71d0dbfcc2117b81e118efdd1ae322004c61a8aff62cdea0cfe3df844b614e30f86f91baf9685984e5844f082ff3242916831a297e051947f5337746638e23cc5cae095576d1c96355f663e2dc2d5c48f36673851c4a2420975d1847a0b327eba1b82051160821c75100187204314152026aa2168214212ddb042e2486695a223204470054068ddc840c59842903a451138671dbc62c01402c4c2872c88408dc428c914865e22010a496499a82249a862c0b938482002698960492a42d821848ca869010c931d83030e1066e8344410323840cb88c11b58c12062d14847009a129a2b84dc290880b01102420529c187151b62020c14d2490305b066884160e21c220a13292494281c1366951200218a86901a40441c42c00a58063040d89246e53946d62c60808304e9938212421650cb66011b749da200192c29118a04858c25110019159064ca2045141a0912228828cb870c4080e8bb28c02148294924061b645c9923084440dc2288ac9128a20334d44a84521274202804de31480c13821d0000d500288592846224526091144c1204ee238701ac86848c00149168d43326412812c18498c91148c92920412b108499090a4384dcbb46ce036900c332883242019a44109a75183446d1031001ca06c62b009082085441451612005229401224450d40672d9c0680c94244c4281d41609ca908151248d22294a24406ad3302ce1a648e1c4601398800cc7910a100d14996959c06dd4b8291cc36dd3200e8b28108a2489d2c4298b08091b1491091145a2c42c0ca680a1020100846d53b6200c804559c08090344102460e52086cd40848c4147140148c0bb7801b25041987701449094bb205a39060d9300518024d09a64014014d8c820c0a06654a1668e0b80d84860d038201a2a68d423202e384715938421a264e00c8490c868000374dda4248a1886d89a4885b062184208522418d2012006024421c242820c311d3b2810089202118849036848320405438921c472542346a58482c0498680c9649144451103660884482ccc0459c406c94124112496cc0368a080865098808803645c8224d1cc34c20a751d8088a1491216496216422901c957093408a81b20013880c0b0042102209cb0806242432202948623684d32891008100c808894a384d19024a51247154200050486a0a3842d1a62508c805c808011b9145c09200d30471b77ca8d9029eadf93d3f8b966391d8bedc7c26946390622d1fbdf03fc1d7f09e6b09d9ecab13155798fa73c9af3af5d65e1332d6d9252a4763b72b2c397c379c5c808cf9134b0528e3da402ae95bd4c1550bc63a2467c8b95285a70c4bece907ecd92b2d720d9342d365cf185417aad3ac8b43945ff9ffc0f17ed661eebd606ac84ad4139c9da296391fbeee6fd4690951f99daf4092a985fefc9f572e7d5bae1831118e79d9b91b84dd540dece105e1991657737c44e84f4e4e71c71bd2affc2d71125e13cc9db7fa947f0f9668bada3228801b0c6ab5c6816134e8708a11cc4957e69ef5c4277b6776293d969adb3ae793c436394827fb57f8c576a71a71407093137b0c2629e17025f5f5049acfe9617fe5fa67a426715b963ec92e48416b14a235c17259f6fafa98e096589c3d559060652fc6164df542d5d6b5dc3f83658fd9f9a8b286216d6e67f987e9f7f35299cca0cfa03dbae1b83a29fac7f4f3a58b7612a04eccb65154b33a5af5cce37f118489bc06afa6139ce4627aaa742bfc1ceb778ce7ba03328098848aef290900e2736002fafaf1ea7c85387159d391e9e916524bd9ca0e03039ebcfec05988f1ff62623b28bb2dedfd0f870b6d8096f32ba88aa53c96d78764a779467274f2e273b3f2cf610722bba65faee6790c0785c303d20a2e518cc5a2e8537c21f8532f485ec7f1a9ce6bd07d34a0e2f6717106a9ea7b5cae3bf4ab96d0c30bbd85921d0af85739eb90050e2e010de234136cf44e184490310cd06d38008cb1e82da59d28d4f727e12f50eafc0d6da277188c8e0fe99320850219c1595f9fd93a1e12ba5f3d144117a6e229cd1e8eef208e0008df3342767c970d01fa43ca99a82d13f7ff514579d9225e434caf73ade1f15f7c12c62f4d3e4d78057d9b624965d20573de107c7b75306a7bd62349b66e1ccfba69fe4913c20cd8bfe1241f6ea8ef41bf3708cf3091c34fe4e540a35f0cae09a4eb745e443dd7558d43a7f9ade9e7b887fb2146e78b966b1d558b2c96ba12717e89c0f06a90a7bf76df7768ece0dcdf9d04e832da091a3accdd30c3770c605cb71f3b0a2a41d4be3228415779550ee3503be9189d5eb5ccb29c046f4f23c5b6341b781b7bb44dc778c20b0e8ebd7cc804b90b680274c01687087b60a14f73d5acce93ded3f2bb2dca53e6212964bb89aa2ca289fb610c0c9584154bb2a9d57f8a944657578e96deba6bb8237bf3232cee4bbd7c5a19b00d84c3f8c935a6e890beaf4b670998b18f3cb2d47f6835b4180c6e43a096bfbbe8a509271abe2fa7485102942dbf5e55eebfa78e3b8a7b46de394e9fc76f6364d79b373db5ecc9fcca18f978e7ea5537ce7e7389dc48264813fc61c2bd1b928347960db1283bd11f98f9a1d9de47e3c367a95e85f06acf5dafe150badade5b562d48ff49a821f004d73ff38bab9dbf1088f5027befe7af2b68aaffe1f836a15ee1170b80456f0df1857dcb691d15442c95b3bea0107babd69823f71a41d7553e6f80ebaf05d0ab37666408740a8f9f09b444895e85f3dfff17cfba2172f01fd22abc33303b36c783bad3070a1f179146dbda5b471b34ef8a256e7738260c0dae8bd824867d19ae04aafe15b5807352fd91bcdd702d647cb62335d95b7c2b5b826b53f5d2ae0b9409cc61d8ecc6487070d6a9061d6b464b419e57f813db5791d7d1662dca079107d161d311dddc7e54ae823f8f523de265948e68f21837f68f01b494cdbae2bcc938ad6aa4e1eb53af5797bf01b21ca2a3351eb1e88d6d5ef47ba3d37a5af3350127845c9365ccd0b64462ecf1d0a416048c3d62cc8a37606200675ee6133ac74a47f7c0fb82e0b9fc0e63b0bb2df28f2988940e1bfc39fdb836173ef5a4969a48530586e7f53cff1eeb2d0b16ea3652b4a58865955e93676eb911eb5ea2e3d0a94ab22d186f2e70463bf2e7dffe40f6fb5123c0edce83d2f0473cf312c13a54705b391a171d72697f7f74e06d2e9767dd50d5e624f164ab33d701932699e7087ceb017368fc1ec683b9764ad8c5cec324b54e1dc903431f81032a45a5c61f85381593e5e3bfb74a5c1a248d5441a226760c8de4fdbfa0bf13e3064ff187242ed189690151351cf28a2b3d40cbb5b3d880c3f2d797635135767f55349ed3f4a4e279b12701f9650807a4fa5cb9100be67bd5c6f8260dc8933346539128091b831359863943faba2bf92240003845c2a871d4f4585e9bfbfd34a6b5d097d0f16086bf146e2e33aa24e3d75c3425bfab50966577605b481d9fef718d235c28eeca058f658289ce2897c66401ffe3537dc6d90bf5a4e4786b274b1d3cfaed61f8d4f89a5454" + }, + { + "seed": "885b7df7cf6695f30aa3f1bc6a3840b8ca3101734118ae619166838aa3efdbcd", + "pub": "e657ea5cb1ce08341d2e59c0426796a8315c8c0373c89c2f5a10fd5667b2885bfe5e31a1e0339bf83f848092b623761c8deada4b6139ea700a1a91e5164b99357fad504b095a74ad684e4de51471d0976ed23e6d8164f7469e50478a91d45ea9caf0dea00a618fd60756ab09e617293db9859c82eddff4e5274af10019a233309073ed469f8e438ccd82894f6d1f095c2a40f05bba63b58984458a202016849fa2a963ff6b7eb55c3d881aaa46399e76e2ecb8ed8f05f15dc60694690b1e7687608d5b97684f2ef59a24625c4ffa0ea6c3cf410c275e37b6fd936c29a803f3f1e90ea53b7516785aac68c7ab89ef15562aec97e258b30d7b3947e655228a5073232142fb96d4cd64c562c2d0803d109db76a2828e0a90cf87ea097e3791105008b015e236fbbb1e45a19c8a46132ee13b651b81702c2d19a41fb3feee728bc5e67dceb3236ee5689e9eddc9201ea91670459596a35de88552a3d18f47f24ca86717b77702808398238d59642a38fa10f6d17bf28de18184075e6c4ce4b50ae2ae0d59d080694e13fd6500f96ab34b4e2882755a11abb9f4f56a275f43329a6cdc6fea7220f63d6060aa389e5328b4de152a17575369a62ec41cae80f4f7fe22ee78225485de16af283024604d979fbe7567c8a6184ae0c967ba5a8d8245e8cf9623b36574aa27e0e3f8f2f5d8a1466d4c45d17df60ffc6cfcd9c7b029763c3139087d7ae8b61f0bf34a1e03261b44106a5a18c8655ad7740b335564b2c2ddaa81a52307d0ed0dd777d955c90db55682f7a5563d68dce388a41e65503fa2b5fca33f636062dc97bf3cdd9201dc1b973a4eae1b7a45a72573dcab1029e221e9efa2863440c4a41b5bb23fd457cc5847e80628b8d6f5e713789df20350b7bb295344843023d06c793ba30f9c4552bc7579e484119472f3153d2621c948ddb9701a205e9b56acbf08684ca237afab9dc9894b885b24aabb86bc22b8afa248dfb776eebacee5719e93e8455132ee28b6c9f716bae0b91f22c14a658ac1cc6d15bbfe1184890d5b0573ca167bf3f3531637f0be6700e5d8b279650cce11d5246097bca3b43722396708cd0a4970db56263fb8c04934ab7edb97613276dd5ecc5555f45c1bf0405e83655d09a79e179c877b7b53750b2d6862a6d96b2e3112a81d52bd3a96d233e6b9825cc48205b344b3844d1b10469c4472d2545f9594eef1340400a9634848dc5714f21bba6b3d12df8596032ce265954890b71239ccdd141872b4652f32b861a5e01b2e441d2d7765e489e202f79d6dabb09c082fbef56a3d32ac30dd76434be495ae13fcab1e9cd16cbd5fd3e9cc50c25503c5141068966db483dcff107a0505aa3dde60c66ff638c7f84bff86f3bc5c2f8daff19bc8ed84a2df6f39c04e61f77b0fdf1d9abf204795ad545d80e7524508f221c9b80b244a396aa46ff0dce813e704bd9c402a5317253e481a8742ae6b99a5d7f5bfc0d5a266b3ee57a84588f7e825419fad1d30bf7f856585bb6cb22e7639cb6f02dd52cccf9502cf30f4c9fb7a54291058f81faa4b1525c9aa6c5dc798263c07922c428e43027eb4f4db3e41120ded9a74cf1324b5e9a4612676134e650c22300dc078dd5b84611bf105703edb7d64ad68af62efa52d8cd684dd93599adf15eb2a0a51edc60633741ad1c9cbc15c092c2717c619d01fc6e2483b6831dd8672b97fc0cc7e86fe8689d30c02d2b710aecec599e0c706b7284e13174b1c1d4c4f5f88335157dd837a823488d0e1219a2d92f9ebfe7b4347c687a4da3ea6026f96712632cf9abe869812f6f12f5200c22c5f52e12e1a08dec556acad92ae79fdb4073146d133c000e", + "priv": "e657ea5cb1ce08341d2e59c0426796a8315c8c0373c89c2f5a10fd5667b2885b04ec298f3aa93654e01a33009c14a0e4185cbd4245ce8b5a2685df7e24976d3a709bf64d5baaa1791d413f8da075a552705c11cba7404420c31a291fac5824c9176539015c3f03ad5e63646a93314ea25909acf0c95f7015dc8446d063fac8d6199650191125cb848122c83104a3092434615b442e48922452000019c160222484122700cc322d02c9408994292109428a42494a0262100252c01801081040a0c6908242001c2530c2340963986c14c90c18294a0a870104a99082a04548108889167093322a98288dd1a42801286142486219a8441ba5248120302222080a139109b2240226410b2888a228919c0892dc420c9802051aa92c04b35151168e18c044c4c66041808844b04de488280042868188409142461c812c12286510250e18a60921220a14c44918b18401c70c80a684a2a06024468e18c12d82902c89b68914198e80148511c790d2c82918093009a170d1948c929061a0026c5426508a26821b494c92022d89305002886990184cd498491949268020889b40110c3584a2b26d9280450112485a288d82480c9a02898a846cdbb87022272521b5841216421b840ddaa64063146dc4c69184c8299236020a220d032445081385903409028089c8880022238a5a8050d310854a124ec9b46103122d8ba44dcc149160128840b6201cb9015904040433890b0802c18209a1468aa13289c8a611e0143199b00981462100b9611396441a982023258a4312489c36680c0670e43626e11290d8b64591b22918136a049501241942249471201628404662432848441801c12848a42092420425dc8449db124e0a468c03274420322d0cb06c0b81110aa70c1b83710a312221497061a4609c3222238664d2c28d0c92259a94315a442222088e8a3690c8186859364c08438810226d22144c8122840b22520a2620a28048dc128091904dc1381252848413b771e382909a2602e4b0714b186022a5889ab6514cc80161c200cb465284c801c82865d230268b100620310dda184ad0a208a23891c8028141162958b068432671c224658ca2614280290ac24920401042c68002444164846018a76020021013924814272d8b160a13c510d9347080a00dd9380843968192a24019b16d40423184a031d2442020988892947098064a0cb94592422c840084db202213928000a38d94986812140694326914488194168289a46412a760b8aa3d5dec680e7b2d97ed475a39eeabed483b48ba9b453a7d9d8ae851ef1f82fa57bd3f05d03180b744766f3132d868f1e61bb3529523f85c90e7071137bcc54ff472b378e664a0117451ce47f7234575082e8b66941db645d27aca53df4056c6a79d41d8efc3a27c676e83c43019cb56c8676ffabe1f924456fb102c2a1059113a1d049ce6063a56d7b0ea8eec76674cb36152c599d78b6b9adbd11fca00f0b3e2165c6130029913addbdb64e5cffcaa65eddc272205949a0fc601ed6b31cbc710cd4881e4f51d3489bd97342fa8ccacf9b03f07d952c37217c057efd8348534d0834b6537d14b1898d5c6644d20285a4269fb7d20585019acac0634c9a7a0bc91e595637f9b16dc454f38a1cb39a66e9d8bba5c496644d0584ca0ed14a03f4b03901c82d6ccfc5151c84103eebc22f2d190766c60c31f3f80aecf8e3caf750e02d2745f4224c4f4390be9ba43da82b5b3270bcfc448aecafc87eb2065c0d4c169609b78b3c4531be26a84a4957a5e40037c4ba223ea5f19837ac3078cc5219770445a57a23b2255751a654a84a95983bfe09290a058646184a4019a678e0cfcc3169ef5ce169956a27cd987172efa8f635b81bca50c1f12577e126d7ac0fbcb0356220da0bd2f3ebb2c0474cab6cce48d9ff58743eca8bf0e3c6a85d045edd4426cc33f86093bb76d946e36779d7e8c6d451027a826e8a8c2a1dc6ec5658df8804c25f17d7c5bb4b8598d495a8e198bc43aa9b75d85f5abe22d95788a59ece32f4eee60d30980cbee238b314d520918e0fc73bab0b8242ebf523b9cbfb606a68d7f8884da73979bf826507f7a348266925c8d1168272f7851d5610034e97dacd3b014e0ac6a04873d1d1700e76231cdeb7e8abc18ef36ed950744bdf888f958fd7a3955aeff2016ec2d6c1896ccb4a6fb558f07ff180d786925ee1e818bed17889f7d3b0ceea8d5ce41f477c4b6b4bbc17d1251a0e4fcd8f42ab3f0b95a2e060c688a6b912e4b7438c4cd144fa4dd2b3d7e6212497cccd111033523bbc907eb29c7674a80d0ba4c27e24ce74998475a64c0fd82a45b3bfdf70971cef736b1bb457f30d918337d7ea5845b97c1e3232440dc9c82140e63cf88eeec3ea4b5bccc898ef909a556fce59d1d4fd6893d3f075dbc37f1d25e54a5fad6b647d2eafba16cc4c47e253ebc0885e5587bcbdba3770082864899654722f29058b8bd14c739463b340962556e9fab20f1b81fc799d083bd2839c9fb220fd28f0319f64512eae81dec1952cb9a74976f42e67b72533e68651d1d4437dcecd21840318e4ba3989a965c8671bbb2594f32797efa3ec720cea86b456361819e9dc13c54381a03339f35856d31785ca4b863c0e896aced0016fb4f0c7abd5c6bbd0ecc8be62a87589df02b6aaf03acadce4b7b72b9f60b9b1aa662b96a13e10c2bae862f9630c9cfe1fea119bae1aaf0c7df95fc08933923addbc483d134c60ba303fceb4096a389a333381dd8355fd3f39f9d9d6a79dee51282a153e55e5823759f4b2258e7cc0dcf0d41b40f76e3435a68dd7e92a27bce126eaaadd224a049fe4f920bbcafe1a057b29e9b9e64e64455ba54140bb4f0e4d4d20507c493e17ae36c6b6a36dd3853894ffb44729721714abb859908d84982071137bf5b359e3019a7a267ddb333d506db8a918c2522f267b5f4700a9179c7c0668cfb0be412ea7f4b36c74a1761c50e15c5d292b59e9c1e90bfafadc8c05d7146fdc9df78777e886a0402423072381fee790f5a0a5f6b64f3bebe02ccc98ec0205d30a471ef69cfc22cfc1c529ffc3959d2c57ca744170f3e95ef0a9166c67a4bb41ba66a9d46556ebdd2e8c6044eadb0bd49e298d806456062cfde32c017d881629381bb0b93c14f70fcb9a48e7714b9de2e3f636bfd1167e1e3476928882b333968af9f3953fe40c51df6eedfe056cf71a962d936a821fbfb9191d60096c27cfd8f92bb64ba4d7b47ec12dfe0bde1ce9e25ba50b33622bf347cf09c607a60c9ae80646b6958b8a5873551b4f927653ed379349d3382d856602722dbed05dcd0999cb88a20e04006b3e302e165b5aa421e1c4226c19ee0c75f7f6cd46de0f243f3367886ce804e57ce16a3db51304dc2492649cff16731fc748516020c7b2ac3134b75ebd685088d177b7bfe1eba595c798619476b8b2f626d3837a0397a777a84aaab4e998ce5d1174399bcfe5c5241e5288a241c4b34036f8b30c0862dfba419ac96de6f4a0a5e3eedb0532baa2defece28b912d6f50707f5cf743dde5bd33d98e9108fad9e83812f0bb143fb0f82cee14db90531ae1738978bb69c30e7488fda9232e6baf117fa45601e300" + }, + { + "seed": "658828b30ffc0d7eadf8cf3e754c0b40b6d9f70f415688b9de865e0c8c3bd9b8", + "pub": "3c7d4b0add86db54db40b76f8f0b54c62901f52708849adea8dcbfac2031a4f8a8c5e0649ebf447c68927db564000ca468108a33097737e66b43744b0bd8b9c8d1851d8d95ac30ca888fede8f3d7de2d0a36a8ad2067e6d37df99329187d17c0aee7d9c4cfd3fcf9bc00ef4de08ebe6fad356f7f0493826f2bb73ed1919d3cff36efe633fbff5876c516ddcbf1dc2b6216a74af1a7dfcbe9c10054e68ac30890de8f56e52cd89307cdfee2d0c7cb46cedb0cf9a07ab0c00e0a09c59077104a525abefc57d4f2d97fe0da2c11c469c9a68ab584514975670dfcfdd2369f91d8ece9bd6500dbf95d1d0edd179a968b5f07a46c0add1f476939c9e1f28f7a26a3bde80f710cc5ef23840494961a14145fc92126b2992d6024c1a2c4929416fcf4a3c3e2149ca0dd38163e7bf3bfc268bb840f1e5430ecc973136517ad23ef9bf98ed180db59c81a1f77d4bbfbabebfe72d4f625761b741fb4ba8464905fed4242ff4349a2d63b605d25dcc79b0b6cebf3c94522e8cc87e55ad6b53e79264da61e2cd5f713d064a0849dbd3607dccb2f4598d2a7fb6a878161137875db4d35dcfe5264c8a8b34bb2f3b5099a738abf0b7c295da1497c8620b28fdb48c2d6a7396873670944c61477220a400ee5618e8e4fdccb30f76ec7591003a6bca221bff10aee5ba8ac04512c29037c05eedee6d6adb3106aa3e6ccb4d5d567fb693a46bbfad9b555a2640a9984df3ec324158e49c8ccec771f9219df4fa86c62ff1962dd99174c6739a428918fd2ca7d66b7d54f983bfd542a612c71e99bcbb9b09011a6c11aa89a872b18c6ef6c28d87a068eb4bedc22e4312febc6f6169a195baa07499755110e43b64895d348589c01c54831775c843a4dc18564942a53d1ae2e5913d18f2852d0e1dc880fc78fb01668f99a77c58c1f642fed6504c11bc5fcae9757596f884d7b83521f07b3ce8c3e33f8422d2621c414b1b32250398a97235e79f86408ce2c82923901bb317f7a399bc17e05353fc0b02886422a33dcdb22b42d31d01458675a43a0c0d532a2b4debce202bcb5d9386521ffcd2b4556eb07914ac2f3b1cba224fb9ec5c47afafc8d2eaea24accde7a18db8f9dc7b2bc5e90d69d4b41ffae5f811b724a8ca6ea7fed987c9266b0a3c40e59bde7232edca46b4b4a12648fb146f0192d24e341cc07b214f75f2d6d7e828356711dd4fd9f5e9db7fa984ac3fac6c904f8bbdb2af1522708ffb7e7e85b925cf180713b526b90b4149ab22de5bd25a5c5ff4ada3dd4dbf3600f2c7a909956ba31e36575d34b1ab9c899090e061d4e999df6ad50c95d4ccf79ce1e39e513a13aaa9713f08787ed40f6ea11e4bc44175fad4510c67c5d1b07110316db3e7bc63efe64faa2504b14a4db19b97c123d7c20f25c483fce98cf7aa749fe9465a067be82807c00b9b6f709714274d64208b2d44b50a06d9856cb89eab677539dd9b595eed410f581d99a733d1e5158b8ef1488f08160f99d34613bb4cf04d115976fd04f190033c8c3881c2ae6c4c63f03390f0cfdf6564f1992e628525d25c8a55d3cd2f9f2ccca0f31c1fcfbafff97e493edc42ae8223c891ec989d17c5fe85b5ef1fe2c2c5df38b17a8dab26fd2f85865351f21ea261ec96f8609c9ee91f67da0eb6356f461d0d1b6cdb7c27d6b2d47666c14a11bbf5f4c5adc7c5d86ad04eb2e46261d6725ed55a2ce49caf880722d3a8bacc10bf8bdcd20cad0bc8f9fe700eda211bf8e1d0cc65cbd663737eedb7843e956801520f7b800ee75778930e0ae91f127330ecb8d3a0dc28ef39c2cfd6659ec2db93e30999fbd59cfdc3ae39c423eedd2609fd8df475f03cc1b624ef1", + "priv": "3c7d4b0add86db54db40b76f8f0b54c62901f52708849adea8dcbfac2031a4f8a7aa9f102b413b49b27f8002ed446629d365c4241b4c83e0a8e2623c787a149dd1e31b8fa7bce228106a87726516e4cb5c4e608bbaef80368b8dbb33b9d991fa293d86c08456f0f92d40f7f1b592339c98df48650b7ebcce9317810bac44236844a270110184024212d3488a98c448e1a001092125c832461a37420aa331ca366dd24822c300111929690c0124030350100924a4982d9920240b417054084219b70821454952b26158040c99126e12215022370160844082144c9ab4085b2042142206db4882d01251d202484c360850b251e3362859426918b208092972d3266512000e4030085a84415ab63010a724a42829c41251532441d22651444670cb36449c866894860900814510b141d9a42882860114338c1014801c216441366c51c8289b0452d4162a24918c03864894826004a70c424292884051210885d4288880a4648ab60d92464609a54da0c22858040024336c09852d032625a0a60543a66199300e08b52c1214511c446cca0662228904c2b421134762c248320c12414c3491d0905009100d140231a33268ca1804e110024b346c0a974041128a6382908c307061c44c0c1065d086619824728290501b104a1b38624c2484cb446a8320700b180e22432d5a242d198290cb84851a9121c0148100018a4194601094051009441882092342041b44890a04885330648a400c993820983241103704c3424098347002a62c224044dc466498442894344999468cc3b64823256258088209016290120582a428631241d24868224029528824ca406c8ac08401322d081709544682d3b289a3844453a869d9046ee0b631c4b64d612281218101e3084ac8382458082e10370e031408e04012d4b864d4046cd14290d016001c95652310021381010100650a0520db1870a410114b1071cb941140184c8b222a99388e60b2256144911a2480c0208da41072e4064963c88dd3926c13334e40a809938025134264c2368dd1a800421260ccb6484b1491122589e3120e1b1406d322921b413014432ad1c288c8b29099b66da412806202120b1428a4c084c8b884c8b64de3c88491180a22a40d203192d2c68040a6818a9001149171cb2846dc12010c864c1927110c480980c03003354011b76ccb266cd0b6481b842558909022a94c94085220945142b8311a146d992062c4a28c89b22cc98029492630e4842863360d0b094c88d115f40af75591f4ff17c2a85675d2f9e25895555c6fbf63fe3f6f5385464996441505bd91fe85e98d912297cd38029047a412b036f1860eb35dc1a7f967e7cada292762fee43b1c833036751902e4bc1b6f7aaa241cd3ae5266739476095ec7bc4cb71f8348f705a5baffff836d77d8bbe46e5605c29c1f474a5c89193e88d4f2dc019026e667a82cb94edf89664860863f33d616928efee0b8d7e29f4e5b7cffe99326d8c6f397fd21400ba6d3934d6a0752974272d957eec328f37798486a508d2c78d65652782539fe03ba9f640199832f97f4667e655cb958ce1f66e8536b4a2e77f7e81400ab56ba853726d50b059c2061e359acace131085a26b6f4876f98962bdbbc81c6e21f958d18f58653ca3506e7dc1c80a9b94312120d89f66ca4bba57ca9e9ef8d8ac01a89716eb613b66b071895003c55dedbb5ed6f7d923337b14e8b839f14a816f731a8b79bd1647a0346574ebc1c99ec7a651152e6162f9f2f0ba0e0a007b9301cb082f22759f2b57e400bfe69271a5325f6215e9144a0f6f6f0edb0b2203c9a77596dc5bce2a3adc7a895431edb956bfb91076509f1b99fca9ad3cae4f4b7159bc5c99872c0d267b9a81cd87b3690a739e26b8d15067715c5a86a0f7e4179468aa8941b999e1bb2b9d8e8fc040ecd0b869815d32dd9b11a41f2edd724a18800977903da991191713e2e098ac750dc697ecfe4fc43487b38a5c2733dc0d842716e856673a1ce3ed046a5daf03ead9a18514b27a2713cff3742abce3b6b85ec6c494d94f655f7089d1da7fa6d18d82bc8f443a67b00bcab82964a0b377f595896dc8195ae8fa2fcbfa71be62f23b802256a6fb07237971276fcf88ee15e41eaa263f958d19c9e7b7ffca6b30fa91a304601f439786313364a03e73d115cddb89eee9fc7104d223f7afa582a1d0ea8daa9e706240239840b4a154107d0e5848f428a67d14e8c87a7685c3de8e1bc896ada3b67e97186fc75e5fb8fbfb9febd0d68f2c7945406c56e574aef3a9739a96710bf8ca7235185f065ca059d75b3cfeea0b049bafcebbffd67f63d66262271a3ff6f5c115f58e523b0474fa9130108e93f4b7e44e084bd2b35d41af8436405d0f8df3b9f780964e6d552f09735e58e74e567833d0753645199a39c1e98040b5ef86d388a93036c4908dde0021d2b70085a3b3652220358e44851cbc3b4f35b6a246573528177c6fd231181a24d9479329b76fdf416117cc2852a6ec4daaf4f6058ce99b8348375937647de52bb0b3d3b136fa632e708c8c5119c6085d89da0b98f14255bc61b1a308bf38d8ba61da39d7b5b5d3e68cedc12d4a7ba42438e5663fbce1c3bbac9faac4635f2740602e29d2845b7460c5e965dafc618cf6d1afc0497317c044a2adea76bd851f9a81c7a659dc152ecfd94687cb446c1c3c0e4a094b4fb71a035f5d1bff6b6ff3633c9deb6b2fb37d309a67050521d6d2bf7a6a43a48e20176837b2e571e6ee5caf4f001b7abc11c431d5d8c0d0fecfcb9685c5827302bafe15528936449a741c749d3d1d544daa4c0bfe982ed274d13cc00ce91d40c0876f4e5fd3ecacd5b836e1321cf862951e4177c528a56ee0259e61da4b8f1dc5ec786269749508ee6c83f86fea11863a99ab8add986eaa8a822c644134bd1e13e248d74595ae1409e79035afb7caf640f6689b49c5c0f3800257e4e163dc9c2b57a672593365ba3cbbdc0c5d683da3908b0a98f3d8456f9db786578d2ae605fc1cf1a9d11e598915f70cb75f6094cd38ba8a5a830468d262ddb6aa08238c5ed1d9b4213d3234d9ad19270f84560cb4a8b95f8b9aaf42b7da406f7d915443c37efc2e6cdd7da922976ade9d06b449bf966f9644e3cad50697f671e985656e99f116b0bb4ec254fd9be5c1d12f66213521d8d02fe264ab4e64e5162eba7dd239d191791bb8d08b7f0201c02256e33770aa2fc68c24ba0c5a370600cb7990b5eb8d0c5b5b401b71914b86df50c647cf32d98891cfa9918cb3a141a9117b6bf6f58bafab4c5b3e947d31854a094fa79069817fb2e779ad650fba85081dcb0c5ea416a850fb4e93459cf79c4c6c0207582c2b17043fe87208dab93ac589ee63e41531dc6c662217bc9275b2a746ea8f6f231248cebcffb8ce3788a7e00dde53f8c1faae36d72aafc225e10026b67f2e82f986b457145d4efa8282a84ee0b1361696aa2002ccc0ad84c65622cf8b2e6e2b56fa426720df75df76c01da2fbd90625f848b88e261f09583d9b950fe3141fd272f31633d0630466a90653864d52b0cbd641f5950af58292cf21bb77dc033194d81c6cc070ec1731fda5951698ec8c8788e2a66459e62fd" + }, + { + "seed": "03834ca530ee44dee4ebc059283cd8c8154871efe5fc84a7cf945e4e084bc080", + "pub": "c4680260b24be4bc2914d5d459c3d433675f87bbff91115df1109d37fb7439725c410693613c00a1c1b0f9c1c5a5df21877c4b3c4edbfd388b3a17d9aa6ca5882fd1a1bdcc0be7e3f3f8c0144fb9d4a591ac6778b4e2a0b03e70edac16cdffd57eeece41072b801ddfadebd38f1a888b59299eb13d638336b54405127d31e82b6dc3dca0860f6150f3d5f22f69ab0834e4207efa5956d5ecb74d6f9a6ddd9e7e6522d4333dcc1f4706a4d4cf1772a2b1b88cf36bfacafdd15f6799d53adc98fedc40fa5958e78804c2ed1be76706faae0671d9544ce64c0f15632820b822103153a764eef265d870a35207289f18ecc5af08da9705d89f3cda09814de4b9a1a8b698ef2010ecf6487feea73240d7611e52f206a7b9351dbb13f3d9720b9e67332866c89f25bcb248af99c2dced21b44f595facbc25e594655f922c430dfb8d5158d70261a785cb861011b919faf10b5e338d33a4a0852c5ab4e3233a78f85a3788e11663ce4903033313f8654ce1df2a95c28bde03d7a00a58f45a5d9bf865d8226a23f78aeb3d606036e039856918ea5664c60f11c5bbe065afb870485391bf41035ae9262bd3d98791a69541ed4679300904963796b77c4e8f0be88e0f7ccc3914640873a83cd5047fc61af2cbba2f7d359850e5df3291ccc3e9d0c3a87d34fdcdbcc2abe10be5254699e510cbcc20723a8ef243a30a6c6bc7927e1920b80de860dcfc4679c0c0a60ce5637260192be9016ae6c1715cec2f0254b121017f3b2145e55d00a9fecfe40518904f39e8c7b2084e04c42417870f473fe1e0950c61418bcc7b87fec86177b9b52c704f542d7e8dca57b44380c459a69f8cfe38ef6c250bf1ab8afdb36773605fb1aad72f735be7c2425aa4c5514bbf0752d8c23a7e1188d67cc5117c078d53d98567da7e1a1bc2cba7e1a929f175211645af78964f97e52eeeb73ed6004e2edb147a424c84a49c9f8cc5fc1161fff5aacc7b6f5ee6703a8d324f6cd0c31bd1a8b10801a2da191f534b2cef2010960db26c0a7b3a80bdcec1f96b0285696b5c42dfb902b16a6dbffa60fd5fa36d1da5b93a46bfb3a634d0a97bafcfd47c73dd83b9dc14ec7255b33dff9199d3506a2338db5389c999d63580776d4e4d84333d8c2d1768ed4689e4655efe46ebca710e81da5d751eac1022025a5a394db2d43c74d1a572417f6f1967caed9ed36916f077473d1d7929cb25ca85ee0769b0fc7acd53ce36d5d281b8eb4c905a122b70edbed753c28db4a0873a2cb1f74be1df54f73cfd743acb9e4aa328eaedeb45ca25714d26ad0b389fedcfce3226d32c1ac624dd565d8a6162b1ea7edd3f275067376620339e77c6555d93abd3f2944d27da11623f43647e88005cd8ddd6fa9cdcbe0d1622e337520f75df5f3455f9a1cfff579d2fd225f57097b7296be79ec58ca42273b1af623b083fe526d7528b25abb7b9f721617ffe720541b8d7366dfd519c4c55950d16949839825a9371df7fd2d45e56162732753846040cff66bb26b6be9c9c08c517cb25c466c8bd9dbdb5b55f33c5250a2109198e8267918fce4f82f3d9cfc251f09d59dfb9f3379ba8eedb505b5456580317684cfb26a4b8e82d1d6d8a7f7600edc063c269aaf20ba3dc2b9ff8c1fcf4222a561c9146dbb17ba8c285a776f90b5b201a28373bc45e2a711e5030427e45c8ac6f4b9e959670bb07a1613e01b507734b17766a1440a8aed9b0d3a353efbaa04cb4211d1c60b0b47bc43db9a07f1d636383d0d4db2a39c04b5c9d6494ed59dea6e24d01085829948d12af4876f1f6ef1322a684e3e433ff25446a70de4aaf2e1b70eb2d22c55c47a44d3f1727304e8255", + "priv": "c4680260b24be4bc2914d5d459c3d433675f87bbff91115df1109d37fb7439725bd11ae9c4181c64d8323de0767ff237a4ddca6475cbaec564779523d955ca35ed814b25076b9cae11898c6eca01837b0d37e88aadefccec1f9bb6e34224b9ed4c64f10efd771a8df76c6e82793e7434f856b5d37127180df2d83d9245ad3f5de3220e12104aa3946592908150360e01362c481232118031e330604ba8209ba005543652923620089944022021941431c44871c4c0001c2062c886051b094981b229c23068d3968514910d98a24d9118201a468e8c345014a490422265cc80898c30420a26109a0444c12485612688082569e01004021126e0802409c340c130724b222d632029440630a2c0719b02421007720ba285c024280327259b30090c082d94044a0239291249705b801023966c1b462c89386a041786c4062ea3366023806880960d02a13021927109104ae0104849a42822a645e3a02418c900ca1029e2268a13c564443850128451d18620911065e42668233531002324c98070c204644b96304808281302080225248a347280348402c7290a849008393119c08c2009601b891009b72808384d4a48210ba0318418824b288c60046de3388902102248a0858800125a96299c846c93b43003486a61007200280e4c140652a2401821111c2326a4048acb042adc105243b8501837824c1442894448c4346691c049512212a0142600a70504c5601b434ac000228282210435616344481b2244da4432caa6258ba64401410c889804e1b625db188ee1b8805a3406120188cb1080043332cc86855886044bb69182440ce034600ac50dd00680c8166603a50d213926832869c90425d2284c20412844c0085ac86944c4814a0621c40630031171a2028549a40013c525182382cc14215ba21193440a5b282251924c82909112b2105a308e6444501b448208a06818318e4a06256038091481108882610a1549e0402593880d98468e9c1030632431e3b468d1980c411212013510d994601125918b8240d2188e53829082863183328a408690c102465908911cc270a1020200153081487184926824132c62c65020396ad9068459281083406d89108e0b08516208214a0028032621c2226e0413242213321c214208364c8c126893340a5990281c379112865060262cc9908d11462903a2244c1866a4c8682282710a41664290305b1010c400860b934d480200909484cb929010034291306a810684993080843086fbce9345af7d3fe2b562b840ff0b6c709d8b097d8a760821cd6a5c9065652386b8f7ea3181241c55a43750a2c610a9c2d7266578cee9ce788ee9f0dacf01bdbe35a90cc433ea92f79fcba7734ed5a3ccc8b86220ca283bcaa30c40c00b3ed47ef75910deeb4620a9db27a9b6a7e94569927d13d3ef68ec07a3a2dc4441d6d53c7afae417fb099ce354726f1e1edf45128bb25126599f29ebdc8e9abbaa57f14bac51d678555ff069c67ec65894065ae8ab6ff6573e7feb96567f55c3f0f96ea898424c9109cedf46a962f577549f0900fba979b842fa2879e0431b9d45c5903018eb9594f90124b54002777a18fe963ef1d76df30fed1b5b71d567001197ff397a2eec7682da4b0545a20ecb05deeb622ecf97a9d9c334f7a070f1e52c8de008e207db24bee58f2257a0407222e880f4d7ba72a2a6ebfa0fa480b3c6db702ebd07a35fbd58086d4a39f0f861bae7fc7eb632836cfd7fb8cd88cb9f68427572f3440882a7f4a7d9e5c0c312e4ab2576a73fe80ca958479cb370c13b73da750d87eaf0e01c57afc4914a2f1f27453937fcf5aa8b6d5eaa1112fe82e5dbbf210ec95273a6b602fda0832ab8f388944451a47b00e7b67616fde0a00f8c91e60df8b4fe41d5f852d1eef09aa7b5e7f0c3fc89961047bb5fc03a5d2b47606b1a101b3bdc925891cc963a41a0c6902b7c0350af6c82a130ee7dabed433082603c58417360bbf27171a48f3086d36c0ec029cebef8c1fa70aa824be6ddbd7ea6391cdbff285caf55647b72d1b739726219999f968e2494b545d8d23bfd896b5506cf8f893bd84cd8ecf766acc4ce7ceb1050def0ee8fae404f074bef71c715117b03c4e312331905d48e305de5f9d71a05d588f1dbf000ad9d160be21382bca1dcdcf7579e105ee2e6a1d744a0404b70358c39a8422d5c43a622661009f0a187a049b12230fcce7bd764541db9e889d820aed7fea9ace59d63293c6eb62ff6c0812978ab4b45efbbb5f6be84139304b0586c3afeec169402d27e3847dc0fa09f5b2c26f7fc97d85963acc86204dbd96a6bacd8c3406165442c01da97accc49dd78c102071ff47df7e1c300530651c65d60688f85c7247576e2f975850565f5f4cc42696b47777d112b5174bf82385eb9a22fe1d5ed2ef3a23fb8eaab62b21e921e1ce79b5422dbf21e51efe29723bedf0a53768097c6b031863c9e41a75eac1b8b8c9e1ecead9220dedb3d5eef913d3081ddf98b82cba5f98943740bd1b0037e6e44d61b27d51a53300e8aa5dbbe653c29b70481877c6d81ee5b9086812db3f178bb1007bb4eec5cef62e2e4a123a7baf6c9c5ff0bf5a8a666f1be1fa001cebb8292034348dd9c4cd1d72fc1b0adf20fb94daa19ff7af829d5e7111b4b2cbcb8fc235a57e6c61875949234098dbde1c5df1512982d1cf2a29205de7094bdfcf2cdf6bdf2faff4360347f6d7a2419afb3ff24be4eccf8e95912752f63301d04c93192e36cfe04ae90bd0f71e149968b9cc68e0474c71365efc1dd16e023d1b65e3b92392a1f13746d823f1662b298719627c31cfcfeda4a4a3ca8ecf3868f4399e0e732d9f929040ba7ed82d9ca8d00dfe271634e92c8b3592176fa04b8d7aed0bb5e24c6591725dbe2fc1f91e3b5be6c7947409201e585d047177786bb548f51f74c2ebda560913168062a7428addad7fb1a868b1cd66abc94e797b989e70db375729091a37eed37cfe46e56dcd5bbf0d3fbdc51d3877ef31e6771a81abba5fc65bbe533c1c83853ed696545e0ebb4df98710ad40a56b0ffe87f911d58a727bdf5d31a23627282c512ad9fdd87960c2b2e0568039f58ac5691df4656d034aa2ffd3abf4f739dd6cc06a624cb15ff475b73886fffd703010a2798605b38d8f18a156368be1ec206bd7daac27fc9f828571ebcfa142b04ec5228420d0a31e8562ee001af6e9ef2a7f585a3685bce59cefa010a7995bd6edd14e8a29d7556b149061aae464ef2db99beee91360d093b13032f2fb55694a14ac82eeb7a0e3eb53788cbe9938373a972fafdd9223f00b7ade5ec5d80860b6ec8b76e4643b44783b254805dce916a813814ff350b4408da3881d12714ece20bf344f3fe2266ed3042785092cbb4d6433c5d47c012dfc2a32fa551f9a94673a1cacbab0ae0b3c96b46a4f4ab25defc974fc9dcc0b12d98f5c9df4923b0f1f59c899d7e1e5290280ada678b0158b734a67329712a82183009300dcba9021ebb44baadc87b1180ee017128ccf21d042b45473f19a6542c5d233642904f690e00fbe5706bf2746a916a2edda363b8d14c81327799042f3c1c7252d3ebb8023d5eacbe1edae22b77cc8f8220eb5fa445" + }, + { + "seed": "9305119ec76eca4d7241ab89a182730b4256c60a3731c6bdffe705e69143f901", + "pub": "8c13f3bc3c30694acc7b56fdb9a0f23831f059d56e6d547c7e2aca9f3f95061a8452ac21768474f5da5952451b59939bd5e8c5940fee74dc96752b1f3ac342677f9c540d9587feb7b83290605273ef9fd970c84ae94dbf95dea507694e7059b8ba0c10fe73e2db5fe7c157677e96b04cc5ba72e490141f779049829f42c0cd5c3471a0fb400a4f30d5adb522119d55be115d513f229eea2a203203b0246eee70b876623b5d1a89b2e7606fe892099832e38d50cf84fcfcab9e0d14485cc8074c1937e6dff101d3f70bdf834c9677f16dbb587db5313e5e9777d86b7a6f803e9b0af2833c48e86195f914be6df28316716dad5708a2860d014ffa58c4d3ac98c5eb9de08984de7e378beddf2a687bf9ecd4903bb36665f722cb47e15b422684f29cf6d3882e885d0372634788e28197c4d66e7240fcaa76bf63796ee1a1982685bdbed44d33163da88ac25ef37d4664f89057a8df8b9df3b8fccb1b2e935578af528701ef7290c8b1a746b6fa7c4c7b4ea965fc6c19a7c66e8ecd2f24743941868f2e32adfd18776d9f8074d79accea40f418a97ab5c9b26c154c515623ea36cbb7d04e23973c86edbeac41cf808bd745a46d41bcbce34a414065afa3372ac064f3477c89865284d2688594b6a4c825c338db7725930db78cee7b877ad055ef1c09dfb2d50f0e2904c59c7575aff389ac533cf4a8cd2a0586c3eae6bea2e9c049a480cc5f85969cbb02de7db179e67d2275b98a295bd07e9dc7130c341354a46597831b37f586c6cf94a9004ecc19a4aa73d9214894f5f8d0f68b9995f3930fcb26e7fda727e589194fdf8ebdfd1076f48fcc6f709895c653be10bd09c71518262656d03b965a8c4cf2c83ad8cf49813d2a98690e0188f3d12d48e4794d8ef1ed9b89dcfed08acbebbe2c9e364fded4f8558c9414a224fe1eff9516b280f1e42fac28cce0a47a528f0cc43ffdabc78a97bec58620c576a1029cd5b63caae58590abef422c1fd33ca1912c37057ee54f6cb8ab84512ea59a5e9892d7f7737a78797a00cc16f46a53128d0b40d7cd215d0e8f810cd27253d1f8f879652e6a67cf1ed6f2d907f785249bad4e78dd537d85364b88ce95caf4fec0d1a6fa937c6dad5f6e50de3b519636b879df9a2d5758303d7d2205778d6df8f5ebe21e8449893a93244a2b1c550f1aae9a25f955b8542816719137d7dfc19c40edd75fd0cd6ec8c3a1839e7260cd3cb8cc3f85e60959805fb2ee8251b5793da4a907a8bc5b110b8c0d9cb22efbb799b5c77cc909b02ccbd07307a6ba6c3964414e8308932390dd3afed2aec841616d7fb418d863b7785d9e6b9146fb6e0c21cf5b429dd34d34396647dd82aa736850c59a8e823a09a6f5d6caa4fb2e6146e61e93a6fdd85d9f5efaab1e3e6c097672bfe8b6a0c83d757b5bc2d71a2972793d0314d3ed3d58a73deb35347d79dbe1429bf41a4de1a0657afd31457f402e29992b8c5b7bccea49d4684347d54d7e5b240ecf83051d25643af82955b556bd1713ec9e200c4c5c2ac7b89e29a1a7b4f8f9bf4510438631897e5a9e2d2ecba60da2561a9ea67147924926f43f62712b595bbb4ff0cb343a70cda5ad621ee57b5b4f2989f22446ce4fd33d37805b9a02f1e67ab7c7d62f8a47d9dd4af65abf938214a4a0a7e03cef3a97ffe6effdec3d66d1596e7cfac1d9c574f49a0a4490e71b42fc3ac9b4a844aba585bc05d54dc72f23f84f02fe2b6e09179ab2e2af9702fa5ccd9be0cba8748c84543b7e2e064ea792bea0381fedb3b53cfd6ebd4885a2f5e02f3d8f671786129075f600cf386755fa0ab8817a7ac235d22883dab2905bc9d906105c5309f99f8870", + "priv": "8c13f3bc3c30694acc7b56fdb9a0f23831f059d56e6d547c7e2aca9f3f95061af9a0dbf8dac8ce4d103f40a918ed27d4f964df9e2c2df9b00581e88a636856de6f383b9c48ed863a6b6783e0ba3f21f4934ea94d4c43984f9f4e9c2191453490eb1938c2b7600f57c84e6195286cb2e88b153b194c98c53eb0e0ce47a6a08c95a400689b142463143181944c8a382821b4645cb89154440094a60920b66921950099a4250b4301130086d8c0890ab58910040a12926ccac64d1bb18100170c11128614c87158a83018456094b871ca8008148890d9c848d386305ca02d00862464904982c26521976c93244810352114b34812960420872009406880c6445b1665821026e43691a3b424229549dbb2808006860a1388500609ca002664c0844a202d5cc6289ab668c20044141444cc126a09b42c1ba171c4486d1a16726344728bc40888c69058164111842c64c64c920420d80222c1c409130860012405a2c24c43c40114366c5b802422477022b74cc9806922366d12b9200827088c223214871040466e22b631c4b869d1406c1a83286042485c102da3b62118b02da0c231da16884c900459348801c44980068062908852960d9b483213b48d1bc55041940da4002058146e1bc750518605d036252040415ba0440a8585241744d23031133204dc989154226513428610236a0b022e4038309102121b4930a0327108c5604a8430c38665933491d09009530449d214204cc40d82c4841327500b032e18480a61362909396184a0809b82401438012129441b072214352e5c280a03126dc3923054262ac92406148088c1280598c224c9042d54a4881845710a42010024640434062393659bb241e3264c58484c9b243262048ed0a2701a803184240c4046804b106454c04008c669811212d2c40912338020196a0ab84d21071250482a529888d4c890dc082c03036903b284818624c9802091200080468e6244465948401a13491a802053b02198c48c0a4141d9945102a1048c4240038621db460442145022a64803c93022a930043344d3b80c63941059a8448326241a89881c019144242dd900064048240299219414480437640a180891202188b664490820a49080c3286c50b89101a11002b33113952d01b30119323224c221e0860c1c836553a0418a3685d240720ba44cd9b681e0462194b24c04104ca01206540249e19001c1360400370a02258ecba64d8c088121296ce382011243314c822d2300850ca43094aaa0a97b4b77cb1e8b994c285e9ef8dcde6d5fbd2b111e9deeb3468ed229ff6260558c6b725a4acd5eb86d9371787393cee4800cbcf92c73a8f9d0398d92695faa0750792d1ba7c350fbd3a67b34a7dbea1ef4da34d93c72d7d6e36a6dc541b86ff4b864deb38d50156a71b402a2c6fd648e8f7766be43ca860b99d64321aa7055ef376d1fd778d6353beb51e771ece65631e2df278382d941d74f8491f406859de34333043ea8fae13a80f0423cf8e43f1921b70e0fb567611b77eca605017c644a42762d4452bab72dbce895c6b591ccac652b97622849e544f6f623a0b7b3c83dca67e03951ef90b789da2a9e2a792f9299300167a0d581f82a4a0acffb98f5d12f501cc1f067f54f60e373510b5c237b9710122932a57011e259d8e07035820e338b6a2c7fa2a9568a3a94d23d9e4c0df05906d54ef7d7f45b3bdc0c299191098b0a45ff066eb053951b8ba02f9c7938bb9e8f2237a0247ddd277f89809ddcab5770275a9cd53341e867d5b8a4936459374d29ea066c677ae06a468bf393db5dc498d0e02efbbac992092a956917ce2061537119ee6514e4ccb76c42b75b6d42d646f45b00b940621bfb86f9653a90d5dac6a6d8a2197d6549686f0fc6dbfa29965f8186de38ced9defadeffb292a1b10d8d9f6ee8994588c8ed0f12dea432c2a5fb246d9a2b847f3cf44a1930b593ec46e71c158cfa97a0e77784ad944531d2302a21ac0ddc1d0850d05f35316eaf64fea20bad6744674b9758ded8cd13c1d15107e34abe101354ec4243c2ef6c698269aade798efb0bf39d6f6a3e3cdd9cdeedd44b152a625213acce5b5dcb03336d5c407c31744f2aaed26824219076cf8d19ba2d6a6240ae54b113244f64e812396b75ee72289d6fa82e3221a148e128c70f941b959203760f5b27a39b931e2bd63ce6cf96f24bf1deddb99f5ad23b60ce6d79e6a5148e83b7fdcfb7a048b883aa2121433dcbf72d3875cf1fb3dff091862e6e0688b1f9b8254799e8e35a35fdd991780ba7813d104937701c90707110fe91282c306d87f0064d9db7907e2d686f6916100018806c35493cc93501d748c722b06c30f685cbdcb3803925a761a677713fec964029b33dfeac14a85fb9e1ef9ea64d1c92ef901c4e2d174a1dc158148da32fc5bdfe276af6c0a09aa00f2ed7fbb7842fdbf13103285631aa0b556ba0df298058d5a980e343e31c07d92dec13fd41f3966c3e632bad6bd5614ff5c8ebfbf006c3a136732f9a7f20a5a93cca3e2211cfa8636260f56705ae5c451003d557af584cbae0d540fe6b3f1e19d332fb31cd385f77fce7c506e1964f8038cf025a86ab76715d1ee3330038d6eeff8bf50f7969da650ca3df040ce95b99c3f67940579ebe3c05a894fc8069236bb12601afe8416c37a4dfed2a192bd8783d0a42e0028e972c3a3ecd41404a9c3491cf15bcec17f64d0ecfda8effce8cf59a35426840759162d1e2496877274c9831aa1f7064ee493c13860a8402b363730096e3cc388240170f164972beffba2def4920bbad39fc3238f319b703e073addc4d5451c02b2e39d01a4ecc56802613411491904f01691d74cc5cfe6c1c9b89d4540347b800947ace8d93b5e980c28b5848b41b4c80b3adca35e5f252cec267a021560e5ec0b163f4d8d46ff20c15efc49c9eecf9de63c6d630a39c4e94a09f714041b7aa4883b0a852fd887e9445d05d657d6967379ffab5bf59d9ad50fb147342d3c969e16f0716a38e33dd507e1d2f0444d25c02cf112616737098a12cb7e102b867258e713909c8a4d895087b2bd39e525ff4fdbc6d48fc03489c295f248556561eb140f35087d815b90f2d8e791ccb12546737447138c2c62b08e34b3d99579b0b38a82683fc7c5b5d169f3a5845c3704627c4e2a7596f601204857c8a74beed96b6da9f4f0153fcc96261744be58d11afe11a449ad371f9b9f8ad19b64b215d1ddfa3f18452b6374fe7ab20b2504c9360ba9d8417b56ea0ce11ec05fa4a8d05516e4e7472f5f9da3526f7af99cb41a74036cb7f46797633f05938af0d0c2bf649b3eaef4c634211a394254890ea153c541a128601c5f124fc070386d2566891382a97f704a358469283f1e013f598129a428be0e6eb33bb6a8ca3979a8637601577a35a7cd438487936fad05a739eb9d4fc27b5fa013aa9f225936967b7a775288ac893ac08e52a4ae3ba6ace0767171c6d9d3967597a9ab00c91cecc00350931a6f729cc03398f859fea9427ba40157afc02e645ba703ff76ec91f485e04de9f91fa708c8399bc0e37167ac9c9327aa78b8f97722ffac72cb08d211963987b69c0ec86d0bc4672e90f1825b7" + }, + { + "seed": "4457dcc0944a1d2b4bcaf855446c30ab2eaa96acdbc48d068d0da044e0472dde", + "pub": "6ab15fae6c60943de527e98204d967b41cb1ca3ad5d03ab091faaa11364997a4576dcd2d69516923a280541425f385fb89167844e497e7c07d6e143e05e0a755e22b7bdbc870f662f88ddab790b9d74fe1507240d8869802d197c075fa85d32ff4f73654f47632458284279ee75a91ff87b42cf5647ad4af328d0fc121448d2a4ae4fa676d0a924f36f641296c0639e4dab85ba34075f713308347cb18eb9fe4b1a5a576d1fdd771eaa91e3d969b1efd3c84342f2c4a3e1662737fcad14dda17400ccb76a7a4b6be842fd3cc49aae973f930230ea922c329646126cc9d68b82d8686f41a96f52fb062395d99d1200c4213fa76cb499161afcb5ba7df3aaab60b163d1ff71555c551e1353170e29f19a46bd788ede62e7bac4ba26743354dd7b1d9c4244acb1cb290309fc9fb534b8007061d193ed7713194b9f3e0bee9d816850162d7bada35fd85de50dd6aab001646e6b7af288ca93594a40b2b373fb4b6b1a118ce1b19ae812b92ee8e375e034ffadafb1ccf3ddffafef254c688fa3bfee4ba67332ed4a1250f122d9f5280c57d4e9184dd71aa7de7487c68a4234b62eb473cfc0e6cca2599887b2c9685202bcada8e469123a9ab14e4ea041a2b782080ea8cfc39d2ec91e6a771590b0efaced11fd61da887cff7d777eb35162de6b88af9d507db59fdb9bf1a5bcd5c884763f3df57f1cb43176346a3ffc63d222eb0f6c967372994e8227bfabcf469ff850b7d25d99acfdf39c07a1f990cb4e76040c07ec499d84d7d69559e7581f004666f56607ae5cbfb110d3d57df65fd027b3084ab977d1c51c39133aee2f4031695cf88b0cf09e26a72aebce572df80a7ddc943be1a377402ea94707b73e4929df064641f4dfb5da81b8b9d33245e1f28e67e5407e37178c3caa2fa20b7c14757aafd721e2563e7ab8f5a67a33026c6097fdbd9b788a60bb070fca9c9dc286c5a1d4aeddb1336e501a235b828dab92f680724e84beca6183f1d781b1805c9d17ac138a1f75fdbc99f2c300714b44a57210c4c871a214a01e9d44a22ac83460f1cf003e5e7274aafc6d6af605d58793c9ed328688c1c4b23f9b6d80e9efa1ad25dc5eb53bba15c8d766de5d0da7664df70ad8b41bd9b1e1633ffe120dd21705af3e7accaf32363ec3bd5acb993478e5a3c768af4e5fc7426b2c5cc30ea98915572af5abfc43de75378409976943b4b4d5b24a26a386695cc76746afc414d9201fa44de1771a792401e3fcb8e9b83d53be1dc4850bb7e18d06acbcd64c4346cdee387b6b3986adfe80a48800109d6a490237048ffaf8f8fefa692682b0f31409719c22a3c946f320df0dc546bbb9dc30b502a556dd59969eb695608468486c822536f9f87ed03f901800750a44c788433ca10633a92f324077b1dbdadf04e24c2f69b45a4cc3e9a9e8e344580e0bb80105045b59e2742e0c630e4cdae182284cef66a09beb7e6254281028bb0a733cedbfe3fe5ff6d5a367f00b24e3dcc54c6a8fe035b470a8c6ec9a32111235d35f0253896eb8d7ace47c0bef88e66120b5355ef168937274cf6e296e1be5d9917cb26a3d2777e49212f027e72e65af160241c78041330ffb0727152e8ab68a85a6118326519bc9bf00829509b1debd69512e67cb8615113d1ad5efca6db01c417ca0d12bb1ccf5e8c5c6ca7f5e1d8510c8867bd6e1c4b390138536442163663c29e6348a61e3ecb68dd0a4c6d8d306cffb180dabab2b1ea0448514ed22090544781dc3d5633996e489ff9496531b7bdf195266cea4b1f9f015a3d01e88e33f291c95371c23c9d046aeb6deb2a6b966acfd8c821c6cb2b935d57975703d9ae1b3f987515bdd6eeba", + "priv": "6ab15fae6c60943de527e98204d967b41cb1ca3ad5d03ab091faaa11364997a4beb8dfe3471e054264d03ed3ff003f26e31899a2c34f7ea324dc66e5dec8cdaa1ec38e586878883497e0f3a3f11fba337b1cc4fa50e8a01748d13a65796718c2d511363ccc2263dfd9938cca77a49eee3cff045088fb00cbb861d2d7cf4a18d602294143048814a4408b0850044202d3902d2107124c3472d2848c84b440d2a04508324183322292488463360ac80608caa8048b086448886992a43081482ea4146e1a9608c8c290990405890245a3082609257060084410810c24a71018172a00b2040426419ba0014c96911a3470999281e38821242345213071204049a4a840c1b48ca0486c1b086e51860493906410308d4c121249942d9c10099c264219958049c8491b26854a426e0400504b320a490622648600c1b820d9184881266118b2085b10089034715140705c823064440e881230421292ca366691a24960b42d9330124340882427114ba42864862c40c490dc1401e0180e84a41000960448223008a3319308105b14849cc68188340e2403484ba46013c588191041a2c66c92408942c44990208558288914c63059b8051bc830e4326c54b48063222e022044dc244d14278eda00711a414d62c49103b62d404030db2048dc022881c6000c4665d92800dab80d21390063a805c03221020350c2a48d4c324d2101658934211a08701a032a1c496d5b08902131400a25260199105c206821b5649a164a13398ec094511a828d134305843449d3986d01a4711a2400414610242286d9108422002a824690643225819484e12224d0c684dc320d00070800824919836de4120e8a044511a62804a884c1a4208320104280301c467121180864a04d80202209b531e30886e23261014166ccb46813a52412c24913049062140ec2388463308d52283058c4904b8690c204728890240bc3418430259b96248c442ad91209c32468c096480303251346429a92849cb6241c436122a901a2b601093392431208a0b66cc2808122946c82b66411012e123584d2248cd3002e5042649c36481415495a20049028000010850917684026841bc70852328619317213178002b42c623840628604e28630a3006e01982444166ad1128e8b0269dca080c40220c3386160869019807118162403228aa1422ec188658a288580a66c00082dda126192080019a95112184e13176e844671a134251888451011469106301816804a0681a0800540833b904553355fe354f64266798d98ab950628f8c77712f6ab7b94eaf24d97e26e52f9df50a2fa17e455ef6026ba84be5fc06b8043994ba75183ca73f852b132f3ee2e70f31bd4458bb338a30a20a1783ef5947bc42ee9a00b90d1f0428fb9e7d1240b43133b6923d1f564c0f8e0c15f5950bdb2f6c4b7eb57a8ef1b3bc88757e28c5519932ee8b7130378ad9986f692179bd7b7cd68892d3af3c3fb1aec1b4c6c7fcc39e8dd77d43bf9cc6c0de875018370a593aa101818435b6b8d9affca98d29c6e31698366ffa2d87c32fd0e2453bb582caa16c053147217e0b9f3429146fbf24df8280567cbaf793fec7298c61fd97c3ab2b9dfb561640bbf84f02b65cd02a5d947a52f243ce8d245634bab43047a2e9679393f7b0b7e9b1a1bffea5bfd8b17e8406c7a01bc6599857a0d3a3741de638da9f4e140276f290b9b916a0bb29e22960495b1ede8471ed32d7c377c03845d239f576833ecea76257c654e1e997683752e59d95201991bcf428d9243842426c057041731c768e0694ac9a6c1ffbf4c717ff51686b03dbf11f66ec20a9bd5beeb1f22fc7a403fd98c9392465225a4df8cda2a755eeaf42071307535a225020f2de8b29c85d55345a1a80acdd840609eafa62ad83b98986b824aa8599350f1d34f1661bd58d17a6bdc8cd80439e6c152ff87e6e3bce01bf6bd160c7cd05bdfa505d5ac305980c9156bd88b991673e13accc520959aa8a294b9a0e2c9b85c21233fdb413f25c99a7ebc007eeb4a893743710375cf175d91ecdf917fb5c58f1ba576a3d9b889e4c33e548d46083c321a7585616dd441eeef3481c917dd0ea0686127e97d8754735b1dcc51f81b600cc8427be5c33489c852149826ad79aae2dc4b856053f22c60e41b694d8481c09c3fdf257dc79b1e959af42306025bf5f8acf63f8c0e5eca7eb321f8f03cb865c1e53b8fc315281d975589b1f3f2ead84a564a9c677717ffab2600320cea6c8aa73a7ee63d16f2e7efb07c8c7031a321592a930faf1d33faad3985b5e0a5aae3502321523dfabf33c374f06e1fe736070d292c6b2828d0871547854d4424bbbf3ff360a958f473b88f2fbf78c65c9e89905db974c1e1dbdfa7f75e6f9a9ec9317a0e17c4ef5612d45d7d0816f1177e85b1c5b39bbf3f98112d7f07e0eb95dc804b981c408c72cde1962596184bc22d70cbb748781f498a0ffc85014abe2d6b9c013e6e60434c6d53e4fb0f4534854711dd1aadcd7abe9ee341cee17da1ea16935953c63f603289593835242cfc17f471a339dff0a2cfef7746b392d5e9f0c666009a9f9114fc479e2a991b4a2376caba8a7ad362bc73baadade3548625516c469677b73a99ed14bc3d4835f875d6f82bb9d9d1e796a9c7b4597daecfb63eafb990f6a5dcc79b1a1645473bf2d8fe8f1abae8d35d506dd388b6177ea2facb41611d24ae70da8c4c9521a335a77678000b64d2e1de8372249314ef9c7eb454f4d0ca9f75cbf280df81e37e22a6d8785f44bbe9e78e0851b0d2836c03f138e173948c6d0f79f531ef353420fbc7b905cd5f64c4fde1f0a255f62a5c2ef1ca88cc24b3d45b868c3218e34400def9d57df8c4f0c78011ec924405835d35b15f64ea7cb634732e721cc5c432412bc0399d5009b70b8f38cd1d71d085c5b3877f44ea4b2100eedc629cb2bb39567663fc355ff93ad538da612a70072ff506bb02d4ef8366581d92e5b2d1a09e9b6eb89f3a4e56648992c028a8161bdae57f8c15540b68e4d65f7cf6d10bcd9751a9b27274b4c107baa00d72ae6090b6a66669322e5c99bafb91b1eace7b29dd6c2dfd161d12642c5ed1f912c08290bf39439f48edf2c2049ced6521b1f23c87e18f418f959ba245613af90facf179dbfc5b92eaf4f8bb4398b9f8b40815c5389cdec74c704aa1ce32cdd322b60edb92a420acaea1808074a4e33f5f06c390cd1fe2b9fbd2a4e42d3fb57fd76fbe032b19540b00120446eea4b3562f1535815238b397eb42d0ddd8c1155088c38b0c214fb859b9be53b8c1dc4188ed2458d7219972b369eff0045b4995f9c39b4d6adc92d4cb24d076c4c62beba68c807e226bdd07bd9a07e92a87e22da4caa25cda1f1267da53bd76fba31f2e57667a17b54d19ed7e6222b103265539462868fbb5abb952fdc8190817c1b8dbd7f13ed713df8241c2b448a176fc7e9e693960801fe8b001b9a15fefc50f171dd5ea7c09987ec89f1650c597b10c79fb394a51bc89dcec66764ca218085352c2f6d52eb9abba42143544c1afc632108f5e617158a6239f3ed5c86eefb00b13e6734f056d781705cf6194f35980819cd8edbcef44c3865ea090bb260a4c" + }, + { + "seed": "51c6293686c82ee4ca58112b1e1cafb062fbc5ee4490cd095dd60e03726a8599", + "pub": "d1c7959381c9362551792dfaf9cf15a56fbb60f6f695dd0b24a1c53bf9cf146f92fc19ae36889e62633ea219b530a43c568db7bfc4f27a77e25ff85de6c9e69256bc0ef6a3305ee1ea3bff8140306af91ca22e856d3a1fb73e52f97e78ffd83773b39bf83852f78d21b68ef91ce966d2b6f12e80cb88dc572ed0575a3c7a672ca48cc916349aee9a6a77233772a7bc466a6da7e227342ea84eb3b027caa0c1f73ecf0a91dc1b0c3cca9af355f84a1a97e0b491d7d608cc149cd7c2d91294397feb936a5156219cb9ecd97ed52d3d751ef430d5fceeab57cca2f0e3d13f55cf49a04d1abf38cd81a2cf67aef0915ff275c9ac711ba42968681b8a3256c9e1cc00b567f8951b0b4122abf6d8531aa40ec6353e25202022137eec412b8e957be9af93f8fe5889ca8c1ea043d1bd7ea9678f35ff362115a4cc43ca75a85629f698152e6c854ffb823156aecffcb4c27c992f2d4b52d865747572ea33153207b0bfa698babfbc21c1a76578ffd5fe8d53fbee3fa660c0748643898677a5e8408c1cb455e0366fe3e05db9eb88f5e09b8e6bd95c903a8560075bcff5e9f8dd470c2cf3cb10cfe98e7b2672e6d5ed699725598a597385a50f6f98bf91d9306962aaa3a2f2f1277977c5c056a7b4627e0ca20f97751dd4d4159f374d5fb6671f51bb5f871779ef88fdc339aef77099c5141255d7fcb0b4856fd0ab4f267d53c7f8ea31d6cc35abf1fceb2c3208d9a00581879e102fff9a5c103cd81caa8435c6da55c33684e202346545fcb161c144ac6eee40122c562603f28c8b931d871a0cc2691b36546310b01c33df8d7385862b58282ffbe617349a5a6279295c4a04b3a495788b0672706301f4b136fdd25b0970390229b2f8ab92e2e880347ba37f863afb02433c3995d6d050fc0fa8f4622bddde6cad3cd0eacd86da594d977bf68633cd89f5cc8315fbb1b7e89ffb5fc8b37c26b8d9e9b1c7ffad57fc87520c6308a17c316ce24162bbd4577e97187694d677145261ea62391205798f54b300916c685cc6c8121476da60501c6e04ddf236c50f52789e93b40368d0035eb1b194e03c5779775ea456dd15c103725d84e6cddaca863d93b7cd1dde733a09c7652f37f98c9f4dc30a811ee4565fbf3f7b7db9a291dcdba6d19c431e57018b9f5ef4b6b596145161b3e8f993876836b8a37d3e806fd3d3ed7e3cae95892dbc236823ae953cf3005d46974022aa382c357db4bf25be73de128e96f487ec4ec29648c803c98772756321623c94b50ce31da6e630078229400e67378a2697895e6628e6ae6e8b9dd9b04619bc14e3da63bab4a83ff4a8d1851af8f3ba132ac2e218477c29dc9b4922a2c7734271477450acace8861ad7828fa29bad43c735525c82ad67dcb480e6e42192fa5d6ab315dd4b8e2891b4fae7f087013cd6136654298c4c3f194224817045b1f7c0679071badcf13e1cccc58fcea6e819f9b6d06dc2855573b52c8dc81b8b5c03d8ea7658fdd1b913a70aef71aebd4cb416badbec6cbe989e5cb36abe6b1e373cfc7214781259e90db28e4d3659f73edaefbfbc4a440235c8d64ff40d389b2a808fb9005635124a857aba2b734396b11819a5eae996314f5715a482758161b661196508fca63f73e5dee97e2b86c07a5315d3924a040679d750ca376a3b0f2b9cfd14aa04d92266915a55e3bd68b5a70d6b2adc8a99060e7b5c741b5fa9bb50d5c50b49886502b4a195f1ff7f00a64bb8948aeba3303eb0b24c8888988653ea1012f9333dcb6544a75cb5a26fa2a089a1b6a4eff72cd17fc35aa0277572284fa6477716a0f024b43939d56fd207f1cd51c27af2e6bfed36f1686b0777ab", + "priv": "d1c7959381c9362551792dfaf9cf15a56fbb60f6f695dd0b24a1c53bf9cf146f0bc522bfb5f5f0aacf232c9b66d399671cad5eca9cc532062be58e9e1703eb365d36d806f98933d7b99bb7fb509670675ba9af34e99de4c2604bc2537c51fec97dd4afdc3a100bb507741a520ebe75d928641c4b6d067e03bdbf704597c874daa2320a88902413498212111280a6510949510c8368da3801243066e234250b144dc0980c58945120a4654a142c5a3880d04648190965114870a4a210a4b800028644234290da164e99042114114623274901c04448966c1404124b80008834429946508218284ac02da13085c436690118920c46511ba200234321ca946823017200180e48b86113436d0b122821199008b8612421680c310a8012860b8708a12205c3860dd80490a0941118a24493b64518a62dd092644c080e89c260d9145089103082366c94a06d99289109066250480e1ca580042149a2486a18991159002a08808d4a242d8a849003006cda48450ca285181664833252a3b86159362a13442d503262d02025d2804dc49244c2440289b230218380cbc045c2168522340224a049a0a0099a3869d0c6308bc46800c26801862400c04964a00dd198501c0164c8304cd402321b348499064da14608a0068e0c464449a62422150618404da49210993286da844d1427680ba029c4b28102278558920920b98d84a064098511839890a1b09122190c183251e4164e99c284523081dbc64540082191867112092a9138261c088a0b028411900100a74d63204cd032281218064bc2048bc2609212880202801cb54c09a609e1324904880589b66c14a16510b00c189665d0c8000b962163048024898822042d640069a490401145860b022593266064040144c6459cb66c80b429940020cb44658c302023c2410b342649166d121469db36649a02080a10701a8831d3808c934092e3346dd840518a442da2b285901046db84304c002e6216324a104ae408718a285293108501268453287024a1040103869a144ca0084e5908825c060c10c51110904521290859926484460560046c58c66d184440019108991204c8442513a061a2300c21206a4496515cc008d4982d021522d23440202144202006e1328c0b40861145884c4486031688d11409a4042dc382690c060202c51104222c1b2981a2b2854a908401008d0a010999086d083428800208920250c9227020216263c80191340888a4201a280658202863020ecc0625850966c7a593486d5b7062c40afe8ce872d34f0d303b072bfc4f8d678558e46a9cc95fa409063b26c46e7af5c4d86a2e2d972198c87289d2d491112bb32b5efbb135533bec89d60a519ba33994dff9789b3b0110b22a6008200cac2e3a4a2b64a23fa3a48cb4c03cc6d3c0493381ea542dfcead1256c2da541dadc9507adc6937c034ead8cbf63b2359c50d78361db1bcda99305725155def5a373310e9b88c847424dc1a58dca464f0c4a83431a90852309b7f83e46dd6e487fa1941f06bb275c9ee3c7da12e39d22b3660197179c722a7adf04b2bd926e1fa70cf9f4a5f7bd32073a9c5e446269a58da3fe5810f5bd1b9f6bc0c94e21087468ebd04486caa92c5a21a2db2180be2e6f01113a9e06e834c924b214c5b90006ae33a84ffeb991a81cb65bff1dd27cd323cb16829c671412b4c5d3dd5ff3d769ed9ed4a36f7996e06f402b1f4ea6716a22f567ec78da7ca7191a45b8ae195a4a3a4af2add658000a282a69933db8f3b488264ced3d018aac7d54f0d7641393c4dc237c30836cc983950644ae2f6440ed0a411b946d59ffe713deb44c85b6c3d4f7f2e64d94a39a39bb38af327ac270d7f19beaad0edc2ef80aac5c24b4aa5161fa462b4dd9eb13bd3d58c8a402d36dac19fbaa887c72a386d529a0868c52654fb2c2f9cc95d87ce792559390e1a4043b935185d2066e7c2aaae92e0e72019eed7cd2c3cd71e78addb4ab51a702d0894cb65b268e64670a7f93dc88d5e5753f4461bd73dc4c44b0695d6e5b8dcc1d6697f51367f02fae9379b85b9dec7c75ba83811158164367973de07457edf35da8dcfee8943e09c5225038312ae3513b55b582c7f331625a8ac70664d349a8b4749b4471e95e614d9873788b6d41603fdcd2613d9cb62807909213875e5649d494394823a3394ad37fe7aec3e984c2cced229304df24ed93e3e090c14e15efaa90f60b70a94fb4d7a6f0f43a163aea1718d10d9bd98dc3e658e17e7ee2fbd94a556388b50cf9defd8c47e13ca35befae145bf37c35c18d93e62c88e2833be02e52a7880dfe83a09f45fe6bdb6a735b87243cd45f4b02149866b8a566f3798c0cffc08f90281c4e222f6179b95c860a5483258ce1aaa09753cf04c7d8f5da99ce4648944eea39e0bd992858b3b0c048aec907289f5a435946446e8636911ee75e4d7e610a8cca05f0603329f194a437762eaad6697597a80c710ef449a4a647d628cd78dddcfddef6d6842db9e3ce74818b3438c4a1e9623d14b27e5999e1aeae33267501f2b5c941a2265a1dfa7e36d248521a7d1c99a86b2d9caeceb47a9e85302286257d1123b0a7f0f3e3ea9107b9cc4848fc155e03c3aa42809bd84a55ac394c6d076c953f42bedcf1b08dd668800d25405a81d04bf55fba609c655c82677635647edfdfbbdc427208446f29059f5e8a438c9653dd696b827604ab619a4574e7000fab119c468fa9770de8b294d1db16f50bd6b846fc55ef79fd764ceccc666becf71912ef187341682f5cd569a667973aa42489ab67bffc29af1b27733d1c4debef83001953eac16d08dce068099ac7b7a8805f9f69b7e4ca9cdf4e73630d40ce546646c2869b524bd90e30f002120f896bb6dfd3be89e22935b22e9fd70b517cda6c0ab281f265d2d33e38695fb4c386336f63f6f8f041a7b756a8bc758e3e959e7f52ad0996570eb7b152b07f37fc04ec573fd11b47ef34d55608766564aef2d5e6ef48aaac1f3ca92d2c50725a3f96cc4aa624a4469640f7f152713ddbf87eb068c10b4f56f4fff8fe58ff2b71ffaeed38886f6b03d538645ef331c2ecaf844270175bfe11db13ea8da833fed30f996b466ca53c018571db62423ffbed94329f8ddccff2e1bfe8a34d213b8569d63479fb0d78bc47d9783b4d20fbe67cadcda4397d0391d0c55db3d76f0ab5f5be01ea7e6a6d51269c90653dd3177bad7d5cca9b901fda1c39c50c8506b3fdcc83519383e9a1c31a6cfb31aac5c013d1b77f639130c04960a040d40a2b4fc817287f596823a46a9d12e666976c82e954df452d388fe860eefec92161a566de935ffeaccc02ff90b6ca68cbe58b3bef65b63e94fb157ea5fdd977045f1140d8f2d5651b64c684dc67c265523306c57e205565d3361bda5461228e201a70a050691cb0bba2161de9220b9c2ce565b08e0ff9ee3103e422c42ee4c19ea6f22e4ff8f8c05411b16c6334532fb24a22e5add3183ae8bfe802e353335854301a6c076e93bafbe75ada5a1e593e3485db3145f61e2ba8b000d4d381fbca0ffef8e0beff1eddd1f38529364b0762ccd9c851f6048af2d9af895d76a95067a4ede445acd1e6cfcf600bcfd14a" + }, + { + "seed": "96dfc5764fa6c029374e7fb68a82905b539fe8afb0b2af70b22cd824b2728288", + "pub": "521c611025e7710fc3dd4a129814bf3899b7ae91b5d7c99753463fed5c04a242df76c4fe4c20cb2f24ffe5e58e5617ef54b1fe8b7ab4d2783fb485210636d8aaa2217a4731d9f3d31a9c89976f39d2bae6a439a42abaec930268931c2afa3d3f3032387a0ec8ca28bd3de1ef91557f42170ab50e4514e329b09386debd358b1786e66b4449bb57d9c2190014050fba32b34ef5a8b95eea3c01e7c905c11ca5ef7de2052711dec8af1740f091f7a2f69e92800430ccd48c6b9341d8575f8f8b7031eaf07c1af5225715e7086afb0a57bd3b4622d87411f39d6a4c508808494d18481e7ae15713ec90766f44d21abaa3a468f96470a81b54afa9756d61d116a3412c52d3c8214e3d382be943f642537788b1e7b1a017b8d4b684cefe14777d5f136190de46e59938b412b226b3409b583c9eaaa38637269fc094f17f95907092529a1925f68c18bce125d71583203e46da1c5d342a92dc24390f28d86745fd511f518c0a48cdc05c249f38d4029f08fbde4ed2b2c02b5fe01a17eba2bed94b333665334fde45f10e9756b2964b9afc4fb97131337a6b7ca56b4791d7d616c3a6dcca62b83e0b91f8dc30896730aed01582544a0be4a185e5a768473694e500192a49ff6c32230ba5765751aa106e6105a4e03ce2a92e3d2b8927437cbe0585e859ed938ce3a9ca581702d8b5fef840bdc240e0f9d063ec22626e47ded2ba588a5c579e05e51807aad23046e9298a7be18a71c37582a931165a6394d881b6700b7f39d9c12f9bbb0fa7f1c1ac46d630ee202e161d9c6681d2bf68ea2e722aa71592558d3616b3dbf67462348e03bb8329a34ab47ab0e2d2c7d3f98f616920867fea15507f3889dbbe909200220b101ed6473bf6baf68dce4cd2f91c8705166668509226dac6630d8bd2b41c7f133097ad72a15f1498e79df7f228668682e9d5f64a12fe17a9b0f0b5b24c1960ae49d96ec1a86db26501bd75a6cb14455e62c5d9c361edb80a031480ac028b8ba1fe03648989216e068c3e29ae9d2b7db90b138a85b7b32100ca48a980f6455f8433fc7609e10e06d915d9d307719f115e0c65346aef62a9425a17fd3bbaaaedd646435536dcd55aa295de0ef57dae9b180a94722ebd2635ba66cff06f5b57e255535f687ea0cb3d5a87518287fced61d801922e2caaa76156028131703875fe80f002b2c71a76077fea41f64225c7cc65b6680b07e16257ff86c3cdf2194339bd8f92c84b9034f86793da857f7efa9197ba06264f28fac145af4baaf49c6acaecb7f1068fd9db8174727d950863352010565570512ff48461cdd6bf13a30d5dcaee0af2f5785dbcd2f1c9e741097ac614fdab47da2e67545d5a33f6bbcf6a2c87f3a48130e77a53b8748b80b3f1c1fc335f1072676310ab441cd212e752b59c48bc5f04a3cd212b6fdd0405bc48a6724b31978c829ec244908cd0fb2ff9ff12419518c276be15e9bbaac0f7ea4240998429a75c3dc9a791c38542d96a50aeb31e94bfce70d8614e3dc22bff7584472c1d57b772f26dfaf7da6aa1aa74f89643c12de08cee2aacf6b14005ba0bfb67860e78c53c6ca38d225f4846928c90ad2c2b65799eaabdd23ca19b3e30954a0dd52d7aa1810de54da118d9bae90017d85564067d15a22efe486229f38c7a0599922f8d63b0c95b7d07c8f63ad91e861dfaf0713fb2bb10aca713ae6507da47c6eb8b2242dba61cdc333070fadde288cec15363bf6db38441c50b72f04c706165f52be589054f23bee1b2d9d626dcd8689be376e0b68bf030f30e740be62d8fadfa9d1b083596bd725025e3cbb566f08e6b8bbdc28a97818d3a13d341297845ea7d0cf27bd2fb", + "priv": "521c611025e7710fc3dd4a129814bf3899b7ae91b5d7c99753463fed5c04a24246f2229bf83b6d7b8b7abf422f5f867e09ecb6f78192bd4c4be7a64cdff4861c226841bae6d97802031bbd36d091ff746cf03bc2512b813a5a233791c7fb2bda3b967a5589a1e3371001be0d694d4f11928326b5793da4249e7c71b6f92ef12d4b0686c1862504974809256413078e4a82851c3642d0822d49a44c6408468194102296450b0466082289c106005a440108284583284d82a824e120889422406306452124310b110d19b088c4962462126c213412a410719c3012181128d1b208e3b42804096402478d6240045c347081088c8c162d840065922211e41860dc164694b870da340802c584e4b800840084d1840d919670dcb86554c289dc2209221062e4489102220d21162a44060c1932328ca4444c184254386819448258b8495a304249146ac4244ae0b280c8b88122156eda086111012200b97104a130499870a2282010237213988401264581b60454b805a1466c5c801044324d202731e4a42ce1324922046c90a06da20029a3c02803346ca404300498841cb3451cb5080103644b228e92160d101789a2466ccc0864001191e4864111256408854889088c52c841a29264c9403063c608d490059102658c246503432dd9c22803a844a4064618a281022548642882dc064e63240d08c68c61980941266e1a222e0927449b322c81a0604c964c1bb20ca092250b340540c20dc14872e10424a30440da10624bc08dd1c645d8944812076a6212090013680b866412a63109c63014476810c51148b0105c9825c4869062462a4ca62524c941594684a13691c89284d0a40c54c4689020251a472cc918824aa8914416704aa63188c22d0c4031602444a312608416250ca764dbb40923921163348454388c83864d5bc604081064180461091912e09065012380408200d924310903525b326eca862520258e09220c91460c01b32921487204b92993c610104091d8867118842da290904022845a146dd414124cb641991220c3120902900060b8418bc484a24411da128ca2b4051193855aa411c11802e3884880c849018270cc148d24b824c900016132814c881119332c01358e09b12d12012d91285209a70c4908894c362d5814028a82401a20689a24914422229aa64da4a62d4948860ab001e4065210230cda02702245419b000adca2080a04058a140aa0b2700c441199b2684ba42c984685cc94008b988593164915e62de3080492f8955f1f4b42d6067199b111ed13e92d45ed748c83d892abbb3d4cf047a00d8b29de503ed6f050af8c2954a3033b27cc2ca58ea7704fcaf6ea0fe0eeba018473353fea855e27d111ef46221f05703118d281f9049b3b701bd85bd6f942ef4c5793f89476045c229454a4fd14eb444fd360342315e3f5fcbeea55b5e16d0eb8d753e3bcf6b4b0f99e09ed6382f9dfa6a58476fa359a41fd1ab6a397adc0cf99192546ca51ea105964e6b9080b94861964aa6d8151050fc413edd025af12e5b977b968e21fdd2f2ef79f2d7af1157e1d015d51250ca6f3291a80aaa2d063e29a4e989b3eb4ae7354e3bad6ed04140604e9c85dff4144e7717e23c871f907c38bc012207c6bce30832798783fb076c90a7dfb05d2920a88ad2c4558a2badad8ec21c870a7437b2caf26608b48080ac5f20d1829c0e5486907965a2c433a788fa4d774dde972f3694d6e465d020155b92781034e511176d3b5e7b971b11dfb4c1bb5717fe723f9f555031e52b21f748fd141fcde6aa394ca5abf6677040de951e35bfb729e07034eece499cc41126343579287cb2fb590da627e2f1f6ac180962fa917f61f79477a8fa83768fbb2b6d68e12ba513174de189eb88975fb0be4098a42bf9f542220da49c875c50b9a13f5d26613154b85103d29e397f3cbf61820693b89c46feb57171d83ebebcbb8ac400422ff77c3b2a4b58908a374a2c44026ce6fe277efafb3f3052d6476c5093dc4c7670d7d835ce09a173af0ed3701c4b2f7a6136898bb9ab6987a0e05a8e46250693082f6acdf54759dc99c856d39aabb0a2196170419c858380e97c50d52f69425edd670944a7b1e5fc186b8c102cb768af20c95dc166a665d10839478ceefcc0d6d008cf49a9c6892482b6691b9420ebfd5de8cc38104940aead3380e23b330d86265047290921a36a7e73b78b13cdf5539b8b49f6a3f06f77af9be1aa96d76542580ef8ede5f44371247caba95d8c2c1bc3588b08b93461d1c627b7723e402e8b9affc616e1610b66a1641782ef95a4e0b969dad6a94d7a7d2df9a1c971c4f6b9308065d5c8cf56857574e4858fa8c0269bf26647ee404172aca0aa8595a5f845a5b96342bf86fa7f564641d2286082cd40ccfc46eefe3c311214639f61dc941d0940d78200b24c6580ceb499f1725d1d6fdbd66d78cfbc8ca8bb051c953d3d053b25ae6e4faf7ffb3f2f6d0f5a7fb72e3adfce347d43c673bcabff21c8a28854575462946a31ecb7f96397e3de8c189f85a19341f0eba6c98d4984f645e5a5acf125c3f8e5ffbff9826364b3afab5f6dd0ace76ca78f4697c91b88dd663f9b23a9074d346a07af0dfee15dc898bd9c79fc41924ca68d3714dcfc825a2598f8be97fb7d9ddf96b8ca10628cf8c7c3b27b01af676eee6f6afa3747f91bcbb3f56ffaa1206722370f464e99c00003012dc53583e3ad661d309a7a4ab11b2e994caf11412e10a7da775bd28bd0c94f14f2de7881cd5f56f1f62a9e0bff702d5eeb9a0dd63325a3817477846cadc06cd01451be5ff50caaf1d185a4e12d80e63d1a0a9abf80b3626b59eb3b89748acc942430b5a79e8b5defa6f4ba131d037711889c7957ff3e60d94a2633c3a357a736eba4103e78eae3b2139bf3ea35d8ff2c71e030121d57bc892df4113b32408d0f71cac300f12bde481174e5403f0487c3d2fcf5f2d74fcd1e9f938269a11cd5dc97f5d519e3e65d6cffb92500fa9a58367f6361a7899d04b878b29b248477eaa839b6d689017777b8ea89004f0b422ccbdde25da7be1f9dd9026e3872005811398aa0f7260c64fd30b9335d19eb58c85358772b49bd491821779efd768cf844f9b374519ef6ea9f9c4ec56b1cc7b696a9e7ffcc1385ed3fcff85c7a668f8598ef78af3caf04d889799a4e8816d5a3a6fae659d6f8e71b0a4e55a93d9eb4e6033d7568a00f78769dff3082eabc643917f1388d8001f615f2451fa52dc57f39d357787ad0963e81df77497693ec8959afc8a18dafb02c0c0fbdb8ccc8520cdb28d45b57bbd0e969b730219db5ea3838f4660eeb08a951d7bbd7520b26f2d3cf5ed050fe75eeb318efef4ec42b489363fe2ef68bb0596b551c4e2eed841a1a32b4e0e681ac92d5394d92e32992138557544308d427cffb81737d03db10b44c81c73b091d1ec827e14d156e7a7f75ad1c505025ffcbbe63ba6049ab190d7555754ef478246ed64c13b8348accca30c988ef0919a5a5213695ed0f9315074e5ac0e13fdfe2ef37d94039ae0563d1ee6531c44dc4d7a34584439ec976556f8ed9597e1c308fe3eebe8fc00b901c70884edb2d55dc75b86114a4bd4b92ed0af" + }, + { + "seed": "0c5ed6fcd0195c183df1c0c79b0df4a437a12e596b8fe77d5f22981a94f91309", + "pub": "86d3642ade7c1e83f33eb83eda74664396cf666a969982352f091e2d16116dbb44a33d32badd201d6191ac57da8ab5483f8ee5cec7018b5dce6ed3f1050eae22195294eec5f803c8c77da0affc6bb9ffb9e80bc613b2db7d095ad037a30f6eda572f44c5d8083de990373828cf5f130a515024e2c38960d03a109015c6a65153058ca3b88cfadfe446d72c5be5fcd393fbe7a51ad2f94ffb13b0031f960f8c0ebff66048e08db1cd25d7fd4cb3babcd9f52040c1d87a8a54b254e53ef0042a352e4d97b879bb60f07a99e50734655e5a208e2b5b17427b0610449fc1929242abeb54bce504f492aaf3fa857a3a8a0af4182bdbec330107704064b3eecd20e3ac5acc0d0cb687d18705d48d9fba7027e80675848c30764b3f0a0342eea8f17b091294f1357b3c5b60ac8626422e87927b4f50998924aca879c11473e614ba9f8aee8443b512a96453b59294b18d9f4f0020bd6e7e232219f50171a07e05624b87cc7074aae9f8aa55fbac882c18eda8c5649523aa08d2e6558a33132ca99a71b285940c01b15819ea72d4a67942d4d9e251d4c4d93e742ed3a674dafe95c5e06ef493a46c8728d5b6e32d52135053b769dbf7eb9593aa1e8fc25cc1efc67daa11bf2124fae65f986894a1e583077a30dfef9f562a9e56bd32c908722e7c838ad6217f3e7dc5bfb7a20984b98349394a42d0bed5d4a84c62eef53dd782a78d16871a79456d89fbcb885eae4975306ce2d0e10891989001cb0f06051f9601ad2ffe4b3d9db4c5c7d503ca1a42215b2c2d87b85000e78b6a8b03291dcb48e237960a9383dfaa6d1f2cc6cbf0370e13f7af53d5bf5aa069f433c6ef3b42e5c953083bea10248932104f08e30ab3895fae1167fd294401855786206f6e479fe1ed841e6f57ba04d5d56a1af1d20dbe777bc7312dd2aad0a9fa4cd55122e97b98e8035e0747b143f6b8a388ddb6d42b84ef1c5c0ed860cfddec0a5ec3a44cb8d649225afb4a1eb1b0fc9ceb13a202a8425ae238077c07f98f4ddf09252c8e9742781697b1204c9c2040ec005c0118251df7b6b9c172103f8fb5caf1f40f809cc16bbc91ba257aed35374c7f34c4847313a0e81d89afe45f60a3c2c128580f47908f69986f13fed38ec91b4fceab6f0e5f3811177cd7e752851c532a6c9ad94c90ccf9f086beb3a3ef841b3766cf7e34dc584d07fe54a57d6de9dd8c76d0975790acd7bb8912c5cafb020b59f3f1892300fe30f141fe03226609a1c267bd22091f94e1bc3c41d1e8567ab7972a372f6ada696cd305b5ac0e71f4aa74cef1ef3ec5b074f7ca2b9a94b8af0c7f0227014d419017a245ac305209a4eaebb4db98803c3e208a3d40e34e12d3875842b2e6eefb64067d4cf9b59464c295153b8813029a269c9223984e7cebf8b6d243ec525a3ad1583d0508234ab72d737e7bb881f380cf76b55149966b6d600d03b09ef5bae793c65af6ca1cdcebf539f7e1db466ffe7b2de4d0e79f924155d2d5fa50dd3dfdba7e289efaaaf221a170c3b43547bc40d05a02ff77742c7f7d157db6cf8054d4ef1fa4c565eeebf03c0e79791e680e7411389e0c235b7d4214b450bb00b41409bdd35b3850ebc317786e3aec137e5aff9e522ab46231a5db108921248a4e56159d1e64a722d439b10563933dbee5d50c218702967f5b8eba27f8f63e4e97f2b0d3c30479f128092412701348dd19d44973bd58d5fcb23844e871ac21c1dfa19d30153b5f20d0224dd8f529dae7832d41db18e2138f8967c9fbe4f3f37cacbdc79b727f2cad15b38db2a03a96ba18314f0e2148aa7ca7d2524d546274cd64ae02add152265af3484c0236e381367da7b56d5147", + "priv": "86d3642ade7c1e83f33eb83eda74664396cf666a969982352f091e2d16116dbbd0e1eb7c7469ef747610eebe57b0d5629564746531db34ed75fdf1ee4fc4ec3345b51a73ffed20a80dede9fee6aeb601dc87aff835a3e4a0a50d604c5cce3783599d32e28839ed1cfbf959850933abfbec7d87710fb29c3685370a0e9db8e2741ba25198960954b06c99466cc2b02c8130200242112321060914621c9071118271219530d08644881401e4b631d0162c24300d633041cc4664da1290d0248859362e8912101b39211bc04c9ab46400c781db2221ca064a19b4618cc86449402a43a86440009209012292c40c138501184160c2382cd2a4711a3446cb000a81c84820c845da16115ca08d201310a036326036069bc06d19014cd332280c274dc8c40004c06843b849a2346002b74993306a98488024c84c144532e206012303684a944d8a166519398594006e493232e336251bb924d9340063104a9134220b1684214331044561830606a1b46440224dc0900519249219b30542160e63440ed0b8080230428ac8885802410b240a18888d9b822d51047024c83122189061086ce4488802c36013192619416913c82819a00018a70d08201151386194108c1cb165224931044444cba60994222a4bb484a142401aa501cba83124067119a7495c048508256653c261583850e3b06462302e198624e0964922938404060919c040630000a39284c0b0705ac2444a8060982024dca07144262a09440ae3b6400ca2211a492aa48865d4424c943246c9a2290c18129a442cc2c81012c481a3240c1a166a23904c24348ec4146d908629891469d1920508274a5bc42c9b047008b78063326a4412016044811994311ba88503452a24494851386c1c200c0a364dd8b0702089896388644428129b82300041068c282688044843b67023178ca408481c802cca206d9484841028240c414e0208242238321b184ce2c28992a44918170e60a86092c04c239751dbb4215b18850c3400810604d4804020074202922918995113908d52b22dc188449840842049221a959108a984caa231dba071618071009825d20088c402059136725208411208285c102ad0802da22022cb068ec016310c44252319725c202dcc40698a164103462e20262e8026821395051a2672e2386e132961d1c46490126458202261b68c24a491594229529200c8308250968401916911330963280da1a608531832d2c00d004200091320244266620246d9482ec9d3007ecae3a74154f6a5a09e4b70174d911539ce165e48d92bcf550a0ff49f770b6deed1b6c0e6e1f2c6561d5c99f26875b6391b7d23222412e3ab38e7408b6f244692ef9123dc672c1eeea18a608ef7d6fba47a9ba7311e6cc2393efa3c3aa2b767641303ecba436ed9cc3479375208444809793080e087ce0cf37a09241f74488adc0f5dbfe7e01c9ee6e2f6f2a48b8b4ab21b453be096e073f8e1521f581dbf1d5a5970152d4cb133bcfe40352b6d9d7bd6bb7af125015575ec185d2e353ae80c10d48da46ac3396d71fb2ab97b5a39bef4d1b620554f99dc5619acd431898d2a996e07a57ffa2b2cf5b111e12ad5981e19939e7a54d5f305276fcb43c6c66f4c75c71bddb5157538ff6e2b7c4b30321ffe8d002f619eb5087e1403e55f8233b293f8190695fe145a39013635664de69f8a26ee790e58f9c8181b69783777d4a3181c7fc8eecacd92e1367dfd36827bcd97df821cbbb3287db1fa370fde6b74f207293054e3a75c03871e344327277c8b39b3198daf3aa993b61368725c704115d455a66a66134a59ab93bc3682b3e3d8645e0eaeeb91208e025c456c9589e147ff9480f451af5b1d1970d2ad06d37029dc379fe694c75910d78cfbe044aac3cb3b71c9186c643a7f3af596fb6acc999268756e85a63c3f0cdcc9c830a7814b1471fe1759ca26f38800468ba6acc27b1fc6c1c4dbf676f48b34718a3b6fa02a637d10bbd5be4d45c2a39cc92a72f535f50bad84b6fe5e95261b80998e39d0b42d7d920ffbdfd837164b703b018b20f12f60f86ec7681b15be77c1e25de0ce1e0dd3395e02631e8db3497f1f2a1061580a634a76ec03b43f5f9821ca3179f74eaef6ceed4802d900be5eb8ccf1008b50693cbd33043085825112aa32ddfe4c8d21fa4c6ed80ff2c2b20966a14e475a661dbfa583221d77133dfe7ad2e6e872d0f0fb2d3ab2e3e06d16d7548af94c5f1b976142d8670218baf21b2fd08eeb58783cabb9b09e5bd96cda814e22051b37b1874e0a621baf3eb87bd8c178757abf4ae2f2a7af5abd6f7ba8365426288c4ad6d0f84e8a4e9abfc8217baaa0f2f40001cb5a68a1cc9d9b2d28ea097c92fea8bb8a81947f3948c141b3c0a638a80aa8030eaf9ebb0bf10f52eea5a06498e4f630624d8a5e42682c48d6cef411ebd32ff05afef7c2d51929b05d8cabac1a876dad4c6101530d608a7bad029b83534d322ca8abefeda00ee0baa8fabc403bd4a241b60a325b15d2d607c9694a9e72f726ebe0738bd4f457153b6835da5dc1755427cc6bf4e46689e78a69218d711167935914a94319f6cb1f98a5bc6849bc6d255684261f9b987891508745b8d3494845d98bfdf2025f6fde2a0e268a3c7792d36ecc53e42c1a358a8fd9b95b2fdf14ef48a5deadf6256f2341f315b9eeae46df7f6bcaa97d0700fd673ab7e05fdf1320bd8a6171c319dbc46cb40084c706ce184a9ca07390aa0cffb5a0add0209506ceb6029eef1562f55b97f77ddad0aeaf179f77e07d5643ed64e0bc3641bc51cdf09b4de63d366759c2451537bbbcb5a718946a8828955fb280788d27cbe7c168aaddec5881904550666288ce2a919c54c54cbd8da95b09a9e15030966eeecf8c94674156403fdc974c924bfa0a3f135de8a9c5157d9c43ab089dc36e77680d04433ce9b2767f7268bc0a6fd3ceaa6d3676260f45239aec9c65d86711c23e03ca86c661401e23d9027dad571a52dff1b669e97d3125326d16684e855847dd55f0393070cdd842d34e28305596163068c7b3cc709947b89392a07494a8b944ceb6911b04a7bcb5f226ffba4ef4beeef4dac3ccc95edb3b3ef688cbbd948e61f091d21697990a86dde51ce6ef2142822508f778f3b43179e02decff25995ac8ed5e2b134504d5606e6f3e67356eff26eece69b90ec332d27bb8ee29ee0c4aecb711560cbc8cfbb0ccdfbdaaacb938cdd28eab3f4e826d71ba81abce1549da60c1a97bf74893a2c058221560512af040bb906122ca86e39971ee940ed66f65d79ad816d71ecc28b3a0c12d876cd0f907d8a4133ac33fe63149de322cd5d47ddeb187507ff647795e5f59a085ebed655e5867ec3d8cecea694302b3ff0f50bf1080d3f3590553d25783ab92ccb0807d53d98f293e359cfc73edd37c1c448f5d9043a72dce2588d4542c5f6caaee21fa4cb5f798d9a4bc7df12cdd5d03b58c0641b0be07b426dcabd03770340820cd77e0bdd281279a860e878842482ee299169de90f7a6319d63d224c74c7b01030aed8de9c7539431ee2b01ad940d340de959a3b9596c7b00a4be5914c91166d56e025e08b9dfcaad5311cafd86d3739f71a5cb0e8" + }, + { + "seed": "fd11776286ddcd876fb30913b6b75f1473744590c584d9df63ebc2a2af3e9dc1", + "pub": "bd642a283645774dc3c46ca748a73256871347b6e8da3cecaddce63c4274823dd6f388a9e085b877048151171b078e5b3cb5a4e82f6fac16b1e161441bc07dd747b5b674dad2d556f2a40bab693b686238bca837c132b14a80468b769189e38fc953ade9870c276f613d1c09fd1e65cbadcd65a56eb6e5a2913686270ba1f4f356e4b865fd430a996e0543dd0558b71af3581fed521802a602a639505616918285bbf079d98f7cbad6e0f84ec979dfa359ec3f30e47fde4d0b18243a2f8c1b45814eb3e98a6630769a646c7c17d51963b3a7f1efd0b940a778f8aff595ca71a87ff2a5a8ca9a097f16d6c66eaf3a3a21b7ba011f45cb993048c5c6fc32c2e3ca5e4dc7c0204f932aa0f01eebc002f2ae56e7cb6a6c2b113927df0e7394cca0890774f8973abf924643cd85e494087287e767da9894b4daafd8013cda66f198743a4e89c09d461f93c33bc28a6157edb87584bd0bc953f65685e02e4e7d72521c5096ac729f7990025db925bb549f4f42454804fa6c0c558c737b829d99ebb44c3e3dc782d11d601af682ff1ba08dc962108f259303f762501f01daf54dc15fbfb5706f27bb6540906afc4fd6d5a0634ceeb347ab4f0cdbb06f55cba678e3aa810d67245f67b14f9c7fbad9fa9f817a955af151e3794f125c994cf7bcc01e980b9cfb0f130ed1bb4a588cea66768fd050f21d0ccca4a903f1fc01d040b1782afb3b6d948fe02282a5c1b8487a4f3edf4a5cc6b96abc44777d0dedcc018b76ffddddaea2fd414fd3ecb51e6782858f4ebc4e3b508f823cb1f262f4135bd19a7df03a18cc162b3574cfe21f549155135205ac7532933f092348858ac1ac018f10058f28b784feca816d4bcb3deb721e58ecbccd2274f9fd575d89995eda6c0690b08bdbe81263aec448c5485ee856acb76993cbaba2bc3b6d56f01a9c136e3ca87a7b795e8b24a44e049c2975baf73bf5e379c6acee2198119f803c3f9bde0f7877ba922049e22cb16299142896b23e5ab45c32d496e2c97dc3f500dc529362f3eab9139f7421359285f4d9711b53c248d8ae50d036fc1efa150615eab78521adb46ef70fdbce1485960987c616aa542baf3ec54eb23996910c784fefb41fa389a2410e5ab1034dcd1265d2f116bb2bdf61dc229510962b1ae3b52c045aa894a3756d22040ec9d1a989c6c110c75cf26dd369541963b2799436ed412f636a57c2a1b65ea77b0e8314b3c9a6268495d2899ee62caacaf16af1bfbfaa66487bed0dec53c1b9e91ee0f6d9cc7562960a4a37b8266c4854164f708d11bad0a5fa1443321d7d52e38fad8b42b90fa70004e5a493499515fa9c10d66a74c5eebd7931fb1122be6d07902ced23674fcf8cc80babdf1cd2e26d0911e81dc74dad426cda179dad8971f6b9e1b4017a227b093332a3849668ff1c26c9582306450a6fb078b03a1ef7efc70b31bc063c7b410c8199a37134c9f7b764abe62dacc6cbe4ccf7defa3aa23ff38e79bd34bd99506cfccaf4cf4b3f3e6a1cd453db9255ddab516049145ce1cac8ffc690adc8f339d1794da06b1c35b98bf13eb09a028bc288c5238107afd73aae60b9d4de26cce4ff961033bcbb278072f20222e386210fd50574c8c85b2b13129e88200095bddb623da86cffc1636bd89f39167af3b1fa5977c815c7d9834ba804aaab5bbac75c41a79df8182dc49392af1dc8a2667c395ef21561c601fdbb7ad2d5009bf203d7394789256e6ddea7890e281c09375024d4853a8a092470736b2f481cc73e8ddff0d1d8fd9c168008444bd5ae28cc651d40be735b506a7e201bb7d2216d15c178de1bb55c5168df2f7ba9da7fd790a3be536ef5a854", + "priv": "bd642a283645774dc3c46ca748a73256871347b6e8da3cecaddce63c4274823d2cdf4f5af97a1c4faab2e3e85b6d6fe1d8d2c5fb3daac92fdcf67c0660405f559c53866cba0c5a02dcac42d2b0dcef85c60721341d6a0b0a3a618413d2c5b068a5271cf72acc5dee6ba57b40f54bfb521861b1e599953ea3ea8f3692e2dd4d311211910bc38590220cd3486c1201501c3000a048649ca86d10234411296edc20021a39081b2246d0162502c92453261158268d1c10454a104943282682a62992388a64124521c149e1906922084c0324518b904901a54191062014956114264449442a54048e233469e2b24153008e80367121482a04942d5a022010196a4394259b9450428825e30804e402801b396298044d10a050c9a88ca2a44c9932261b486418389213052e2485604a248a00272059b62003366589c86d11b161430411a3a86d02374123207221404452446a63c6411c188d23c42c5aa4648c202a1b392582c2258c86210187401b0805dcc649033352c0088200425098c40514c5301a2711649811094721d406501a9648c9340608469102a10d144970004751d2286cd1862552482aa3207120824914454e03080903818d93146c24089118b4010ab7401886099402881303909812410cc00ce2a60d22307299c02888300c9b208c9ac62191c6889340094390695b2282203164509831a1800c1c294992442ac4146d0346922331624cb40d8bc889618448cb802d243750130465d4382603020691284dc4442a14062ed38029924271e29889209780d3a028ccc001191762a2c45144068ed3848c019411a4024812a66888046544c421101548d0b24148486664022801358ce1300221496d50922cc8302ca0842952048e134471a4160d4b842894b425d926298a4222411002d9180e4ab661444002182420a4c0040c3271e040611990899aa62c03a24993044122156054424688868489842513326a24a291c0468221328094348ce10262d12404e22664109140db424e201661c41620c9a0710c4100d4a041431404c3226d92b08420928d20b73052464222052e4c046918161260462402c650a1c484c34660500689129221111624e4262881349189006189920d42020c9c886c40c44823386e94426edca44910472614859119b1418036051ac1095494899c001280a88558b86c23b661611005e0344c11a401c3964d9a042023b8705b128a91227023214861c46919a744dbc46cd1146dd24888d9a0258cb48868fd61b8393beae887f8bf8a08331e296d43fc0c7af5cd3e6029909866421471eb66618a7aee0ef77b928b1ceebbfe53dac8edf7c9c675b5fa70eba1f581eb0c018d7b47df541e8a5d5fcc261650b18296bc4ca75852bd17566be6b20236b427ff0fc7e092c5b9e2120c7da0a7a2f040c10b77ed3676efe339fc46c199ae4989dc629a4ea8aa20d36c0601663ba57c062a54302375686e1189cc5edca38aa4a71e9de23e8a1440e54028e73528fbe4890dd11a459fa9ecc1a9d3ecb06546bb29a6bb124e4661db84077eaad73ae80ab4259070221967f71f46a89e81e94b8944b4ab86775b9f66767512272fc5b8a6f16a89aa8782f9ff68793883ff97114c447dbb649860a3b4025edc2d9409408c35c8101247269b83e0506f77c65647b88a0dda89e1d069cda5148a050d13f0fc6e4016ce89fdd30b7aae9512e4aecb1c74f6ee50daaabc827d78a1bbb85f9491f75ca0f16822cdfe543e084b6263dd7c918bd51f006410a5814b7e938873d2d40bfc508914ccb44c8890c316010f3ddd50ec07b3c59c39a000174856f524c62aa2e9929ec2241cf2cb1c68be9f2645c75150e3a1415d0806742e2b4d36da5569c0e81146d2b3033049591b63f0263acfd97186cfd022361dd43e5854701832867eeb55656a716e3f6c559f04b43f2d759d4841d27fa6a39f90877175c90b006e2c0124d762bc253df05c6ad7707ee276b20f68b1cbd4d7b6e51c7fe7fefe44253c2d2fce25a614588ebe6277e3ea0182b03466b8d2c998f458a2c3bcc55e3b71fac76416e4990d24bb1d13b6853aab2204e9eb8ccf5c54c534c1a84c9474480c959ec5f91169072841c0fd6d5ec6d87b2011a5c73e58f18c462ce585c52c0dcbbe071145132cae1199205f65efcc0e2f163d966e38d0a98a6fc872bbd527fcedee8b661191a5304b2f56e0c1066e07715b2825c58057887a73b6d23af031747160ef6c73178bf46a50dbdd80fa3a226052e6ea7db19868eb4289c11fea83bd15bd511bce5782082341f01b5599644878d621f52a02edb9ddb52f8a1dc5b151ec151365d94e7a8e35e0079e7d3dc9d44755cf036abdbe17b8e0cc42a5273a8ed8841b573e1da802361887d647eb2dfa5256936b4004734088a586be1ad1c07dedf4c35f05b922a26dc71f3b3ca40ddb39f0d16ee51f5a2af50de60d1dc2014e3d673397482180ccb83f39044d37f064f63de80719c6f2ed235a35cb79f5e5a7c8a31ab4d6ee867d7451eb514891f77988308fd269e816229e3f812e02fb2bdf717d22fc7046defa8d4da8a95d37c8d077e82b2dddef9ef371bca1569839d5d7315c9121580d3ce67e6591ebae8bf2e4783e71673f1d558f0cec4765e354299ec09e77eaa9364b213e4c4eabb84d070f801e3691f4ee76c18a4ca69422aaaa8234fbacd09749e9ed55c97d89ceddcb86171bc8b2fa2618b40bde9766d3688bd8dc7fb958c24b0117206fb689c391dfa75f2e1d7039b09cd815064485faac8bbbbe3c52df12743a9758b5f721d3d4a811dbd2a7c3bda42a9fdf02fc440489519f2bb39a4ff36f92f229d2cc609d2d2bdf5a39489a71a5d06206db564f515fc0dfd989307b4976ea972e9180e64ae3942d16ea63ff9b5f4aea48b7d29eb78fd5062517c7390e385ab19fef65da3ef887facda423e6ac26936ab62e5df0cbe2d406f816cd87b032566b7cc186ec98e434337b057913266a792a4276d9cf2ab0f25ac88468b9738d239d9a495a4983a7cba223528f07d0d00c23f8954ecd9fb4f4b07597124b84301505b8d5773bd23f0a1bcbd2af7b6406a69c2f9e2916ca58226c6ad909fc8427b3b446a883b50f9ce59f7ef45b0ae24266dc13f13c51b1e1ebfb62878a508e64eccd4e1bd8715842e97b2eecf8e89e352cb52bfcd4ebde4caf54d406c850b23f8768723f003011e10d93173839e4f0ca58d2fcbf648c5b8b3d39d443f87f13ab4b98e8952a8ba66de70d49442a7b88f47b5bbedc357e9cda1f702fcda75a546989dffa2656ef061c9da7000241044b97497475a538e9b3511a61b6f18fe46455615845090a1e899a66bd86c50c7a19c136a21af39bb91cc46458a1239478324b7d713d9508ab902f1fe8d5fde20aa91371351c44a6d971da2a9d8c665a2b0b637f2997167788cf2bb5f15fae794194881becbdfa37e302b0da2dc42753b6645717a98f95fa6bfb61c3c32c2dacc26b18bf08ecb9a04aeed892bcf186b34f4b5004562ef91c8492095ded5b4627605414b6f74b00c22966de5fc21d003ba3519d0df0e2812471126135c08d80606cdd8ac249a8fe034cf065962714eeb59e455d12993945e6d6f1ae257fbf5a" + }, + { + "seed": "f49409a67dec616f636ad26b725ca897898d2ccb583d00ebf5bc9367028da751", + "pub": "9f79626b93b2f0a7e8f56617a00e6d22315294667dde4942e77bbb512362359d406e5743909bf2d103191e25de7730950eb2a18bee5aec97c406f4caccf0dc35c7863d9a4385044a4709587b6c0a552d7af40fca36aebba036921bc6485369a4668a21eb22cdcb540988738f50ce11e7b56b865cbd9f53890f56215c07aac2a48f7aa5399debd331108f1138b6e80e18fa802bc7a8c7f433f964ae1510aa9529a3b2f170adeb5a6917264a8cf77ce5ae6bea799afd21bcd73a957c190cac924fdbbcc9c68844df671cce8761170c148f22eee9828dc45e25f9aa08bd0a6d60a32f2bc0283674f8ce37a62d8c90f55d4436096c645e38fd1ab833c5586c39c5ad191318389b54d3828be1437a1a4f12665f563f6ad5a41389011ca574878a42c9d181db26814ad441af8146a3b9e8e5f78fb38c1c3bb122f0bbdf80466355be08070b1a60661c711e9042299055d0747a6da483088adb9262a3e93d7834bc277c4816a966724ce1ea3a5cae2bd54726bb1c231def8478e02fcb35ee572dcef1f5cd921e5fcf142d5fbf3ffa98d1770c0edf328ad6066b3884098bd3258a1e03676071279db1d01e1debdb0617b6f07a6e14858fa1c7790a66ea721a365c30aeb8fda3960f8a8e2d52c56a5b0c9b73f9177a3a553cc631dd35cbed7bc7c91cf317426a20f3fb01a9f4fd1557d2e11e02b964857d90d434b664706905cf3c9576749a313dbcd2d5a25ebe2ca752a30179b5b9163f9de7cc66accfc58e42b51830dd72e0e04b8f8ed8fdbe2d8e0c930d9b17cc27e9b58136bd847ff708d148ffdacbd84c4a7c31b4714cf196c38bc1d4e6c5e4e6512d76d14b166d85a2df7e741cc581923c1977dd95fb1dfad0b0226e3d7b85e124d9b92d414b1425235a861403b70e0d007a82bc40fc50700c18e9b0f30c4ffda85282b86eb476bbc1312349006e2c916de486800ba5adca0a3a550f07cf29c4f38e9f8b2998f56a2578648e167dd37351852c067bfd7924f713712c6107ca39bc7d278242e4708456510f8487a3517aad131cc9bec0472fce741f21a7d46f011b3295d304c464af294d580baf2447b3c47157cc44b7ff6c3f2fcc4781fc0c23eb2d0437b541efe4af9ce8f51598914120dcf4a4d9b38bf0002a862c476c84ea01c973ee8307f95c3d902d025520c08499ef9c337a4d4406bdd7d5a7e2b0d489ef307205a271e6312b5049fba2400af79be6bbbf0a87893518d8c4f479a1b8dad5e1a5653f61579ba1f535a0e3aac54df1aa0c62d82f9f243080f3b8b43763c0909b5a9c33c313fdd18561d7476027267127d675fde37efc3ab88b36d4ef347905eb62561932a0151e593c270f12f2a54f1624fc7aa3d28fd7541eeaa061640a6e3a81b9380ea0f9cd539ed2472579a30c2a6b6ab287c9ce2844b8c9f23a4d9db2f5bd78b2e10935821f3aef5798fc59049275522c08fd49f7e5e9b8b9befb6466b8fe5b3c401b8bba29526f5e9abe442aadb40ee418d3f4f4274bba4209c6aa71be2d4ac4cec7e60b3d1b0314447805f0adaf26b7624984c66e7440f13158998b9c097d46e7055021f65aa44ecf865f592f3b8d23b5899834aec1ac6e6e6a3deed65daecbbbeebf94b64f9ac23d1661d10b077b186eabc0ebe477554a36ab8c6a6e7f85efaba4803b6be3b6b010cfb09c89eb2a1c41f2a5fd9e5ee68c58bfa9f9da0e12eef4b185a6995d42d4145a1cee87560bbec1d818da13fdd955522407f5194bf1552a9f5c4403cf5e799cddae6a2bfb480b22258bcec8a5d118d34e7f98a1b1f22cc88b5f53ad0e7f9a02b92259c975ce46a8bfa5e2c8b288b1e2387c49770a9ad6ff93fdf92334578bad", + "priv": "9f79626b93b2f0a7e8f56617a00e6d22315294667dde4942e77bbb512362359d7a85e6e5e14d4891abbd7389147a4fc963ca7c641f0895fb68e1b1248b4edeb894bfdca0c92ceb7d3a1f6a5a9a0aa74e03338c9fd2ceb808a1f818754c48417beba3ba3025d2b0dab378934f3ce0f78fec3839ad5c46fb2cc2a67cb5e76473f5019621e0c82063c04842486a93020024082992886009484d91288d5a18480304010cc949e4244103276983a029d42669dc02611cc5815446241a84442230601bc84d12b8700ac34c010968544610d8b608d4a2499c106c04241003928511b58804c9414c2260dc9480d2125059264810a484d8c60022272119986962260ac3c428d2864401b631a038814c34650c192660306ed3282c892665c2308902486ce2b20ce3100498a22909a161c2146849064a20b64459c05120062e619429010944e49071414822a2204d12a35003110849b04c23154a8a0068882851222206d20265920450133681929004944201db308224c44dd824061c256951926c0028250a0971612829e2806993180c23160621148d5b064d083545db4069d148485312485b164ad9b011c3226cc43889d4886548a440133370d102426428329a4625011651e0c82888263040063141048ce0c010d18420c0a628614290e1946101976553340c1832694322450cc00412c53163942542b43158c48583888814a78c11298cc39200803246022464d4c22413a271c3082620258ea0a66158886908080288346de482841a064e0b058488188658386154124e90c06dd2264a1b2546a220894218661015625996249a9080119921ca466ca488804a106821800d23190dc222481ca2880a128c5c32449bc8810a4711888025cca031ca2271c3460e08a141124308db000d43b24122916941a4042340315204050009662018050916490a172e1cc64103a449512805d8068618c53110344601014acbc89014134d88161288904d0c3366429060a4b48958c40d22078889264a12a04c1a2705124129c3488a4a282da1120491100e5b004c88148518a304214452122989023320592422cc2860d2022099040250202c21280960a65002442298c26510406e03b96ce412525a0432cbb0704a306dd3b001090152120632534445d812721b91841c404898066818c66421b85001036299341292328e51a28999046a84c20d2138600a48694234729b98495aa20d931801d424645b3486e08281092404c1863121c065430046090722a2b0dea587c09484796608c5e4d7eb90c2dd53437f8bdbc9c283e48dd02e8fed46d9022f4c0a77c50136ecf9f79ca812d27f534bce467abc3fd1383dc36e60abc24fd6edacc4c0698e4c463042e0bea0d29325ede51c7352fbc5fdac60bf0d878ba1545d592094c2f17382b8d836554fb2deb424b933178f0abc8c5a921858cd981a9d8de7879ee74498409d4310b50fdb77d81ca40548d986ea5cea8e4d8680ed864568a0aabadb6aeabe82237b99f5d1274c726ebd0199dd630ae81416857088c51e9bd06e217c373e1a73a7c4d442b371acdfabb7b08af6117937e48fba70f61046259a4c34d2e3b912c136364ce086e1b9ec49b5aed018f92d5455084f858a50dd9cf8cd8c906d2775079aa0c3768fcfb9fdc6029c7b0a73b6c9bb89e8e424cf8f56b8435247e36b2c073b6187ca1c632f0735d9a1c26332a399a349e1b8b87e304b5dc4fb643cec55205be224d5beeca8468fedfa3ed79514a8fab31fbbfdfe7e7be6d3da99b6d83b48bdc68483b803f4681c9de7d470b23ae4c345a23dfae9c1db14024f2ae775858813ba375222997207e6bcd6030aee6f8cb75e8c5ae5db55695995b99045d43b1bd85738db93dfab5de5cffd56410ed76a3717996070ebb0c2f7b4dad13358604db1bcb479d0e13308098eb375a8cd989b8cb16d92f4fff776a9b542680302c3288034410a141242f6338d1906fedbf7a369c0a2d2571060c11a94cea8de591404d0bfceee97ae547ce266b0bfe4d087029f3dfd0493e0d6fbe30342959ea1d4a97c3e8f6dbe7f754f76cbfed489f0d8ca90a4d872014fbbd01de99db63dc14d0748afa2e289700e0258cc074e810e40ad79a47d02cf285157689d1632893b94a60e1db93365f53180426e9b771b23d49383faf25a00c70aa934f139480df16e0fd44b361eff25e199d213dcbdaf4ca6aa1d262464beb774ac2a0adfac84789fbefffd6e6986b91dec5445b6112cdd438c991a9a701819ca2894a2533e48f9adb0a6e4342799d251217825558cc6dc75cc4bd69c49bb7f40f454ce407f98b23134628c7f2fa56bfafb4c96434c7b610e3e24ae64a79c66934ebbea70f5c4ff626f46d28bacd59b6967f176c11758f0c88f8323b85d7b9b391627145cb2af62860769ff3b2b3b26d0823bb974a740c189c0bc1788e644d20b815400f499e993587351140eb90f935e840e0559805370ffbcbbb6ff880951d0d32527bb6b49db4b79a713cc81cba000b01f3ab0f84733e8c6cf1fcbbb4b35b56d1dcb4555310ed89e8d7f3340202a017f2d08ab6fd33b381c14a3c8806fd7f469a2a8e76cb3f018903def317bdf5a22945f3b6806fc1648d731d4ae5a97c22af65dafd578ae80b8c088abb553d644f08f6db569d411857535b831b171689e96c38b1b176731a8891938d508fbfa2e139a8b98c1f5f358465294a616b7610627edfe8ca75307f0859dc56a8f006b53a2f090475e19a5cd67478b52f13fc5eba409f00b23804144e61f022ec2cd6340a3a374447f72ff47c8286e462e44042a52df31b80a6cbf256b1ee03139a2efc5e34cd70a7125b9895f04c0621e2b85f93cec68d6f68de74b501b64c826dbe69046d1a388462bf4ccb2541c23058f0d52085b6ef1198ba966dcb7e0b408edd73f0741eb5978a6c397c4aa286464e122602eb5465c5724c2a7286dc3539db789ecb5fd4276e84b8f6ce6a25c43e35bee39e71476a2b10d3dcda9aedaef22e351347a932a194b2aafb2942cee2ea0c8d64b06a197f22374263197e2a907f28ddfb698238396950a592c7003c928a2c4cfc2435382ec2b30a791c6cab305253ea8ee10c74df9549d616d10571d816ae4628c4935f22cef71dde4ba9d57e3670341f20df9dd799e12d9cb96a83633dd9ecaed723e26ee73a4851c310d3b52d25c8b41942d4e281d2edd5c1ca0ba0f7d84762b315a88bc11c9d7ee9fde4eb9d17c0137212b42a42b8980b07ae0988747d28edad0fd315d7dba8c1a41d8fa461b66b0b50e72df7c52929be6037195819d787b09697204d4c3956672629f384da37cbde504812f99273c013a4d8052c8df51755718fad9325cf99360d79bd89243dd01583d41e71876d2e08791b364c9f73a173a1516a0599475d83820b049cb2a21d654baeafd9576efd431627a70ae3fbe3d65b9d43629867650cb38dffe8e379f058571ba18c90641a9f0536d4d3494e635ac2f0163e4c81bc8765643b425692f00224cc6f108c5daf96778ecf07f153807745fedbd55287d9b6fc06c3e9e1af0ab61b168febcd048a8e310a4a678e62fc5dcd8a96c2d47108677f42cbc1d8c66c999f2fe42118d0726082b6f78df439b" + }, + { + "seed": "5b2a6370092a66d4c0102fc2d8c303ad7a399b8d25b622f7751bc82e428a5ea1", + "pub": "ef8af6ee13d4d939e10018a4bf851c592bfdc8ff22cf2a9774062f72fdbd621eebd8daafa5c8bd71392b2448db27c13f0b3596800fd5651e2b5d3eb0c85bbcb14bbdbd846090ab50c57b0312d131c0f2202720b01cd6bc9527429e5e08ae14e99aa05b2341b812a29548f9ae9b43c1a73e3b4856a5c7169287b15b21f47b5c946f65ddace867969786c81c770a091644fb7c9463b14724f09e63b3dab13c3c6ef5073ec4f4b5ef2fde2daa40a5f56fabc433c32875c0a03a44861ff658c2ec3dd628715a273ed66c8d9e03f0445ce7959004f88fb0db79783ecd73fbe736c3f7a96736903c292e2bba633ef7d7702e32a947f35d28d24fa002bd1a8c886ea9478cae9f2a6593c3dd872739a29cf01fca465c7bdd1719cb27814297be1f8c53007dd5491ed25fedda293124ee6887449073ac6ac193b8d2e6170bdf7b9272337da31dea3971fdbffdd3dca75cf6cc50cae6a449eee04f87367938d040811a0d375f51898ede2314cae7fcf2670a69c7364599e9bd883c055f7f23187faaae33e1189d07dee91d8fef793b0e75faaefd44e6724444f236ab05ca88349f15451adf90dc68911fbe2d7afc7867d321bc5450e0d089c079ac97485709f22fa0b938a00278359b63eb469114b478af677ba35571f0addbf6dbfea9b487d8bf39b099615000c4aecc245657b7822ff54d14df262fdc3deedbd3c7a55c499ecc45d67b29d0cfb1e2427de6e11b6534da23d358ca3a2867039ac6d7937e2d241d9356ed5126d4f7b78094875592843c09f03662b32be54ccc4067e05348886856d272227b2396abc52f728b063bb25929fea9b81d59ed69d621fe97816b441dd94667b921f85b7999dd9fe1aa5ecb01b5edcaf1a8dc033f56921dbddc7cded1cb3d42ac0f090b57a0a1ee371ab65e89ab9f05cab1d5a74867196aa489d944f79743ea2b935cfe0104c196e544639b22c7656b2cf79f7918ba12f80b14166785fb5c2f0572f066cb745e5cc16827bb147adc7b51783c8ae2687a815575995f923bb42a275255aed8ac630e37dca4953b9c1bc79e6f94e0404e140164f995d8f8672d42def349f4c019f77af837584891726a4806f0f920faea65acc393562affc738678229edda62418a76ccd33bd4fe2a5394b792a7f7549065ce9a99afced4925211fd5581d4c67e03973c44d0fd8fe310dfb847dd7bca25b48b14b94081d513164da60f81d4c9af874cbdd3d7a4c443f458c1c05879f0c576f5724e33abf738a086aeb482a5af0ee45ad472bc54e50b7f471bf1cd272a53012176374bbad97b8be83bd997149e03c401cf3c1a541990c4a632716e7e16012a946d6c6893c366768d5e5a57d732cec2dc47d6b2e4090e6c7293d0129d2af31eb941c55398d003b906b329581f8735284f238dc33f0d0327a08265eb2665d1a7971c61075a7529079176460760c6c36d877ab0590e8dcc639d92fc0ce1ed2cee85af984cfaf52e5c57a05884be359a04ca67056e6e90112a5c1a79dc7db9e4508010cd0f9a3f90ca5769fb4c1a31337ffb5e85154a0add475c1b712aeb7929e2f7908d98b6d25ffb16ce5ca4b2d7dc87839990ba66e4135ce1c0ca727bfa51f1a407c790e6331446f6b54365e3c7bd130069090b252d363ec3f2c8caea466dee10b89e1f0537552b4c73cc7be8faba6c73504ef2798d949cd20f277793c0f9879409b1a43d6f2d0ce6e9eae2033e9582d34f1e4f70ab03080c3723f6c01bc55ccbe6445f51a2468bbead7e0acdc518a7916cb9300284784dffd9d25ad680ce071b8af4badf2efb6ea9d2124dfcab5ae3aaf0f9e72b704a563c2656308074c77a41c9d58f07a8ecc3b93897", + "priv": "ef8af6ee13d4d939e10018a4bf851c592bfdc8ff22cf2a9774062f72fdbd621e9a447b65367f4ecb1b750e44b0ee8769c30e14e76b80149375b7b4472950cdbc63c69880b088245abdf23f7f1fbebc0a81d9910f76289c4483be3abf5014576b04d81ef931ce01b508e9a37a5ad8efbc1b80b6ba3ea2ef0a1975429f7f963dfe6182519a9044218761d2948513398403b7718a406890128219464e11b3652436281206802386810ac088a2b20900128124880420272508324861304d1b808891324198c651988868da444ec2468113470de10820899009d0c82c94b82c83482a800071dc102ae4c2410cc860e4800d22206983062c90068a814406d9222da3c02062c48951484599c0450a15241112319c844909972158082e8c4289e01609102669219404c8b20819206c21c2842126118ac8241c29404c924822334454125114a211d4864420b34059b621c1220298124898a665c14004d4404c592261244992db206d094400d4986020198452c24009800023434c23a00100c6316390499800201329524b3492022888609665a0c62dc4080544368d91b8491907255ba044512829901825238268a3a02162b6895b08008ac408a3b28d11a2411b034a5212911089848a8841e14291902040d4a64584082012492c522684c3060919c86cda360012b125c0c82020106d639484480861080180a48630210712024382e00828d10241d1306022882d9ba24c14a071881852980404230521c0846513242261089244408d0b4362d84062d4428d1007718a468e50228ca4828454a68981c4101a126ec096300b474518304a14030c02000ec0168019132c62a46ca244045334640c0380412825d94801214770d8108c09242dc8c06910a7712196218ab4719344688bc22102056064080209a4089b386de0482609a940a10248e3b22858004d139740e0a44d43246188a88d92c860189630e494840a1192a0305184446099b04991c4291b30084a08045a160024156ec4106163b47141308d4982208aa20c921804c9904524426de3921142080182006504970190304a129989d9422d52a20d5b322524210c1308209148406342828c48611b8270e034041a238d4c0090a2a06d60166640162514428602b808000869c8004203852c1b2810e492690b468e2023114cb800e382240b0822cc4024489640131661c806725a18510ab04822c8719832919b1651240672db3066d4a051219400dc0281140920a09680228980521011d91d699e20757c2a8faff52d78ef4d500d5496a4c554434e7bb5b3445957241991cef60d776a91c753155e24ac9a2a904b67427953b8cbd2e019feb398e06b67c8bc722fe68542abc7dd37e51d442b9be301e10ba336b80babc081aebcff29c6f9076ea6496fe6b943d3f41009254c644ea0f44a133bf27b06fa41e78c49266607f7dcc9787e24eaafdb74c6912644faff379499aa48c5286de49c7dcc8dabe01f9ea49e69c4ab247c56a7be601afd4b2f224e7cba484f4b9f69ace32218c847eb3e2b4d3292ccd298b3465529a1115602dae0df14a1f0730ec85a80b970466a850af6a2529363efd78634bd4af0e4ab7606ddc57202b9d5fbecf7e97ce23f8e19af023c2739bd656972c03b94a8979614624b58d2d70bb9f09407e8281350a46630b7efe1e4f55b4baef5b53193b9818b5380a49dae65eefb1d02ad82acf66f182aa5fbb97645a7d4eccaa3c4450d1f8914aa1a7d37025ab198ba69b884d7907341ba7fe259fcff0c1e760061daac6b3d9a57fa3123604a9e8ef8cb45d4b1af5fe7fb6d8f047771476933abc9daf871ca79f8f04106a543e4e72da05ead3b63f8cb9df01b656799f2fad52d1977328211fe362ae78e361c93b4aec9e1c8d8cf3a2bb0cb495634833df82aa5702e22047286e3812ad5395b5f053d0e9f530e344e69b12b574133b25021a03995ff453dac416c82c082410b3d4f31bab63d2b3aa8244f6930043cadc179405bedfa7f7983f3e8343a6b04a63a59dbb8f83dd3eb6be35adde7a5709f347b98c37d8e2957a7d1f4724622b7d51bff1fbb5e93641990b09b7b90c57edeb5a59280b207da0a71c7cc5d9cdcbeb356a997e77aa1b47bbde9fc468c92694d9d414839c7d1b1218388b3eb96c234ca65825e1ce268848af303e34c22dd597202c5bbcd81d7091e4ca0d0480609d648f409a7759fcbadf4113fdb832c0dc66351dd000f87a69a06051a1bb952281e200654ee366a746ea13cb1cbc7603e7e1e9d18fc366da8405c0b589819f136850264474cafc5ecb6bd1c255498cb46765bce0693f1bc010ece3270b0c1ac3d086ec66a8813e9d98b1cb81630572972c2e473ac0d3f291dfad56a2fe1c02b7e1bc01b9937f6616d73960a88735d4368e7430c4ab75d1c9e4e0abc06864595c6e9f3420691e74831fc70e62fe7ae5610eefa42d76cb74af71098e48493e478ab8699c1e77a0eb5b7b3f05b4baf521c7a2ef02301382446d76f0da501b9dae131b142405ab340890a5d7cf917b1bfa722e4952a9b2673733dce37e4a633c08911d8dfa87eca362c5081bdb4e48a80a779850dea15d99322b11c3609fd0218f446fb187f998b94f0bf71507a10f5c1114df95a56650f0fc484ab31472e45fa71b37e31df3708cafe6276b18fcdb28f06cc1f82523ae49f66af37f8256a43a842fe489f04d6e620096fa3305503bfe268936e0ae5c2daeea27e50cf1aed12e94fbcefc8232e9fd7af8dd2eb695900a63b99fb65e944536ba1c94414d0cd6d01c249ac9cd55199daac2ed4b3cfba2e1aa421c9833c9f57268b316725b8ba17e0cfff5ac83026a091832aa18a730df6aeef27a8c9fcfb01fb59140e5e80501de8ceda329e35c57c71cb6d37dd707d470c38df972710845f202c6f8ea7c7c2c21c1edb884874d9484065a243245ba81ec24563f0e9a5d9a112afd67e82d018bb7afd7a3b07aecb15c9818522443df2660c6c67fb936ce1f32f9b7d1e33b3cb061e4679ca980d444de5919e3b0e78ce7b46336df2583c12a220ab27c27d5158a3f23131667707882b0a92df25a96da72405cace3b6b06afa2c6a0c903e8e6b581811bcd8880b15a7fe4815d5bdfaa98081c3065c1b809089816f88d68fe0bc520a34ca1d5823bddece839d149417add8ee39b385ff208918bb60bc168c5c73e3ff3419e0e4abb4c2b2c2b75f35aed97adac0e8ba4443d31bc06ec1f68359bb0948127b2fcb546f02de2869aecc5c4e2c3833a6490de2d41f2091f77edd2d9c53c270592d4517ce8dbce1124788e3f812a9f26a25156574b3e1238dc5c6fbb543689c52245d3189628232fa369dc0589a6afe2efd64c8f025b9df60e2d10b09e1105e9864b37be62d2e36f4b99cb6718fb938a3800c87566ff5a46773cf1a9a2d08319250338f094659be9db4201d04784c9d911b485bfcedf4e4822425c0b3d99527d230e84bb325a705362242fefcb067486c63c626b435ee15588ac39d25948ed00fc1fef8c26edc13746535dc7c576a4bd0f46384029e82b58cd8b6839239bb0577f682947fa971ca94cbfcbf5326c92a2b7f7b5c307c1b98886413e96abab7519cfe9a32340e145c35cd479" + }, + { + "seed": "986897ddcc7582d8b695da6f912a6b087b6aaced78ba6af30ed8140b206487c8", + "pub": "a9145e8eabe23a73668adfd6182ba69e8cc1b0f1fc90cdafc617ce173099768f2d33486dbb851b392b3edb03f23a481b05821880d957a87def28d1e7f82ac0bcfee5169c80648e2a63edbae8d1a2df2df186025daed43b2e9df0ef3db4c25546c6ed4181aa45fd9fd76fb753b2bee8758a7e2faf40bb10abb894657c041fb3b4ba24bad5a1d22081871789880c0a1d2eeb536688d77ec316b8082f65e5e61e569a3b932caa5319095001056ccaad3ebfa1df38401127cf303febcd12071b6141777c0cb4ce736f4d19c3e33c8d98207d4a5ffc0a48cdea345f2b8cb7c97352d000f15093c57cdde8ec261df712019f8093c7ed6033b10a07cf938d589f61b6212a69f28bdd05e32d83f9961d15a01aaa3617f3526e370961e4dbe5b3c9d4a220398c54a5c1a77bad6ba60d40c3bb49c3f1645379d403332d78e785a590b469e34d1b6f13912ccb1e7dd4d8c3cdf4b13c4f41a9d7d0f23f3cee4925f6eaf3fda87e5f5396a673cc53c53ea5acf3bde3443546c5d25354a9b3f82016b875a74652fdd4aadecd56089ca5af8f99d04804786fd8a97803c24a1498a59b2ac4e7cc28845ac0db7884e4362891a66ed6a29ebbcbd8d3eb6302082320309c3a75c363cad55bb7fe33c90d94103ed69700985a84c8c3cfe2d678d9536277b0dec7d645f2bb072866bf0f1bf6b6b81312f6c09dc1a68cf0fe3416300ccbad115c9db573fc842f0fa5def49c4c77dcca008d88b8e531f60d84324590f75f6f319b393fb03a99bd53e0e1a4eebf72161db6a33a5c873f2b3fe50d35f9c750ab7328727ceec207804b96decb376ecbe259c9994ad98096f27de3dde285bef27f66cb86c4a09fc2be0647e5227e7e54fe10661cc2dde4bde47f0cfc04dfccb0a8c7f40553f455968bfcd355e07d963a1e3208661dedb9dc996f090bacb1d123336df5b47b184810c0907b9f5b2220c0e192fb839b625d48ca876d5d71b5eb83e13f744602b0d02c8bf54d3b9bf3fda643a6c75ccfd3e89c4a23973c14c7aa10a60bb5f4652b90dfd612678d6e3c8243cfe0222a8a87ad52f2a72159dba19d41fe0bfbf5a42db81e3776f595f1288ef4eceda2f1ed696669032a18b45a6ef8f3c775ea6f0e0fa98123c24558443df1e225fa1518ed8d8bb617d5e5a0574f4706aa8c34a0b9f6822f8eb5bf3722cf2d9f77ef76bf184bbbf7090a98907b74f9a55d89a5b404f6fafb267d923f46539690f7b924c65a6076467d0cc94c43330b99bff084b7b9718b4451346ff427ed3cc82621e46bbeca950a509fd5567d3ec61b2d5f54996adf375dada1525a21360e7c4677e8798cabe8883c588fc1c18b0c78d3196b379602b2cd0cabf5f49bbd35ba6683889cb07250488a382fb368cba0d97964370b89b3df2eb3ef4695f2b040cca25d69b3f5b84a5a62665e6a7c7c8e1b2e5562f032d244c1c5384d7baa0aebc53c3fee831adc4efc6a032eb87c6aed6233c7bae163556e7afd154d3e7c01dc05adcf09480cadcefedab21edde2c1cab541902e533d81bbbb333dec191fceddde16877662018780db78558c162f0f0b75f747d4753470ec6035bc4ed04316ee5b097b4f63702728eeb1c72d524772e1d593b9b3aa78dfd9324ece2298efcec92d6ea3aef819d1f90dcff2553911c56f4462fad018846359162de3215a8dbc104e9ab8bfa8b821cd6b49cddb7899df46ecc90923c857269fc0335090dd3ea12752fef5f1739ddcf7790ee3e8ea93826c19b2a7b547c07163bf18111c5cfa7370fd97e1215108c1a4292f06f596c9ae94de70c1be8251f585c5c7d94c43a23cca1bc396d668a4803503af2e4f74bd6898577b1ecbfae114be", + "priv": "a9145e8eabe23a73668adfd6182ba69e8cc1b0f1fc90cdafc617ce173099768fef9edfaad381b043ca17955c592b0b55912c63fc49ba2d54282c715252829312ff3c34567943e2c69bbc7427195cf525c81b25709040d38b6f2e34a08794f1c6f4f73023957788b958cd002c41165a1c01a85ac8ea3f50de849bbec47064fd448898699a1471d3142adb0005a330421189851c8208191171c0288e9a0264d3328e2126800cc80ccc2882481072cba4241b482c13284519000600a8480923049b060d23816812c4411bb08d48022c10022509188d528870c1049203976c233808113900e1188c622851e3b60803a18190322a41c470a3160119419260b825db4805d0a06011b48508a3098186602005869ab84d0cc088409890ca968d2135490cb6449892856126318ba82d11250edb80695a006d4b0682c1162d2133319416828c404803c7281991918c462419c209d1448c2110314b14880ac65121406d1c28912005680a11651b084d0b96908896650a41421aa5691c202218394522930049224512326d1330050303508bc8105a384894182aa4226c18294554382c649401e3466a5ba4510b430c5c008e0b90690c470e08830c421002d9400c10998958966c9830641214801c934d8a189154340a88184803340853845084b284c1a02563c4010a198a22269041c244d0b271428210c20811a1064500c9410b816110b26c4a344ae42449d89288824204023226918824090492e1104d94381023448ee036689a9011601866d24089829611a42844633021e3302541800c1419008b264e1208304c1881e38205c920880ca82c61140e09c624e0368e13388edac464484200e108415898000b806c90b4412118328c0432d90620cbb04c5834125194681c4206e3246690222819b7000a03258c0270212272523021d1a20101119122c969d1b44064202809454518b38159b82042480458004ee4140110c1294a168964380801c3840a90490c11640ca7451c3010d1102da324525ac82c58904d9318691013411c058e4a480c4a4884d012480b2925a0c48d10b661db324519198e0ac0855a422de3a00dd0228a201740a03812c3004620c8600b4781d4926494207012336ad0182442c06811490544c068d4884d5082281a420ed2a42d1117401b884513492a040370029880cc30510348224cc48c02b18c22b18d09416ad9080ca1000ce21030938420a10226098925e4021021a885194405c4326598b0218bb2518c226cfeccd44860d30fff92179c99338931eaa25729b8a8f09148d8dd101028b9e1aa590e5bd111f02aefcbe07e47e8dba00b2e41bde7f62f0f825deff3a9ec93cb1112369065e23f50124e45160b4092a44c8d2c93bfbf79d509a34ef0d62e5d8b5fe312de4156f6b35431cdf9ac0ea0d97fbbfa021b3fa76fb6d8b5c25f80e8a14136df6fb95764db1890a3eb2cb51f7b2947439681e8f8999bc951429e5382d543fc995f23b674985f5229f77ba8c583763174cd43af3aa06f100de14815f6c0dbcc03d198fccff532f8fc66d5c3df324364a423f6df6355625037a46b89fdc3c431f1d8ae9bddcc211d46340d3ab21c5df54ee1e128c9d1baca76ed4bb57b63d4eb5ae97bc90eb7138517ad11ad1da3b408e6cce15ba06f8c88db2bae00dda23d7af43cf7f467c797540152b2f3d5eb132d04bb133704b2c31ff5f0dc20b4ff60668151d9345cdf8250a5d379f4680f9bbe49943f4da7fa0180515e19bcbe0016475c01b93a446919cd4296961a1fa777698431630ebd065551940b6a35596ab29f9c0b4b51ed29e9b1bc5f3d66b5aa91e824a210de839a96e8dd1ce1b21f5b2c41cf47b3cdf29797647cee3a6c0b3b3f982780fbdb1d5e2030e9cecfd75d7e3681c06f381b288bb0c51ac97353bf2a875574329cb725cb194f533274b8aae48d9033d7d78d459627bf0a7cbb39d6d8949c0ad43354de13167ef16fdfb3ebb0e8405d93ea9b451524d7a4295d5f814726285b08e842db5b6809e4c3e51bac5e940e9b621d0698edf44bf868cbc68c734dd512a33ddbcf6b59c526b509820dd2d5f78b90cda9b39d6a70ebc221864175be11d732aba56ca4439a85516b48ecb51d860863a7e95de4874427d4055dc5f0f8d987dc9115059772c8b799efffc9d0d8be9df373269982896222451793bd9513fa47cf8b65687f9c6d035878e0c3084941a35dc754c7fe58a1425efafe5384256a88b63ca7b388d7f4df3a13f16e19c78a61168d5e55d38951b1380e765d7620920f09b3420b8a431f819dc1fa8a9a9de5d53d288fd9204efe23df0d059f24e18b9ce4f5cf5321caf12fe4faa88dff2ed5db7ae3b74dba83cea23f5f510fdbd90256802e1901a4fae839a9ddd7a51302e8f9b9920549fca7367c3e65dcce963bbac7e85eceef42e88bf57b5acb1caf59b2af94ce6aaa92e32a14eec8cac77ec83535080659782322f60e336db5ecefade9b5822a114b4951f40f42be9faeb6a5700775e8e03c05eaf799ef76db5d70db9e3dd93309e75c7662e70d51da6a383a9768b454fd3134cc8dc0e903ea94e7d1e1ea95ccfa1d0a9322bd993a44280a1047a553dc4c44867d08bc21d4f9be26bb64d18e9b3a932efae37532ccf5f3cd8e5cf5f8463a2e4a336a98bbbe2a1b9e981f7fd47ffedb11e95fabc1a0a198eb9a1c8070a7854802ea774690d309e8dbbd4fff320534ddf5f4a9cf398dd6e46a657e156818cc073f03bacc8ec159de3e08ab8280cda69cfe201d0dbd00dca9fc907029899076a1f7a136a52ce710d9c62f5d9111d7b75c5d27a2b299c7918e233af8848e54321022e67b54d8f3aebe04e2a517a18f1b9377521780205662a41c718161b6226abb2db442f7ae66b616c6be32df37e3884485c50710d0edfa3aff37d611e445a5dddb9768eb4e66277d87dbc2c27388f7336bb920019d7883da747820279532a31497005dc9f87b69d8f14a011a9d2086278a8dc8b9d9a4696df86a7c562233e10b7e6a5aa9800671464589a1d07be9fbc581cb38e518d114a48e6969700bbb5577ca6baf8f8d3549b4e90dce2df74af70ce0199b79e12f29af91d2f93bf9b2a109a1e48dd9190e0c8d975774344e5a6621fafc8c2b5d012d71209801dbdd66f0c47cf9302da6582e67c9ac88618be010e7e88df2852749eb7208914ffe11a2b0ed753cf55335d99eeacdcd79343bb421d00eab20bf8fb6db2d850fb4976b3d9a7a3815c064d69ce63a1785d725a7ca5aa8fedd6c812f0b7645bf311930d154546f13c774b533b5a0f09a5034b25309fab82b312f47f7ba278e291f819e1a3b66621d938abbe1e34b864f8dd124448d10cfe40b2ce203e583b2fc033deb980fb47c3e5c2d008b7be6f113f8e00d83c47effdec098763be46e7dfdb4ed9f4b7af27e2567e928693088ae6a87af3320b51b5c1815ae89f33fa30e77f30254147d4db7414c98fcebcd7d83429c21b911b0b2abda32905ba9b6be55815bd267199aca5b52328b6c7e5e76743a63214c180db2f85aaedfc7ceb5d18f3256ffe94e133b54a8aaa09fbffecd607b229a4f2569a01ebb190279a3903fec9560e55ae27369095836b2fec6928e14ed31" + }, + { + "seed": "e499971cf4b0e437dcc5a8f0a36aa085a60309e38dd4027418c344727a69cab1", + "pub": "5fb43de33652c891f885e3891e8ceeb3f751ab92944e3a6eb03ef647c39f54e0fc94f5ce6fc1ff853ba1bbf815bb803e23212b76bde775ae80543b3b1d71fc9324c0423c5eebf1773ad897faba985b9a359d62c0e4fff4c5ae12d62989f3bb4b53c92abde858f28f35aae82b908c27e6f75e312ebb0c0601969a567d98f71d6a88e37e571d346cc4ad0562a8a880975ea835df998ddaec158c15145e944eff448618fd279c8a5a75a85774a4765ed3b8e1c371fb8cda9f1a1a03d770ad79539d6b3063bbe25af2bd2e444b8ed76b4300d70dc0bdb5f0dfc3c9645a0ffb0681d7261db4f59331e9349a9d43f0f0c03a414fdb49bb902793fcd7245bf12afcc42d5cd0d13fd06585e85a354352701633090244323433e6d1cc61bfded734104248a125495e86f651aeba57b8428de4b80173ae1a11d898f3ff8e099eec2026a3bbb6188a8be0481d32eda7339dbedd5234aa61e98c6676b6650f223814cfd0ec9f3bbe9aa20e8693bff79e76c204f5170303f2f94c4558ff5d8502947cecb4e142c622a4462cd58e332d754f611df37854ac77ee5926da9eb979b684cbe69e1a03ca2e46d28d63376633c835ccb2c9c4484ffe6e488c7eb77047c7ef19fae32f78ad181cdd8e77926c5eee531405afc61f6c24497a13a3ec0f00d131bbf722f9e1250f6312d28d03e56db1ee50d1134c000a411ef33ef315bd2f7289ac3e6036b5288bc2a3560cac8634e87412bdf3494c32a24938c24c09e420af4273013e9fe01c4719ed67b2b47b6c55629bd6a34aa422d7e097497578d007ac7cbd3cf8455ca1b94e953f4d4c2eb31ffcb5dce3d0527585298e9f5b8c32f404e14b7f5c30a985283ced4ac3d18248be8ad29a0f8916ca43b27ed21c4f1149ef2ae70509953a39cb3b494c8bf0e159f8bb62c3930f998365a446e97a8b16b5f0af23ca610bec67cdc5827e7f012745be8ad1ab6ef4b51ce75cbd1d065588293e2dfaa6e9ffe6f8addbfa183c15721d3688c51205f7acf899b4bc69c0d6724bf22ed9ef4781c54966e85e42741f8f64756a9665c0c8aed2fd59cd069c3341a3514457540913d10d75b9c32d32c72de2710f695c1e5da3b4d135668b792c58d6ba8ef06fc069a13fa64ce1a461f510200f7839d7e702d34ed3455db4b39974937f4ad948e4087099c0e55606fb136f7de920934cdeb78c10e7e2e299707b216f8b8d3cfc17117e5d499bb8f45d90af3c73e7375ecc041c8c04d5308ae6d24d0754bbac70fcd082a2ef7d9545cc2b0dd4d5bd9d3a62772b01b8c1e5b525171a9556fc18269c936f024cf8a1640e2190647775bbaa5df26034d5e139b7b9743f79182b7e4f761cca78bfa00237c1c2d04433bbbcf57544d45528c33bed41761a52d4e0b465db2542b5940bc7ad4119201563cbaf917d5e586b2ac31db9e90558facd251db04bf8c5f985cfe7215d2f6addb70c5436a75a773708c71cb10efeae7fbdba37b28154eafcb6d2cb167e8b941ecbe11db566359ce32bcd9fe1cf925204c5f56349bb4baf7a820a0bf8ca8c540a0e3f27abfdef19479471eb9d829ac835afe2843d78b5c97c72164350d0d77bafc2b20bffcfc3c5518a3dc53adf00aceb3348805abc79a675e6af4f767dc8e2f5d3dfd239b9f1871e36b83ad751ad5561a7757c9154518d5d0463ed3c88fc0b1122ea8055572b4a03fe860ed5ecf44b41358a94bfc73bc79aa2f1aaf6a479a70f3263c6ae9edf0b70c65a8ffd77bf1e614c4253e9d75e369211f41829a30f5e6c42a31146a4353a345008355cb0cf3e14668510a1af1a2647e1c13231a9c469eeb09a0809b5a277b6fb6b34c6acb99b4652d346ea565321", + "priv": "5fb43de33652c891f885e3891e8ceeb3f751ab92944e3a6eb03ef647c39f54e040106c77babde1afa5f1d37e648d59407f41fb834221f0a62b0b42d38dfde62beceb8fb9ad680f8921141dfd6c8f9cb92986a9bb3b9020297b27fbba970da5cb45b0603a60d4bd3822ad96e7e3bb787596caaecc8318d2256a8e4037a7a3450612311003216a62280e23a0610ab084218140129108141171c1824419a23184c641039508dc10415a0828924892d2044c08176dc9806d5286484c282e820024c1300e5a189118a16dc2086ce1248921b931cb086148344404088cd2224ec0062c1b8591d8046c63965119a320c4288a8c246e80242490804551b22414a4114c38850a084c022151c1200a2430228140091c428e1a40459c162a51b88111246241026e2312721203429bc06cdc9884d148414cb24022a5119138448024011a028d98a46d210981822221d84412981680483470120064e130091cc500dab2514914224b96801b0789900410922226030251133041dc928888866d54224659440014174a1c498d0907812234511c4231ca42024c264800a2848a300a64044aca424d12078d4a0209da44644c84291a352811922103a5680834916394680ca2695ba684d2202e58a22948004d0b04401b454c62166948a611c3a84011822c181628a0c2491144228c94659a402294288002883160160e94844158282a50240952c88010816c50088e4c12844826129cc411dbc428ca280518314500401103c764c844450aa564d94466c4463094484242082604a7111984011294201cb80103126521a284211108dc026ec0143011310442c6711bc0900bc804e0080e1985841ba048c0046e20226999162c8830488b46524cc88c2494455c1266581672534666d31000143301511400e4b864239209a2a01121331184222c01b70410044d18157114435011080053286c89b0900bb80c5c168c20486c502060d8288898422559b2300b8265d8c840248980e2b26188b404dc38400a07128000601b9631e146649398680c192c02c68d48c4498a928563388ea1002900a78c59b4884204011817080a278e012061a2160c88b480a34805d8280981868111b101140464014928a1b06898a0485918500c303160a24853469110252c123272db00501c279123a86dc038065b1440a2006e0bb46191422c5c38499a046d22b3400c41881034295b16448a102d034828db886922494e0a0722099910cbb890801468ccc62860428200a360bc48536b90a842e75b4b1a085777d2ceccb04a4aab96a288a61e577e9ccb9d6500211c3d9211b969128fad92280898b360398717357ccd3ea7b2e49eb3b21b6fc3db4d1ec94de81efc86b16ed370b68857fa022f7316dac51f81a108605827be45385bd2fb0b74e1737db4bcdd1c19797d77a167882c9eba054c1413700fde74b47697b9a9a4cb4bb80abadc4e7292cdaa4532c9a247db326d5694b8aa554c61f11deca4cc01ac9a314f53abcfe461771ba8a2272fd5a095a4a58a5521b1a67b7b54a1ac289f71d337b0be3d17bf55ead10cb74bb6cde4945d4345bfa5d9c6f9b4d2ceeaf0fb2b61027883e939d640e0d6d62672f6bc6cd6f042b655e0299cfea9ec60cc0bcc376994c6511edcca4fb75baab48aed38e6d21ad96b9e5e2808a69df8690b931c8ecc7f4d175d4440ab05b3df475578eb5081bc26c0f5810a8b9811077595f8305f39c450cb62bd055f290966a7935971759acf68b9593243a98556abf7ba9b8992cc3f3e4ab89de0b739fe38fae74ed107c6326ddb5e85cf385452bd5533becd28cc27506ce2742506113272afa8c26e143e4f401ccf2e215fa6c29320ade82de29cd98489e23e25e1ccff1137ccab5be7bedc3888873d9a7fa13a3ee24f6b1a143393ad252d2c8415e1146272ab2146e75ac50b4d742ae8dac8eb394877722ece7d34c41a406bd706ad02c0609e21550c9cc5947f674d6aa63d95eb1bdf537eb9fcc7d03295f4b42aa122e32db6ae75fdddf228a88c729a27f007e3b38c0ad9c9df186df2e515628f6237133fb742318bb49b13c253d0f8440ad97afc421cef579513abcc5c3299bf582ee54381c7dfac8c7b0fbb64185e24312c28cfcd016f7872042cc9e323f40217598dd7fbf7a38d8f3b94e266c92a2d9ff82099b2b2ccfe82d38b9a552b2d814c4da251bfda62ad426efaa1b15b204497d242b293507f7b33cc25491797ecb7efa8c4c966bc9a8312261c0b7d806c159024572209724e53918b6dff06b8a2fd153bab5e33b53f129f157b37e3d4ccf563832942a608cdd3e9895dbb26a04ddaa5de94e9be11c547b640035d6876f8aa6c76bf2fedc5d06647549001feab5363886459cfdbc11f8f4ece1e5452a0f8bf0daa6dc9a81d678585da611652ecf2a3cc0fb997fb95fd9d386326aeb2931c59812217d816d7690144ea48b2fd6907032ede8a9c08544157e306d5dc2e90bcd9082b9e482fcf078406fd3e16d9ca67d03b6297d5416fab36e4c1f3d91397496764453e81ebaf41b58c5f0b8a9c3f7e5b812193e338ff082851db36077f3776720881573950dc065413ff1af8d52fbee8f1f0bbc7252d28731628d15a66f9d88ed69e07f5c05f397782355407722dcaca6d0e60c2bd0fa27ccbccb1a65871cc93fabb4b37804456155f05d6c886c37476ce3a7b6474a51359e8b99bd2a30f9e4bb3328a91af77556efe76ba458d96ba689b332fdefde161f9dfb8b341a131a0c6577c3fcd746dafe4c35691dd234e200be3e7369aae47dacb2e8a45dfeed430eb525e2b0797a9137cf3aba1aca48853ba10385f42ceeb95e122ee34ed1d2470e90215058bf31243223bc8d399abf0fb39cc675f2d4e8e1078e1e5bf207e7d926694cc9d3a8cf5c3409e228ae582759af803fc3e29a6d3fac2601b42d2efbf31257064357ac39ca8381f77bdfe8ae8ebbed11cf638cc49e53261bcf3f2b81163d0f447e90994ae4adb702fda568b992288b2aa51fb4d37cf22745dfd5c3fec3125061715c853cd888c65641f2fba56ccf37369ed300e2cb6e17a864f12b5486fd22781285b5817ff2be496010f46a3b940d72d9444d96da723970176f740b81857e6aa00537204cffd5239e4e49778549aa8bb9b9ce50749c61369d6677108fc221fdad8ac2697b1fd4419e2a85681c85a7638bcf1e3c756616c5f3e73913738113583d8e1c92a67d6d6c61ec72385a4fa5b584d8c8ce847658b7502483db02023e362a03f128b8fc135bafeaedb6b5f2dfd40b9421c7f6ad07b4768f3b881fd904f346c360be1414227a003db59e93b9349e1e63eb279be817a569afbd0d8fad8141f383429553dacb4af99d088521a67f08451b0122aeb21655557b238286cd37c4c49e5eddb2230b9f6877b06a8582059f4efa6140b17154d36dcec92423f414a7d8b6efe22cef3aafc54ee11baef443514e9a23dd6a432b96005a24888cf66fa6605d25969e53cca615e07a7f6dc15b85cfe2107eaf886dd889355fef8a132ede7008cca21d15f9b4f0e154ff63af91bffcd8b5319c93eaa23a94b73ede4719220076d68c67c8af17cb914729af440d606296b0f7ee1654106a37a7ef3e9a5e8" + }, + { + "seed": "aa3bdc3263883b3309f0d9cfee62281c3999cd0f0e6d6e4574644e521b8068b4", + "pub": "441cd75aeeb6f30317c646b8704ed699000c27f7fd8c942dd443150599e2bff709ae386b834ff685961f328b9092f433d077a684fb67fbab4cc36b50a3eafb21ecdab55b19522d82afc735d06da2a151668997f8ec87b012d5ef67828f00278a434953ff3e6576f86f8faefcdf63468da0a9a2edeb069300db7e81e58c4d070ea64f39a11ad3bd78d0edb432292f6f7143d822a53657cf1d7fa0936b948e52244dd305a7096ef5a2527f748fa75f47817475b3af05cd440ad7d9398ea9a5b6df845e9fcdc1535626cf726c12e75a8f37289315c018392b95f604efba82778bc6248ccf22e4aba0aab9ca17e0cdbd81c0c5ddbfb59ba326af041fa49f0921cbfdbbf9f4cd5500ea8f6bbcd6c3dc58d8fedcb8e50c89f5e092e3ddb612216e660bccd6b6a53c30153b0e55051526b2fcbb862b1d28bc43cb63ca42ed6f7b6f1acd67f4296a32690a9fd5b851e75e9afd70c74ccfd80806c66a1a9baec7962572e42336626ec455d578d72fc34565adfaa8a3978c69804e820ff5fa9c70cc4a5aea8c6ade16041ab7d24a43a762202ec61f14d5b6831b25e7799543bcc6f38ea9e1e9ae2be9f0408396f5ddcc32d3cc7d35ba26c4820f51405e3dd11dce57b207fdb30c73b468a75d86bb1880811c4b6d3bd85d90cfbfa7880e9adc8ba252f9b41c9de9345b32d1429b9bf0b72a08e96302a2c6a337e70129d68078a30124d3a532554f5b8915449ef044e856e321c515fa3707a22f53a5d5ae201d8cbb90595150735ed15cfcb6812e269bdb118f93bcc09f07cbfbbbb7fc2cb855ce8e9760977ce97e10c6b1f68b2431be5d9b943b782619fc48f484463c3432c96a66a8ecce7a19f79e5c6e73911b558d6d643a68d8836e6f1140429dd9d8a24cc3c2f619d318779789aa93a3b1cb224545360f35300b0a3cfb102dbab697d6f26e29f89f18370892c809d88b54ebdbbc2e28594d65f91fba7b6f8e7ab4c040a0a34903b9d242f6d299c514baa1b313492d79ef2a677fb626e68569563f5cd53250b8388688d68b28a522e3203a168b378595fac564865f4c978995e63558871d992682756879b7181eab3f9074fa44506e1cdcdd7f53b66a3e92016b8aa769f094fd459b580514762524168ba2833bd1c2cc15648cec268d376d4238fccadb891dc20856e3b86591ac76250b19b02806a0520fa5ba9c529c6da3419d049c6149ff02107dffc88481cfe7c0be63dba9ea4dfee6a61abc2884b95ec05c8adf3c8eda8a2a3dc65fa8568039d014f9ccaf51980298b2d4efff741510f980775bd167e0857e875af349a4e58769676f8de1318d070b85808c568ea78bbeae2b47bfabc546649d057a30d374fe4e298c432a96b827fb3ecc3b9139cf8bf9ce0fa0380ac484709d009e60660fc848d458edbd356609c73fab090b02d3c2b3f1c9b5e1921fa8f793dec20e0977afd3622d5d79815eaf6489077da1f4ab660ac347b60a69576cd0fde6637390c641bf93666595fd8c24b20508d150284a28a3f6370b37e7daac0dc5af425ec85d2d22c3ffc8c780975d6d488c97e0543147a20fad2191c189fc71cbfadb570c2e16bf430d723208719161e1ecb26cf9524ab590788ae3a1d9398270a4fcfeaeb3a1d98ae7dbdb674c987e6efbee154a0276fe584091045a3115284e30c607ae9043630b543a04b9a1791a9a01292c45efe8dac82a3f06c71f1075951f372226b278f9766ba19b33cdf9c9eefe09954cfeafe38b34c03a09750f0a0b5a425b308d3cab887a56c3c4d5c02ce283e1c75dfa59bbb495aa8cb2e5f77795bf1c5633e2d2e3b79d298ef8895f85ff3ba399abd71fc9576cd855976e703bfa3bb1", + "priv": "441cd75aeeb6f30317c646b8704ed699000c27f7fd8c942dd443150599e2bff73a9e830fbc3962248234c8b83db4bf60478b48f1ccda1390dc9ce671f7184d8bb37016cf55e817f5209f3494deb3e7d235d9aeb02a80091aca4b516483b5492b561a74706a5da70efdea308e92b6156a478e73b72f628d40f2a1685ed118b8025ca201042525e4388c190729a0046201058094921094c001a1240904c88c0392418c948d0b320e13853018944921b6608230690b130612a7305008065a060c22b86840146a20170a82b48840046a003345643022d19249e31044002686c1806d24c70913246c44b000d2128a64389014288449824c54a6508cc62c1ab485001861111510d2b660c8c40424c485d800801ca90d5b18414a20094bc409d4244241b46c481660c912815c04251211720bc9240314884a226c83b610104400830269a4026882b240d3c0458884700149021b4552e3022e18044a188285ca104e93a06844c2090a446cd8c24d19c1081a28211cb03114104819a02c02127210226e63204d8492710b220210c421cc120e6328612320604844900b992121c931a3a08808830d88066d61c464cc32711a454e9b38880188700b28301a290e98a031a30288a18000cb3031092045d0008053806d60980822044a08321163021261a48119224d0b328913346902392604314e62186000256484921010238cc0282142a88da0c661a0c251d90620c1188cd0028519a42113b86909120662c8845b446e6244051c980d432010d2402443188e649261cc300e13b85024384ec23620e2c401a2c86091384dc846211c40210a0532c342051c34499212692210681b944894264de3004681288c43b08c00b8905b1468402245c2b22141248e89347104417092320d20c48184928452069009a025d1220984848c031766d0c46199468949c26104064e09c04d034604cab8890c262601334953024263100ac4346e00386d03850844424d44889122c5259a1070e1b87189b4216208284bc025d2343018a25109812cd2141209a881008291c8a868130505d2424408258024154803234242204e93800064342c8ab82909079249b40482108014250a1a4551c0423223c8855ab68042c0819c446892102624c80d98166954366124c590e416650ab16d6202485328325084291992858cb488dc26484c14705a40022333001bb3200024891c9750c482248a8430981224e21072434671c42885cb3212239808c4924c50022982362d283aa241878b6687550a11e78269f16aa6730caab4b34ba8b020845b8c589897ba009c589e978d7cd3f93a259501ea47f237d8d148449e06bf6fcf05deee1e2a9ba6325e44de536ed1eacb3796b8a79be666fcf34e4d788fb450f75d150dc561bbb69ee4b261fbc9367735d4b111b21651f8b2223c49bc6fdc7f576dbb52982b39da6c97dd19ee0ff5799524f340d0c98b38fc6a8ff89ce9374e2aed2eadaa1527a5350c16af3f2ce1a2b520a9e380926ffd325f3f49c743aaf2d3677ac8e58c652efcb3f560ee4b4552692d2f035ac4a8dc001785b41aa348ff8886c92f052748b295990a15268e6b0c46f6143f0395bb3cfab88cc1056626b0ca8f65a6c5918c81ac5f0129c77cccfe98705e52ce263f3526d782b8e04240fd777a3e06376ea9870bc023214cfcfd18dbd70fe3e3100c4d5a60d6d24aaddb53d37e8a589ca677a9b0ecfd377712f37144cde55cb6b0465d26ab14016d1c096e8926bf2d03ac4f444841d350230b43a34929f4478f2d9f475c407e310f421f7037bf3c32f9af202ab22bf58225ca9bf959529c3fde457beecd71debef8c7d7bf790fafeb26d4f00eaf8c45690d4484f81894ebc047288f842e864ae5dd717e63f30fe0f7e9099d78e20267eb9014eeec1aa9cbfa1c29a764e8857ddc046c472571c76a7180935e7bd1751a6da3a385c717de193dc618ab3d82d0a62a5d1c78ef9ce3c4c21d828b17a92153b082d55bf26fe8ee405ba25812a2059ef3e2d5b3c08e1e4e2254f6aa3d00a3adec52dbfe381c871be58e34f3e12daa2c770cd5f22d862941d561255cf9febe4278e9537e26bda5f98de5a05ba522d6b32b9a871206717471e9d356c7c217b79a9b74027a005e99ed497d8ced0c1d2be0f128b636fb48d62b94435912076a3a6af0661e988334a811bf16f14ec11c5d1ec97105995d75a6c749b7b60867b936f25eef0d9c625a23d68cb8d1cd4091eff1376ab2fd73ff2f378939a61ac86029537033a309b55503cfc71a37c9c8bb520d490558ef43e7ee2f9989e768a116a59bab433e40916dca3b1a006c656df3c35fcee2d63a72274b18e4d00be3df8d8862be8fcb5267ec96dec0e9b3d92197df5b7149dc4499ff5ecb98dad37d8a09beada0811eea509675f3ef31543a3c13be9605ba1a4589f958415b83e2caf252dbcec250b0fc161d9d375a0bc8a9683ca5f85c5e6f4d5aaa1b910b9967b3f8192985730958549247f343f0890dbdf70a3d3762d5bf0557e6ca8f742925934a4bbabb9bd8d27e16026518998ca7fa82ae87beef501ac3edc07a870691796d596f219ab681e875ab51d839a4505bafa83e2bed1dedd49fdb264d517529be32fcd715ef17d80ec8ae358279ed81b6f1183394740840a0b2ad2030557cf316de275d681e3337012effe5190ce21b9430db222edc13acd35f375a0ae77772ddb6f2ccd6787eaaf0c990f5ad8fae7e173bad8f245d5e8549dfb8c029fbbd9b7b5c2df77c2426ed4b5d60da0a5fae8e1d76751fa037d0553e1880220f8bcaa8f64a3f56680422b2a974f497e98afa400495e9732076a4ae8c98255f9275e42e208342d6f246138995d9ac7c79b228526a9ab27abafe27c6511208e590aa337a9a103b160b4f813088d181c4c5705a38aadcea33cd03c91031795d3aae64bd1941b4fea2ca9756c68aeec60fc29157ae079287d91228aad35dfef783f1f0023cd5f2802388d3fbfe90f7a55f4808a944588fec7d32d33ed61307187edfa11785550bdd865ae1d4f6e8fc2bed56c18c8d47d12ae938acae5ee97d3ba214e6f436ec572f91571c7992874407f19f905a73a215283ff9d757cefbbf830b133b56e894c8509c44dea4500d4edc91d1359a36b4993e1aa95be9e465ccceeb7f58f330168517ad399b4dd6333521c283df6af65671a68a3a9b05784fc3a407ab387781345b993e658624495b1e7fc6d0e3c2b729fa780c770dde12dc0b032dbcf82dd34bb505374e9f484d4c106d561f7571539549aad9c822db5864660b6104ee1b9da543b2d2ee6abb0b590f78669af49bdbe53935ff54cee30db72af7f92396f1220bb10ae1c4b0c5fb673e47f6556b4e7aea580ca110bf5cac5953f8880a50f4548f16962a833b46154603acbfdddffa7f31def536954efdeacc2a50507d4d842fc5423df11723b71178cb4cca490dd423d4568eb060945524b41a78e6fbf271480bca18c7fdb9405e91a42f99e94e9de5b8a79b96c3b8fa995bba5546e9605f85bc8a4fcccc846f14d58ee5d51295d9ea76d4600749333caac56362c9796c4bd07a8c666b10018a1a777e3bf30688bdc214941d86cd919a3a52e9a836a52e6beaa5" + }, + { + "seed": "7281243ae37e408a85fa3215fbef23c01d93c4cda21c9b8ac30b324f879278df", + "pub": "369c0d9ccc87e5d75f32ecfa5bb28d1808e90d2c424f217941fdceac4d45990ce226a21683f91056d0e91876e83e4cdb6e3bb0a4cfa3e6a9a362e027df322ae25817572035ac364e0094a05c56df4d4dd985813cc0dc1d867c2f977aa21c1a9b382de750887066b519eac94f6c781e2cebd92cf7bb01c2f18cdc53296b733ca3903590238fbdb5e12048707098ecbff93dfee306fc9a350ab1b57ef88267946ad21bf3dc007efe13b15b1f74a15b92314c52b5d2cf34018d137b99d6a77ce1eef6a32878ad1fea92121a24b7b13a56d01cd5b0566786aa53ba897dbc16f43520122af7adabd4c7a4bd7a5161ab01d18f8cd2bfbac06bb68919c8f08d77573754da925ea818f2443dead9aae3f3be8ddca266ee3417a9539024760ef5ed3441ea6e0bfdf4f56b747510361464a51b782e3daeda80addb8e964595f9814079f170e8b90fd67e6bf714a2b1e17060e0c810586aed3120c65bd1eb4a2e72f54d432bb1d2faa2e11252577ce450101a2a3cc642104ab6a7138826f6c18aee994c4215406ecdcd65d4d8b34477afef4bc5c3ccb63de054a68a141be12812a2225716a7ebe57a48eb911d54878d6509388364973e7e6341c82325440eae82690eacbc4a746a671cea6883d3112a6809876a22645092c2fdd0e3a13ebabb704f845c24fdd5841d2576e925524339a7503062bdb61f3389619d98c23def95e3eed8c1cba8afe3856adf912796eeeebf9764b40799decc2a108b58b9a3cb14c8f8d816828e97d6c2cd21481eefa60883145ca87c7666ac8cce3015c5f02cbcfe4360aea42f3712a8969bb4bb36600dd04502726a1e2e056c184358ba2fe7d0392ad02f65c9039616d02d14847b94277184ff36910fc6cdff0938025e8b6ee52ff03e164bca31f69396cd8de22c5f38b7b031c72bd4c08da4fbba3b731191c78bbb4e54ebfbab905e12896c3a2eef1ad74c54e45c5a14a803166bb945767e5ad8d8e734984acc2059d47899a7efc9b42df7cb8a525a12039b00de623082e10f665410231537bcda899a9e9c3ff34efa45664391dcd578a18ad410638c50f91e3021bd7408c469c03f7f9039a8e2c94aa1862a6759140e08a77b72f617c0d768a4d05c8895ae85369e1e5768f8e0cfe1a3e46197815c893d722a41e622015fdcddcde472963d49ae96c504eceda30999ef8ef6d969a850fe929303b46f7079163e967cca1ebba3a6538b7adea38d9f9853b1000059c037e79b15b2a1ec75f1615f42d945adb15a35b0dd51df037bb63514d5d8e2fa8fdd85c30cd428d29df0823bfd0027f5b382c7221013989ebaf24ef2a1b96a5da29c3add160a910aa20ee838e2a92540c0763d7557da32d7e36f7e757bacebad9e0a5c6e1f5ceec14609202d241f65d94b1b06c856fa0931a3d0a0cbde9a39332c3272aacb8ae73f5e72d519e1f18f51ecb7968d67bb644578d7a7dbb579cb41cb98a2fcf69bb0f4b42e791054af752c5902901a7bd70551d33af9ca0522588d545efa60f5a5ece363dcb5ba6fbe9060e47fd3da95f7aa07b10ade2e5b97d12e765f6a052a03477160220975d57e02a9072961badc5a94e752335329e9cb8b28f00a3bd9c47709e0561221246c713831f77f8c349588ca8826f023f5f7b3a839f5ded929623f290afc202cf5c52dd053694ab59ba267ff3e355363e6b22e40a250e964ceb5e869c36161ef1ea2f33668fba2a35879f6fe207b6c99f48a020f67df87c83cb44a5509f3cd4162bf18a8334ee923c4e1b396cac249077be939a16cc71114eca94f411e48ce334abcbd97f52837c61617874007fe852520a75f644015a504cb7aec3188732c1a26fd2b74bfbc", + "priv": "369c0d9ccc87e5d75f32ecfa5bb28d1808e90d2c424f217941fdceac4d45990c7f843beacff15c1822d9b2b6824dec81a230101c6fe668e8923c394b7f53912d852572db1453b969a6cf2fa57d5f39ec5430b2b5cb328fb2be3a7a431c2a376a3357d12aeecfedcf845ab60d3e6ec702c35873b2936f00a6ec2ec76e9f6b95d30b21844b828c10b210cc324e202204d03250224951a48491e196315a30104210401943860121515318909cc8281b24489c961120a388c3863084902844100c6126008a1405c4380198b62cd9326da2802c841082183462083146cb4888c3143241a0809a0482da266d44128de4200520b27152c844034689432249c4c04c4a424dc9104244406960c460c1c44919486621a62004c00d08023104384e8116718832481012690bb74809988984865182228d59c60564402ac84670a3482ed8b05010c37100230553068e2325215346241006901b8580c01289c4220cd9c62c80342118b10513205103474149484663106e0229909cc46cd0a06c119769124569938825c01272889881cc204e584824c2883000a925998601d0443292384e0a9124a0164a02a8315b088dc93401501665d2482962242614184aa196058b4886042270448665d2b6684004464c022d4a8240033029202741939491db328213250e9c120de2080912260ee2b080dcc861a1c040033961803411c8c2851ab35148004dd0144c900684882284a222695922500416018148111a088921b080948249110225023692a0323009b60c023185003730a1082d54326ee0383181420a49828c1ac700039528dca24d04b48941940c21a161223186802484100791094286e082050a8110943652ca940850306e0380315930040aa56101842cc336829802464ca24dd4940c6206641a217262a2290c088864346023284881b40dd3c664984229a0006a82425180120801850cd12206d9042c63028d22936d1c4851049388c4822408c168040188c9405023c46503294e22120102182883a24902297021978463207224472123210e0812241b4530e4420a24430d1b848961185214350004a00592c09011167248b02c59265212182810058d03c271903061641069cb28090814408c9488193412989004d142720930841a35028cc268880286a2828518c7905c92684414810b2986208305109161c48284140761209701dab60d11444458008c8c3024204922138868e0848420990ce210519aa2480aa65061260563442c0103611d9516c41ab99b91656c201da81367a60db93d042b8dc93fc34fd7b7cc32941278a352f59d78ad6909b5ed50661985207f5ec31bec64ad46a105e31741dcd901ea8c8d695f7fd9459a3bdc4836d993675126e05cf9dc82e0999510d27a11b8cde2398c3f3b6f730373dd0cfea39a1c107a5904b38c50614287a3ff6c837eabd71f489e76756752f4ad9e62e8437dde8e70a5dbf3e6cd88df7b01a41628745c99b66ddb12e2461c6041e219c617dc39bfd6caea7556451308ffadeb28c8c3f5eb43f6f95c56742de1847d7efb513b5b2d9a014ab4c5589bebbe57733b828706c9d3a1b5b58a3b280a2d350d559631da8441655d61bdf10f0cf10271cdd9a92bfcc8504b7758461b8199966019f0d38fc2aeb3f1b88b22609c1fd693a9f383f742bfcf66d13ed26e6f9020b20bab66ca5fa9d49496f3c9ac017a1974f8d66969a6bcc5624934f917a17f8c115ee01653f1d856d86f998f4329c3e65da2953edd904ee89fd5177fcdf7d397d12f62e192cc769fe37a1efca4f453df26726d8ae7bcb0001ed56d4f2935752e571258d0020c8c4d742afa802d6efd039a8349bfbe91850d50cc96f687f548b3cbf94bf664116a12bcce04fac2ee6c44cf47e5420cd31dcd52bd9de4717fba584810e39a2887eec163f81f48fa9cd927f65516ba7fa8c188fd6915e383a81e3cccf9590281a5ef62beef96393d53d9643aae13955522e377f9506e0f11bdece484679f65fccd7e538f78696f391eb0caff97c9a42025d52f0350a9473c3c8dcd8d9a81bd25175ddf72cffc35f8c55dfc864f2f34299309148206afad79992324d4617d8ed7e28df1ab927f24237c966dda94d4915dfb25e7d2a5475b2804240852b230b5d60f26aeb722efe39e93a5f00ba95f2d5724c51356a0ddbd188b14fdca17f958d8bd47d214808b5dede4b91da79d489b3bf32c5c21f81f66e1046a88e90ff278bd72d184aa47ca23c90f31d9d5ed7e739fc02c62f162307e46a1fc9694bd9300c7209043df135c0fc7989a8c4f59fcbbbbd98f7c81bbaabd41bcfa8d5b937dbed7fdebdcea3534045fa4ceaaf091400691b35587a3844dcbd73b7435670cd1805ceee0909ee69a47ab2a08efd2544399f5957ba41505bdf69dde4213349e5f14866022ecb88d9b9df3b724587b924744cb7c126dab2613b16ab76967effa8208c1f10b123bb1dac7bb3656ac129e5ef50fcb3f374c0ab436168e5c636c6674c87179449c40533bf4774818b2bfecdc92db403e765a93fe340d5d71175460d67696e3cfe15e00ea72e5cd3e3ab77207f0aaf769c95ac6664904bf1027777217a9586aa12dabbb62ba4b4d5b8910cc78f7e0a1a49c025629e301ee46e50b1e3f5cff59702a288bfad92f4a0b4356db15089cc75de8c29fbabec6061ce0352edd8f44599a6be82e53cf2a581cf5e6d3cc1cd37a7128087f26c64fb38bebfa1943f5af74b94313b914288198eda934c80d0f8494a9ee51781c86e3aae09eb61e461daa0d26d70e14ee2a339a65f01c4d74de633922cc51350e19bfae401950a8e439c6385ac3b6ad4314d905a7cc6e65c775bbd594519331c5633f4e4bc4ea78e2fb0a4ed08af87515b1561a22d40db1be0576b85508ed0d362724f1aa161e1c9f11fc57dce78354e094d7a57eed25539198965703072c7589f88352fd9e20ef4a5bd420534f5b08a30e73144b293ce93034e4f2ffc909b036a85dd3842981432cc8b0f9a2ff6dcf81451c7c01ae120d2b05262e558ac449d26a2ab27d767114067943dd1b5f709870a4b846d4da72ff169c6160da5de1bf51cc4b16b1cb2641067cdd9779f283326dd060b1da9998616d898830965c46af9db26777b87bf39f7c09e6f24d9980111e19b497299c8e32f9f3a12d0ffde23e003faa7009f0a86b119070ff9987fa06e04b557417b67dedf5b03d41efe8c37de023e5edbf3e9515c0ee3492709f575c5e9c35a5550c01e20c371104539a5c3879928badbb4fecda05592b9767070e56c211ad1587c595654738f6a2daca2a101a4e654af5e107fbc9176e462f9601d8b8e6541fa2807323d470d771818d9f0e85aa5cbe734b71b1d04e4f3d95ebb72d8c8964dd357a0676f9e5da9f432ac61dbfe1c051a56fe8d50624f9ff46007935b22db908cd18be33c192f64f316f4a3adb438cb85c3c7f03609a69ee6512e10e8bd97ac978260e7eca9617c8a5316877c1b7792891e20da55a93bef621129973033e21feafa742899cabc86db320a31444670c0bf1e34f6b80923c4de05c6f304913c03b129b0e9d0c187f5092b059d591324f675350075f56f34fa4c0a77a3925576a494b927f74671d2ab8" + }, + { + "seed": "2364ec1c9100926a264bea5ae8110c38a7b727dee2a886ae2b429d78a46e21bb", + "pub": "dfd2974dbf8336bdcd77f988f5e598a1cd246c3449e0faac6e9f5567ffd216b791fe2035758cf5f65a2ed353d8aac58bcd96d0ad6a5288fdad24046989379d4625056b3b1960a198d005f77096f34799ceac84cfc65627376ff9d0018bee8ee16c6feeea01c7028935aa9dccc343b85f7d4d3e7d73e2211d6edfeb20572910f59f663622202d3f55862e969b263da037d7259514ade7f6be27fac0df5b20fdb28ac536e22bac2b60a36c58ac1743e96cb3e0d97a05509147413a40feea3212d77178653838c88863788d08be9b12b53e15e78e425bac4d08b162da08c31e87c65b775c308da2070fd9a181a83c0520d1f2c05502144295894d7a00f1beb6243c3e119448fcba17bcfdc89bed4ff0a0b37cbc508b7fe0e99288b99588a339d7954574b6c65e2b4254d10d549b2fc9969799b3891d14b7cdb802914788744f3dd2626009bc2dcc5421491ffbc1c57329d59d1fe7e5e34c67c4fd7ef91dafe24c18fe56504ebd1e0052a13f2b9ea14ba55ed73f2d662dd31cf0c67795f56e5150a896431cf1bbb04fc19dc967dcfe361af8f2d14a5a0be95631512b5777e9da80ac91039089d04555c87bd4e3ab0f8a4d5f7a72993ffd16625b457286af6c6dc5ad172fc8eda6985d2c0e3cf3ff5c69eff8d862414835ff3d23a420c49faa5db4c1dff884bd7df82a37d680e609a2ac44f7e773c6108895b8cebe12b4ed89071bca0c4049907ae92610db9bd42e41282a52a68c7004c8cf9eb514574616574d7ea7745c87e26b1fa18f15337d13613dd72957c033608b1579fa67c7cbd561e99d62a799827768eb536847ab97412aa3760e28df577b77525032df710a70014d369837a4b536066c200bc458ee06ab1ec222f8557f152bca1af20dde19f5642ceaa0f583ca7f464668f940fe75c29f4504e639bd550f35be51a08e3d0d55faa89ebe0e87308cc6109d3a5be4ba7cfe89c8866f806a262aaa60d12016f1e2d158931ab215760453543836875f3cf96fa80e410da1b991ee9c59a3f5a5305f5fb6813be4fe53f2c1fb487f814ea45ca388d51ef9868830135bdda1b860740aafc15b8a27ff05a83af6af4377fb11728900ebcf3c4129fd3eb43aa4fa968cafcc6c60b00e53b3c908d70a2624f1326ca1b83d21eada254a23444990a91f1324515913d4737b12f659418132e3df667024c4083728fff3e76a83801adcaceeb9bb03f8862e5318c13ef9fa3f21b173fc71b13b2de21faadac95bed821ebccff8701e45b323504d31a162a2997d46cbe676c8ab74c3c832b0e2b86ddba51b68865ccf171a5fb56749b317be4ab447e62b8769a63620d91419ee2b27c9102ce19558289b724fb62d15cf61111efd3cebe388876778dc7eee85cfa2356f47d6342201e303fe6b60f1c20d456fc64696d141e996c361adc190739b9979826e220e92312e1a42bf32103a5490b1cff191340c0f3c6dfa414a29cb0fca6165645921d07f63c3eb6064cbafb1222e8f773aac392189822fa81608d350cbd0ca33bb36c1368931dc81d6e410ed983f05186ca379ea355122f495ed70a2cf13bfb8266d1a614d84f69f3d55c172fe161eea713e42130e3dd06a141225923ff5074bf250b7c37ef9fccfe56809f21dc35b42d80bbee1e791aefd1ebeffd33e642670c19fcd7ae449138fb52c6495592d4756a1a7332978d95100149069165a4e3a2e6b004d738216604b6dda0d8622bf1f4e008ebe4c18e1216410cf70cb1814e2b8b7b0bce33475218d6c1fa51d7e57e50a637e6b0a2445f37598aeb860ebf0f19c5f335c71965b4e0d2a659c2c9207c39df1956ce5ba8732deb694df9962331f08cc9d295d322075", + "priv": "dfd2974dbf8336bdcd77f988f5e598a1cd246c3449e0faac6e9f5567ffd216b7921af9e633839156e4c53d6b0e84b71c6de7e0266931b3f10326263f74c8eb026991c77539a275fd8b612f929f40a4cd0b26d45b48592b186a8af5a116c35f30613f9f637866438e1feab9f26ba9cb5699edab2371d5883e46633b9c00c4d29c18a685181762cbb44444426d12c809183701490260d4420243062d54108c63200e13a8891442904a16810015700ba88563b820d414259b482813099023b161d010295212016110320a1166e0b4091217908208121aa88d92c069c144405c328694b28414032511378200446d98186562963064963162380198325082c40c11217011276113483184366ca1b8080449120c36021cc46d5a96844220919a362a092689cb380d0885050a154e640285a38249001489548220d3868c59361252406a0c06654886205c16018030695b8285c0000d5b2621049291229789c024220a262c63482919900893c404e3428909856c190268db14801934891ba10d0b406e1a3590a0064ce012829380055ba64dd2b050dc22815ba6481819011b4329d9b46c14b469d0c22450342d42a044234931012792a1b0481b27924018110344415c248162b88d084846e1381119406c084049c3025193c020dba48cc1a0401408650c908598022892140202b4201a874c0c09465990491002400a268c22b0659a149064146c11a73104341249444a9c1291a104028cc87151160e140980dc883162286ee30862108460939860d39280838288144270d3944cd0446008078a120290cb040d810460e42012613285a2b6419b1665a23892c41270cc9610004240d286059446665428868312328ba4109b98305c880d23975009a7295a343103a16d23b22413060d143120a4a82d1b89600309721238295b02660cb90903b9801bb86d22c990da2629db040a042942d3b631c32485d0b0301b3509220064a3460d10a5684a282dd8406e81a80c1ac30563464e200849d3328413968d12c270cb086acc36091bc808a41892903212dc4822c24242a2088980c2281a124819807081102d5448660b037108a38922164c53300e44c231539021224188e030028c9801a3180100254e62124622251221426422b34c11442009a909130325139349122425a0826cc0b42142107212c109980005da36640cc64c98088509806502267054b6714c8830c206688b880da2206c6342605c246621324ae1288493308a6230320ac251a3087294cdbadf1dedc042174ae5fe4823776b52bf8bd2b3f4a007de28b3fa1a9c3bb8b6961fefd9163d024fdaa8a700b34f32da2a8d72e64b09bc1c1d37101c9813d9fbcfad2154d37689de21ed3fab1051463affebe040a3d0587e9c62c8b628ecc317ad53970a56a4d9436d15c15c47c2b1c1477830cab461c3ba2631c0521b848304deeac12349052fffdd44171769d41ff8237c2622eae633cf3a36f2b563a8fda59fcca4292d4c2aed72259f6cf33b89a0843e6ed703ced3391301aa879e86ec920371e2413095a8e2ce6f2792e01bad7e4014f3c4396e2ac88c0525f56eaecec05ed5455afe0bc8430d344c4799fcb580687ab848edaa42bb301700e2d10e592aacdc5f2b871fef42b7958dbd391104ba3b9bb33e5b23647338f5d3795c25ee532ba638bc4ba71448aefbae32c7db33cab9c3a20020775800d8775d0885edbfb03149cbd6deabadec00644f2109f373bdca74fecf77465e2be41570199ff65bec45f5973f367245fb51a50e0a551074777230f12a13fb694cdb31925b3bf33a62811b26eca50ab64dcff5b9db599430fca5eef60357cfd7b15239437c308c90ce08ba3da25b95c4f1e72f07e36746e3598272269a259a8c4c78ce848e39204b6656e1ad5538e4285ac23a8c2fce93e5b8557ed8a7d15e991f675534cd0a3eb1e52da1482a5e214c22a995a60734e7f493cec48f53e192468cf80964c017b037f7488d7fc135a5c65200cb797e94dbc791a38a94db9d938ae56faa25231a634aacea8da1be0e78c105f2d6897aee15b6fbd21ec013c69bd215dae73f9e242fdefc69bf9124dcbebef1b1b9eba499e9cca131712cf2750dea81bb2861c038889314a33bf0c9b1feb29c3b66bec8c9427f310f8f22ed9d2cdc2dfda34d2e4f44ba661b7c9da0b7e65fc6fdae97c8daf6c91bbf1d28bf6e8a02de261cf9572631b1ee941732c54df6d8adee1b82bd788bb608b5bd63c2435fbc3a2c7b7123e3cb6e63acc1cbcb80372cc4044dd59a73d570cf1f6ae011ade02bb59151dc08e7ee4deb1d839d41cfd1a75f02efeb9b99df88e4fc4b9977caf95a0101660e23f5ec501aa1fdb0c7ca61ae9931d1588b4353e684f047661c5e20c549e902bcdc4dea9631434978b352321e9afdae50057401efa7fbbe23468c3077c64afe2dc27f725e167caf3fadbcd31a1a0f0b59d1cac0ca2969fca6537699de9a05648694224ff1b118f22d9b9cfd24cdab13dd20f7e1936c484e9c9fdc3662cb8748dfcea001a50e6ad8e460fe34a1ad31093f8182dbd9ef0537bb6ed2101c2ebd90e5f4d7bf10fc65f6a5af886acd262eac207317701585ee358661251aee01414ef3977fb1e97e24d095480764ecb783255eecd1588c76300d9fa8987047e63bc2d9e96525b7cd0242742b50ca014fdcd1d0a238eedd91b4e698ec5fa54fe16b90371803ae3e6eaa759c23e52190e9ff1156580a2835672380492c2a058161b972e78c1d3261e79bd0273db1ed90a694b8a26599203cd2c633e15461fb25fef3ead91083006f070e84f598142acb5fb7af3233352147747b60c113fa0fbced5a732f2aaa572393fa14516b0d30fccf1e8d69dea38821a7d71276ca349677b14467fbb736b7cb30463c643ad62b140c6ae6f06a94ec45c71d7cb5d13bc3a10020af35fe1923deeeb5b98464b96664bab7a6aa71ce53ca05205e3530a5c159b9cd21519e642510bee23d265e291c62d39795d838c07667d37de0bbb72a649fe83e64583d2f5640cd5dd699f8cd5e74924cdea58f1ea2173b832057f304b03a859f2ffda51735bd525b26db07549c74ddc354564b6340a8f08fd2af97e8898586c24641bfd3459d5a9384d3d9d50f34d66367272dc4659eb1cccc1730e73237a0b85ab6a3c70be7b02cfd917c005b0cf4a9310aaa8e5d3578f39929f7ff07cfde274cf9af90378e959db1c6dab767785cb87da4dae0b882884e6d8fffdb3caef8a5ce421e25b82a438042eea8f6aa714e0495ad00c698dc2a865fafe4e0c5069d25304fd06e7507dd3acd08614642cf5ccae52557fcfa77069d81798d6dc2272ed00e766f802d62fb2b194f26036a0390d40614cda0dd3a32878a0ef49ac08e8e2409da902dbf79d2564b330739e4d286c0f15778b6bd152088fb3b8ed1fcea0d6cfdf0c0f2d61c30c73497655132c8ddaa9ccf3ad5ef15ed166d4c068c7e016572ab5cf315a368d029ef8d55655658bcb9ff93c784bba2f99431334d21143049bb72495d58814b6ecd46be0955a64ab8dc1b2f003741faf16511490e44a005dfd0e5937b4362f69a668f4298ebd1c1d5ab4ef3e15d9b00f4020860b04fc947be833364fd" + }, + { + "seed": "c20fb09de53a48d6d0829faad50d62eebabcfe8b5b63fe9ea919c5ae78426c21", + "pub": "2026a06b4cb341851ca99f57409b1f9dc65395dd1f994d71a0fbdeeb387b7570bbd415bd802533b3a2866367a2a4ed2c54185363f43c3cc51f2a589953802dadfc11de53b393800bdb6a08854ede56f9b7fcf9be32d5439ceb6ce06565551fac47b63de0e71f6ae78b1cd875c0736cfd81ca619fc8f9770ba18061f9c432b507add2a7ea359aa6c8719ef40c8bbf3ed0c1f0705b9fccb6af791fab8e544b11001aac57979c6ac8ae4f935e10f2ba32811a0f80ce6afe464f512727fb5c2f58e0fdb328368088cd94f8caa32ae1a32323ed0dbe813058131bbf85667110b58575279ef8137190b5084210a42810bc7a829225123ff50d0d032cc2de748ceaf86c0b0ad797b368e6ac26de6835270e8b9a60f648e3d1e658af8fbb09622ed1c03ecb72dad6c9d9be39210c5f16083a7445a452960eff8ceb87eef49b06b9cfc130f21653b08f83421a7fcd8b41f4295187179d93aab17148a7e8c5bf1268804440b5cb05c381c0dcb986e264894e17921e3719b0609f39ad83fda5f540bcfc6024940764d40e2e125bf61510b83e3a363cc5f4e75b77debf3b2c56d0ddcb8ad50f149fdd4b1a3a764b71a9d69c3df77c34e881ab2d1726a6b4de3e02da13bd661e3291613f3f0dbabb8fdd98c3b853c5ed31f5bac6b2988d2f3c70ac0879bbcc82ca6810bc60564c85058ec2d16de1d6bc72de9f1bf4f68f6a5339220b2bbfeee73d66a8ca67e8ddc35673af60db490b01cd92b1da6f3f2e040bfd65f020451463c5ad12007a6cc2e8e41543a3d193acc5f4d1073e152da76926061884b903da45447fd000dea2e7d5d95a35a7834491cded0df8e3b18044430e6ec82aea23efe6aa6068840612e44f77926905d9508e0e298596d740fbc8ea3b0c6de4499729be9bdea7b29d200f872eb2b646880b3204cd5d800b728dba9822b16c3723a1e6bfe0e0425318388b4aa82d3e8f8004ce98924d42b8b10d5af391988b1899279ad244534ef949c35e721fc32217509faeba18602f5611bc0f305e8566c9b1ede3be5ad9adc5fae0ddf4137ab164a2bc2aed4a3004af9b1e8917401711921ccecec8f08523f259cf59a4b15963cf63a442fdb1c19093cd718073d00b46e98576688b9991eed45f04f9554050938baf9efcf7575c5adf894efd3f8621ce78272d39551e2ffb1a4ee1f08ee7c575f68f0f9e4f34495953c146bdd01936006acf8408fd372634e787784aca4f33b3709135cc7e4b85b7a45dcf034fcc87d3a1b846b6438146885a1e0a5bde2f5e2a4f88e3525dd27ba49fa0fc95112fa160e181a0a0938ab14da27281fe49fc439dd515a30b9d8f6e13159c46ca24c16c7150ad6624bb11c1ed440ba5a2626fde76fcf4ab71b90e0466c06b2d125e08a1b56d734a15d13c6c555ab57faa608b5fd2948229ea2e7f457875f8e90357f996699eaa7d21d3412cfcf99c9f55501264a7364678e8088962c1413afd5af47da8d449088eba76af5acb57ca27951dea5b1b288d69b79d16633f69784bae98041f3161a4b3e5d4d8ce2f7b247eb5e51681ab9efa19affa4b368da0d493c33b71a2ca8cf73a61a218f48e530416950b944085c8959b938e7436044b9230eb5a3254d4322cffd60bb950b7f9ea6679502057bc1f88cf624a936450f6619d7424c3b4e9b760a987ce29e85b8f394bf875e300a116232f772bdd1e53b10fac7f576a97746d7e654b05c8fd9e37243cb356a15d7d3dbe4fe9c8d7aefce8378bbbb92e4f3831df9a2ba0997149d10fc27e4f3fcc74bd45ab77ba7bf12b39b199d79a4bc9e05d47116e1725620b0053c7c2cdaf120cde871c41ace21d185f8cc11aabb6a70204639d72ef", + "priv": "2026a06b4cb341851ca99f57409b1f9dc65395dd1f994d71a0fbdeeb387b75705eb488b5c6049fd1d29e8e980982c8c03981d1dffbf190bb377de0aa6df490e1958923ec87ae5c17bde39574169a3030d0bf3088d0b81420891f3fc2ba4da846d5319fd93c39d09a726ea607cbfa8fa688bfa8cb67fff3d28e18712242ea00d1912285e3b830113805e4288a63a6011b10824246414a244e0b262492422a0041720433200c9004da064a50026a08a820c33000c9928858c80940b60402a77148c60008176610422180086c21a9441818821b18080cb56580c60914222dc0301111a12c59220548c61014a680404604803428c3200119270991b6254804301b45894a421211432ed0408401a10c88860d142290198305c08490d9c6691a0608c8048948926d53b22cc41208039405ca942920274140300e0cc03099069240b22d8196491a1571a1888dd004401191041cc40412220954b8080025101916661c2900c1c425123760419048981840923401c1b41050344453442101258a92162aa1247141882820c8240185290041209b008558246ca136011b31685bb68540124812480a2408664bc88d448205181448e22406091360d3c6450b31424a98709c1631c8b4911403098cb04d9a045163262c0c1372010428cc4830e0884419356ad2a60893000281b0841c9084101890800486d3386224b068912222a0101043b204000272044426d4c20451488c0c3130c2224e1b908419056a2301221bc124a1022119818899340ac4a60c00053011c13012206d5c240c88300a0c370e23378119c62521a64011316d53008ac4220dd3a0205948114022450c392201211288a66961206a44b829098105984871a1104e03b1404ba48582b665628289c3400a18c86dc136880a37494c362d1b300910a7910b2226222362028170182141a01868a412311a074a8b06905b166523126ac0282e10808d19c229a442609ca08d5ba848d8a41142306249a4090b11402048649a44850c125142408e14924dd2382822c12480328e41984d110872e3b20d6490850116425888049cb60991804503924951066400248600896d4b862c5b268e5840251a826c14a011131980da32680a140054a2255c260c0233041022499cc821db16819ab808d89848904249da12700b422403132e9b1642d02621da042653a60c0bc600934629a0240622900080382558320189264484007150940c0c410919091019052c13999140b44c4336211c072d85a410c5e8fe076829394bd74c77c20fb70564ac016b635a7391c0a7095d730c82b0270f500c3181411a11ce30043cbaa6dfd7a8de2d6a5a93096c19babcf25c2de297b9b0ce13f0de1c867b1f45c2576b09e9cbc7d172ba245cd797487891d0cd55cddcdadfac311f95c78e465c6b7ddd70356af3f73ba73db07224f80038ffabd5865fda4f822cb45dd503c17db5d0610be05479335ec4c23ece5213fde8b2cd62c866a92374f57274d692adcb455035f6fe87f31826502ea74ab82824fe97cc67ad512e2bd1a039bfbcb0c4be810b137a58bb582d04961818a60d850f99eb38630227b779714742b89ac9f39fc362d6352b64fc17ebf292a4a8436abaa21d8ee7a0baf37a2e26a3ae7182aa59a6901529b06280f0fc504bb864066b92aa485ae1dd94aa06c81afb05365547604bb72c0d3d21602f5f0c283e29339fd129339ebb5b87abe7d6310c8aaaf7b34b94084eacc2706ed860bf0932da8084e959a4aabaf352e18c25e3cdbe8fb23754c9c0eb9981d839ff35c53196b81b80ae969de58ac7a0fca36a27e6b083f38b582715c566809dbd53672a49c08123994a4166a8f8ffe92883f0cd943b7c36fe54cd92fa6cd2575a55887805792cfcfa3c2c0d1abfdb5a02acb68bdf2e5958b3724c6cd9bc630e1fc8d51316ac7904b303e7eb1e819118993557079ccfaef0abf2803dfaaf0766f0c45374bc6ed6475ea8b91c08d89e0f4452aa500739e3a2229ec282f96ed6972c5143729ff36447daebcc9ad87238fc6d32ab7b7dbe027fe8f59c79eb5e2120dd4d5be7c3a413cb3bf339ec2edd74951902ca68f40e54a97b983103077a4d312b78244462d2ac675abe4e4ae67edf9c637d50d35bae58b8c0cdff2c8c97f4d8c78c7cee2c6c969ccec23cf6a0867cd821d1a4dd6545c715aaab46399b9869956da6fe0afe0a90af9cc6b2b7f3322f38944e3385c96dbf851fcbb4dafde6a649c4dd2980f2b2e07e2b91f2a50a6d0512fbe0a21d24da3ebc3332a70a327ed84ceeef3a84b148ecb67612964106b4d76eb6d4782714b696571d1814bf8253fe26c4a6ac4f8c977d48f18eee97b326f50a53c3b6d8eae1d37771ae4e2f8a62799c44f18db324e324552e2d41dd67b7078182c8ddbbb9f8d4cb3d6d711362e374ad48121d9aabd09756cf9f38436037ae91179ae288183752c65dff8774695d2ed67b4c5f598a0dfaafe860252fc424aa0bd0c997e6e5daf8610d137c521d75eca5d6ba82243d9a5e6d2c1fdf2674a4c61a03368f70243001ec5f97929a7dc669dd08e8272104246feb1b773fc4a7c16f3e74c649b931243d2b080a90368178f999a28f4f606c1749eaeef3320ca91f4f6049454747847da0b59e43b7cd1b156ae3a61b86a2cc5c5bfaadad30554460fbd8f0a6fbec2ce07a1fdd621de3a01f48af8e512bbe1e02403d54c4fdaaf9e64d173de655a53390f13bcec423899362370f1aae1f182a555f9292569c9ee54c18f9d786d41a68ed94d2c2f53f4edeaa1e8b8fbd3eb5faab48ca1e848f713ee7855753c437ed2ea0aaedb30ea9e904bad1dc618b4bcdb3a5c12c856846f950541037212045b8c06160e51f5ffd759e5f4ac4218e9b645d91894334e9f11b1037226e378cef117a18ca99604d29f3aa35be58f015d99b7c4a6a7c975c730ea5f0abc212176d96551310adc8807012536a5d766f7b1869ef4d67f1b03327d6107a13395827d3e361db67d2d7c9a51e06467c4cc915ef0b005ecae73b2c249d2cb7fbe55486e7542a34d781ed5a0e3f857fbb9eadee17cebbd03e28c86e561ba6f040af8ca094d072ef0eeaa4f705c49698a368cd2f1782efd5ba74052585dc4e8125649fbcafc76b79da9637b5a78b6982ce583348c920b045b3bc3268d2880c0e9e14eed9e147b14e56b2f27f0a8ea1a0e314cd89c2bfdf532b446748d04f3e0483a5771faf9337fd12cf6eb7a5a2cda2c5d6f380673598ba52a0c720bf0e40890091e4e2abae8ca55e9940613429cb27a9e03e3347594b679627ea8052fcd8eff8403e031c46dfc19ab176fc1a9cf77e985fcf7943e6103f45e87f0fbb0910b1a28523e36835a8e6dcce9e6b4e3689cb5480bcd34c55b518776db7ac11e2c3adae92ee578be59eea300b858ff0d293a4a555f29befa378b6f4f96f3dbb5bf79495361087c18c57e3460b9d108eb43517b1e694f9230b068f6f4ff2c5a605ba8d6f16b10e599ecec8ee3cf562d1dcff4535d5193928979c8d8ca719429534ff90b4a790f26847922ff9babcbfb1b78d4f1859a053fc9f96733791a53c80d2da3aac2b1eec0121d6921afbae3b7a978ca8184cc9fc3303aed6c8e35e3d3b35" + }, + { + "seed": "dcfbcf948e428c050997b712564ac092e927161cbaf6207c7357b798a98f9053", + "pub": "119fdac84acf828332c9ac840d2cee7a94104aa6080ab485441666e9a7828bf3d2529794c64f209e96653207145261de851a89ddaf0c075c9ed137a7ac46b3ae531f0e5384e296d0cb69ebfb26c0ca07fcdbb2d2178840be223b1128833ad0bdbe464e7882ad41b288ce553e28e2cbc078de78ebfd6e4e11523a31fb1323875b12c5f7d2c6f34f7325ea9214c6107a35b7ab94654eda101144eda5bf1ad6d18c011c5025b56b468e13ede987636a2cf02106239daba0cb3be202dc748e2168f718da235407172f065c2438dac719e1a0ffe8cdd6d2eb2e2fb8b2a979c0afbbc0dbc14fe38400cce683e25b40710163fa8c41bf3d7665ca86253c44602b9c0f69e9301531943498547e21d88133ba8d7da421235c22bdbcb6fbfab28f1abc42fef7bbfee3d0a8384fa4087e23e5dab799ae057d8810abe9e1b9606353c91d9ea6eca649a02e23a7d456ba915a8ff1fbd8e106d48161aba8689f179c73840998d17080ad2b564e459f998c11aa5d37a93042d351c11a0dd4a41c8a7bbf1ecba0b47afad8a8f975e36374d2436cdf6dc08ab2903b40510727e1969b300a2cfb796507ea25a8e6b33a7b869954329dea44d3de65903afd6fc9e042d81bc147de707e99c6ba52c37c058f9ae1ea0c76b2304442edcc8922b0a65f964da55bd54857ba09a129cc50f3cb95d7614d9aa7273e37a7d87d9d8dda1f8aa84146f5b4e0351e3c3d9901e50ca0e65b4f67c8b0370dd370d744d3bf3bd3f8309b29a67c3841d9ca609d4d45552901f6a16c50d3364171ae7203965936f0037e9d8a12079604424247903056e8762643d86738b50c348da557627c635c0ae5f282a46c04daebdb66899e22d7bc40cc42d3c5ce7d02c7025666fc950a70662ab01364bf71bc9f45d360f9059b90a6d7d876fdb83976cc9d9dafa9de77ad701944321fdc9c7d7977cbd44b3f7818d2b2c9183914beee22d0abd1518d369202f83207ea9d274e159adaff04bd8f223c6d77957b7f9c3ac0702aecb8c16ae836f4cf43d55d8a0d776dae993161fe0fe62514b172821efdf5c804b260eabc657c891de0d6c80f4e09e48e62e80930b600cc8af77583bb16dafdac66eaef0c91cd370db29ea86325c98fc019d03094234cbd53e809844686cb11bffd28d66f5d358fd1076907fee9f999e8e4afb1a639c88ac8418e6d24fd9f1de8ff7d23256520f411b74ec77f4f3c16465eab779614c1058fb5eb7834457e7a10e4981f0c731235cfa7100805ae5d609962eeef4652a85bedb56fa0069107a3b8467b6b96bccda20a6fad71a2057b3d32ccc3a4ab4d8786d7cf5adaf45b3acca8ed5c230d100314fe2e405db78462963a37e2700e6f6c7292539c58df0ed36735ee2a67e215073293c427dd84199b24e24daeeab0e89deb8a179c9740502adcf45fcff42d327acefbbebe3bf613320d455bdb1d95aac066ff96bffe6642674d970cd0e2c6972f351dc8688a95e27e3e34fef40787477cc9b2f986886c1eda45c5c695832a15e1885926cb139d55168ad29984a00bdb989e69e11fa4013acf328b0f89d565c66adab4319dc17e43f2e2328d9c0854ac11e6067cc206b309eeed5c12b0d2fc2a3dc3de857b2ac4fb96f554cf97f1f97287b72f118ad8d3f96ee321c37c9ae617972b7584178924e67a2a0f05819cbda7e100c5366678768a29d21153d2d8f707ef7920f5ce8c8b7b78700b9aa4d87345e8e13a0ffd39acd51d508f30f3d1d710675624b19a65a03c00b21dcbcec6b3be2a81f9c654001d7e085ed7be1ffd0891f859ffaffac7a631c5d0cafe98d8aea1ba0e8d6f5a1350bd00574404466d1457b9f93ed257d367a65cc3", + "priv": "119fdac84acf828332c9ac840d2cee7a94104aa6080ab485441666e9a7828bf32701fba9a530fe070df5c4be4a07d0ccfb1f1acc157082166c977e831b5ea713dd906ee739a506031f463456cc9af229c9f93f28afddfe04c6e0aed9023d51bc08b44290d3d3775134bb6b1b820aa8e217927430b0a83607449afeef5e17ec9fc9b47113c8058b40880c934449c66d12236c4294291406605b208c23c98094c8810b428162484c43a06da2084e1893309a0010d9847101060012989019b16d8298014c120e600429c2a06d219964e328281440849a00861ba90012251083368a21124198a87010376e20920c2401600a39715b40201c23860c822151900dd1b0608c082290802419174c62364012208608c1408c920d22420e81049294344e04868562266582308810034522332e64808123002a0a4700d898095186441ba36119036cdb8480e2c8289290411ba591580472a3c20c98148de2186450206584c20860920164b630488201a3805004809024c7109a966da4b65142b224d1a84062409004966ddc464a19c4011c2382cb0668029911100480c2226009963011c800d920491cb1454a2611ca040e20c0880a340511932183a28199a02c22174e1ba5495b480011286ccb86211403400ab89113c680da9665d4902823838d1b1928d3828c1c300c8b34520b2140c80068232085c40285d0245293c06892180a08b5600bb38020c40cdc0824118205dc448c013865da38321bc20d12364042820590049003a50c0382244b16001a2641a114421325910131884326819c0049e2300054a8655c1244d4b4840a808484b0004c342a913029a096240a122a4a340609456e0193710c1164883280c14682dc006222014101422091444a03132901a685da82880843820b360481941120b50ccc0465e4c28159b6246210060bc33180a09049c444d9842808b584c280610a3144d40886990261dac885234340c8140e190066233205913261581422d8924c63100d58c62000478811988024906013132e8a1266611230d1008911c00518a731604061cc30252228121b264e09c220ca9865d3c48509a5405bc4841206068330210c434d203529503492221145580400239611a44820123405e238514b066c9b301203952c1ba66122196184c80504b80cccb830d1927082107021b16da3486a0939611191001ab40d94a4250821004c326d90142e201632c11288e1804503269024a46404338a1a856583249114b64101216a623d2c6bd9f37b757f061e3cfd7dcef757f2e7a45b21acbcfc13f6d976bb3b2af088dc37c776c944bd714285265b43e0b803236fff47f41eaac6cd0c5e141d6303db26a4f814edd55df1ea2b242bbfe6b2cfba1256eea1b5033afdafd7214834afd43cb7c7d4cfe2bc7801200d1704f5499ae38491a2396b0e35fd552355341c53118b4e115cf0d0cecab72574ed192a4ca1856224a9cae6c453c3a7151da831a69637158f155d3cb7ad0fb3d812871e407158a987cdbcb789acd4885332e285dff9e45a05dbebdeeeb0032ee9e72ef9ae3ede34b6bbedf9ca55a6c0e87eca4e66caf28c5dc19b2a4d9fe2d74e14abea2aa8920931ccb367e00375b9cd9f4312dc565b7e156a54552ff3c8b77ba83165769c2c29468b1fcfab649d3047d1e727a9604ad6217fdd2d6408fe6aa5565b55ece248cf369503303a935c9c37c508dddeb6b5378a8a95f4db557b1562ea16f1e79b1ba682510c4adb65220800a87de71e101c4b3654a52f50839fc02ec9b422cd5d8f553d2c65d4b06710b7d6ddcb7a3a5383dc575eaa0b48210f041ae23752af4dbd9ac595d6624464cbbc2bc888c06f9143906cf93894dfe2864d16db6c6aa9937c4e87c40de1bf4e639ce27d637d21ec9cf452c6a8bddd9f0b9bbc2cec1f80e7e6b8e0bf5ed0f82a95b0b6eb8faf4583734237a506b7981e269cdf97a6ee91b209fa7cb53c4aae4d140b9b58aeed1e4ffbc6f8282f6f0f63c34bee0affaffa1edb14a8f6d9a75123051f4b4d6434b5c0b0cf3f3bebc4dea7376ee8cd45daa311e1cf1f4e6078472e542d93dbe986d3fb182c1b67fd913e2eaa64f02dce1ab1a64145781922bbeaeff9e0815899dabcc9c2a4af263b0bdb26578eec6318909d685138fa78a80ce6f5c7df64c1ec24b5ae07390711ff5b3fb92f591a29bf2b28529757f6e0fd34e52d7842b2437d9acf7f07aa62192482233af509f16776c7dd0849bf2a32eb917eec35a9c037164777a38a214f813d67383534ba1096b9511dac61f33b17da4371212b4b9397546a55ce32b3701d43389025c1dc8777a6104959a9c95a06fc3505641eff2028f0916960c3b9e4ee4e5a369f413864c8c0049e98ca5a36f4d8d49eeada5f8cdfca0409208fb2f56c9ee772a7fe7090cac245abf2717dc5656da97d9edfd90a39d3f88a5b4eb604dc2c9e1e01df68a3c3664dd29360c8bc8dc30f7d7c9650782d996b9e80505f75ec8f38dfab8f513ba22a6ba77d783d20cd452c37731f7eef184639974ece0203b04575fdddf4e27e3286fdc85061889543a1c36e0a06815c8cd715c725ced5ed21e986666f9e123679ef3900a2445ce360a7dd58665c21ddc18e6ffb6afbac617fe1d7c10fa3a8d01917d3571f77bf07f2affda086e4faeb229a11ff3ca3be788b9eea5c2e31a9a634f0f06cbf0b14cf41b3f53d5399a332a0ef0016bb28f9130722ed52dc90d127f4197f5d5e7ab22f2bffd3ad4d48265b30ae2abd4cfdbf60b71cbeebea0efbd5f61ddcea1ea836e08f5019e934ac7df9eee81180d9d4172a2838296d71e9e62b37957e96fc6f2071f6714129b989c423217484bdc9bfd906c4eca171df450f5ba53c357e6df10ee74aa627970bd69ff0eb213f24d14a5fa9de99443e08ac68a78b93c48dfd53a1c7065ab01462218efcc210a6dc7bff8f171e20f3008be1463667e63fbdde47c3e9a5ab6698962d494fbc554bd14fd55937c26ff7ba0d54300ade2286126cc32601ec6c5a37057cad22cd105455c1b68280d639591a941bc4a4246365eca2109e10ed1870d50b21f1692d215b79c05a01c1ec073084e42f6a7b42cae174b501d6e8d37ddf55631d9f6cf5e7fe974d8fc145673c41ff991ecf571fc741c6a3d77ee4b01bf66aca240ae84d96d88d241a1a8029fb4c1a7134dd018f2dba7e607840810cc2044743530a3850fb6736e73acc8b598415eeb8665b12d89971d3f9c611688892a3a8576c7d16678051fc099cd7185cc7c8072e6a289a52b8305e9481d66d5ac752b5a736a21cb8d45a80425aaa5796e7a1faad7771b1a2a219e71b3b8331b5d68a7285c97dce41d53230c59ae4322c63cc3f13e8d171bbc26bbed3c05e09245a706954ec7f71b5dcdaf6461442a552ccca667056465a169fe74a6af24e77525642a9ba5f978aefefb825f5c0c52bbd8516ee1d5fe103e432d4e4c362110d4a5c22ba1771a2ac3167d0de90dc2261aba06484f78d405e4ca14963c3ca52193abcfe253b8ca0609e700f24c87b5687a562b1724c60128b968f5f5b2f82811469d5bae3b8cd2eda6b90164699476933859538d99bec39a8a4a2258d61a3e23129d5477124c2ecc63d08c5" + }, + { + "seed": "0ecb2d990d29d47ba6992cdce04cc510d6e69f051ae783a01b3163b7fae624b1", + "pub": "ef1d38e549cde7be0a196571a05001987cdd91e24ae270b41556cf1fbcc476d976979199d8b5a06a2eb9a46b95b5efd23cce72e272ca76911805957b216e02f43f750e098e12c0b1454769f433f3e229c09b35de6e34e3de158e734a14e0c101b8713601d1d227fd4e6e3ef0638cdfef84b0982ef3d2cf04658bfa52fa7155b2b490a413252f4fe4782b4f7a1df983cd912377f7861f6d078069ce07d8ead93870006835832674ec624132c3c4800008f26130c5dbffb6dd2dbe45cf32d1af5506e21733b1dc516a84af97b33c35764dbd3a3dc64112dce9cecb539a5fb560b411e7526879df5c280ff5aae4acc8654766517243a095b8a0afb72aa5577580c460e1c432dc5d117b9b400791108ef78359df2ad4047d4e012ee26bf76a0b0cebea23b252b6535a13c40c2a5d0a4a04413484b24c13a583bfc93894f0f07ea461ad4ed693605419cc7ad31b42d5f9d3395ab5999b02c896bc48ef1f22f853f1730bbebfeb0cc564007154593b5ab0ec9e81913f29098042e47371e41d6bbb2290af162ee08626dc7abeaeb7a634dae68895f8d8c20b8763d587f47968bbcfd3e28a58a80275ef6dd7561d28927d98f9984859eb1660dc584d2e1820d3c3264a0a50a0b7d0b15e2295f83d149325c5e1d51234e12514a3a0a353d5bc2dac4ac9e9ff8b38caba710aa2c4c177baed021f6a9cec9918d466925033fc09d286fdf78cc64fdf7b6d06b6cdcfbc773c136476619ddc77c0592f0c7fdb2a9c6d733520c6b417b690e97197ae6be58a3e20dd449ee7ce44a6f0c948cf9c665e35e08f3e9ca5fc46523e1a070cf98480d0406581e3be16d3ddbffbedc7916ec4c35e7f655c8761ca0d2718b5c32d76fd84f6dd3254b4b6ac18cddfcf07ab551278f981947aa9a05461f8addd00697d61d535a5c934ff9f86172aa0349052eba67d5fbaf0385399bdc6a8f6e95179e115a61e65407093b0a8e63938d772896146f0b3830d12eea22bf6ca1aa5fccdd9fcf73187564c2a96453d6277e8ad7a35cce93498ca6722ceb532ac4e564ced41b7bc891e7b1de3b32d0df8f96286d8994c883fd5d515270c9544851fcc43aaac2fba1fc3dac97dd231d0a35475261f64e8dd6a261cdcaee519f59d4c8e9784db11ad966f331bc35bcf2a79443c128fa6e52241ea44e46c24ee556e60526e0fab75b007550ed71a6bdcb649a1c780181ebf7e58c2f7eb8e9eeaf20cd461a3e20b676301eb08302c9b3aacc2196921bd451795c09bc579efc18b088fd2d2b2b20cc6ddc9e9c85ac863141f7c1a360ffc7603ea2d9ca1f5f5e20db34494f3c20477072f0f65922cfa0580c72472b28e4f7dc1c97ad949e9abd45da4a006a3abfdc990a5a060e45dfb50cebb8d98672c4aff7fbea13feffbca4ea5023f1447e2c1cc46b864b75cfc2bf3fc5ebe408397287a565d3cc746963d9e5e09f78d8da1fad877bdc6da6565933ed7950ddcd147ad9d7aae99b7516e4d55601af7892373415bb68ab3a461c1c4565869b07d8b06f81146ae685372e983cbf8e8886d30b231015d25a8ce970acf1a3ac855542b227741835d769abc6e5c290fbb0103ff1f17ce789101d7cb9d3037baaf4d839a584f7abc0f4b7fa79d798c13d799582fa151e72c655a26a2efd9da52776513c24a6e8b8cbc2fc6589b2eb668177f149ac70e246b9e61e02938c35cb5ec1589c17ea27b598ce70938486d9297944a2aea7087acb09f0ecb94d9b5b90251b0b7363a537494b2f4cd08442537f89c79383305d570776bdca36d5fc886e75674ce8783c62e0ad4c8cd575854fb6dc01f5bfb10bb72117e8f8370f0fb6b781c41e7d6c8e34ef651faf414f6", + "priv": "ef1d38e549cde7be0a196571a05001987cdd91e24ae270b41556cf1fbcc476d93b4411b33aa8efa9387310ecdcda0fd189749c27853dd11781103f8457081168cc791fd5bc4290df3dc45b434cff48ffda2be882d4c6b349a1ac02cb8ec51712e124f5b8b46beaffc26fac4ccaed6f919ddb07b4217bb2a1af343f2c00170352893846532045ca00041a42818b2468ca32844a828894a404da20710010095ac2451ba10d08428d9c200de20425a3128640926592b08cc9c0251ca9292218726138284b342ac0b40412915050222c18c024e0a44d48368a61c42d13320ec3082c92806d0b192993a6401c89245814421aa2608246891a440813890443442940162861a60c08228022a041002462d0b630c3247199c240a416248b4409522866980680c11462504866e236444b406e0cc16414a581d23492420269c190611242024ba210c946690aa5210b47441c05724a28011c0410c1402a59882c1c394a0a05444b182589b20942362d221448c9b40424c14412972c8bb22d030604e2c084d2a00508352902920852a471c4a205500212db9681088368ca442292385214344920204494a091dc9605d23845a1024640168018c44cdba68da0869042a48064048591c80191220a19b929d830251c106d88263113490e8b988de12468da920143280119452a0422299c3465c4086a8418298b34725a368e8110618c322d24b16814360ae3a289d2000a028110d0000a08384243440124980c59303243445242188d91024a58124ee4924cc2421213c9085a380a04b870d4168edb94854ac069d1046ca0164e493849a4441003282e99086a43b8111a21511ac56509486de0c845d2c42008810c083586c0462984848911b6900a472148066ecc84089c84289cc28cc8408d01c4111b8620193445e34806e1888454304119b9091c1650cc3271cb2051e0c40da3926d1c1345e3b020dc248c51986d1c238501856c09466209a02810386813186223040e90122122348cc330088486109bb0658c861043320404894998c4619182045c064d9b368d92b84848c4094cc02c53084e4a0404a0362ca4146944c42d8a801094906194945002120818218541168299424160866c12496221298e04014401176540b4900a038d143468cc8270e422818492251ba42dd9b4206406255848704a04511bb86c22114d2014918c92880a8609d340645c2088094331d3102ec1480604032d4cb00450224924482d8a26009c0061040485e2a48530148057e2a20c1be1d8a21b4b0f991a4e630aaa596ef202f4508bb34277d5bde52690dff5eb2253b088829d2b98d7f164f500e018b45e6129fe994ba611242dd8cf8b28595e4bb24e3274fcb7cd4f771de8e4b5fc36c0f26274abca7fbfb9a561b8c4076f21aa31307e4f40327b83d4d3f530516965b972def034fd3a2881ed6c50c49171662c6a649115c86f0f9ac445601ebc98801480cc6b79a3f6226690d0588b54002e3e86653e003bc77388b2e43568cc3a76a78181c3806711fefec1f3179e605f15d9b6fa8e49fe93ea68685ada9bf657ce1340d231202b627509bf468f64eb4ab3a0937f001c4d12c4980f2b35aca31f1e253eca2ddac1cf69c91db940183afcc98219bb9b7b2af6d0065e8274cac3e04b9869bcdcc1366c76989a0c1b177918bf6123d10c787a527b1416555bd07892a5e052cc83b91c770cf4f0b4ca94c2f47ab3d1dc3a2eed208f7253fc46428cb728618afcc1137114be4d524848b7c3636bcee764986a3d872303f715dc38eb4bffab92dfa2ea62670b073e8dc155c057a421bc86a5a1301081c348df17bdc3b78122c691362434c79bd24d45e52c9a25b972023230cd0b779028d73f11077f0f38c44b041db1e7cbc5b4787cd97a98073b57b785e60ca2b531ae9d84f5e9837ad4d61b95d9f1e4e01ca65c778279f0faac1034b49c0424bd7b9dceb6a87fe8cbffe97032b59f16a821230eb2a9cfb4139eace0341d330e82c4b90d68bbd8d425e3cc463ea77577580a655fc89e394f26652388a1f7dd3e440127964a75582086a88f5b0add29fe589318cd935860b60f13044317fd52ad13a912dc5acb5d71a5a9ae622eecc7d970e295496e8f0f5f403fbb9198efe003279c964310592587f2d28d94f47d33776efe3740c31e814acd9484115cfc5e943d9609b882eaf8c3670059b1a41fd13e816d9fc05a6abf7d632f15be8ab4309a1636431dae4e99baf93bb7c570dd2a30307677039f24391c9b308e386245342ffe559d5b2eb484bcf4b93d2315eec9feccdbac817c4ab391e5e6bf5f1901c743c405361e94761cb25e21585827a891bed73551898251f6437c469656558001a5e14987f19ad20d196b92b6ed66493f25653edf6759c5d4fba2023261b86055116b9f460259365eb3f6904dd07909a967fe36fe2cd3190204578c4b3679abc33d096808d9105ccedff99dde3bd573f2662796664b247b6e86fd8410fff9332217691b9155128c33652f7a6cf743bb0ab50eb788cca19595f71de3cecc1e0785273dad36fc41a8bbc990ca533e077bd5b2692a690ad97690e13a67091deca3cf4808055355ad24e42605e39ab65188200384cab0259ee0657736249dea551925dc288179bfeaf532280a649d4cbf028b0ea4b44772123d8cae78d4e35aad24c1bec579c7ed8016c2100728395a1f989f8083552bdead81405cfd64ce0e847a768522fad74b739121a0fa5311c030c944de270b19c6fb009ab42d5dbb63d045903f8041c78e5c5fb81ac0a43adba14ed07cd0045c9df1fb5dead3cce937316cec2f43d71d597de2bb750df96754b9d53fdd0bca21247ca3e369e32a8ef986fca78f702cafac81993bed92f2e48727b49488fe7f91ba4dea397261319629c7eb8203752a0d884b908302881c4ce38295e700595480f87b52921ece5ce79e0696860d07e53e4595b241a9a2f2844452d75d7eb88271cdaa6121072ce19032036b8dd17bfbdc4816af74e5a863d474d5a868a701967de963f94356ed6eaaef05def1644876da76f8efa0552b6cd455821c2cc821c053441ebabb070ad88925e9910f8e028d7c1451f3ad86ed16800c0a2ebbe0033d31709f6cc9f83a4328992ab69656945ee6d1338c84682183441571bdf1e782bd85fc215f71610277d577ed70ce494e72306a479c61184ac52a822845054c1aeabf1026efbe5b3018b7ac9a587c8fc2e59ea86af90da4aec0ba8a27565fc4a64320829d122e789980a41473332d4a006d67ba37ddf2881b7692321c9f446bc30db87364c245745249a46877dd0896bcb433d07410234b1433a8f8bb56ec9d0119ba30ac7d76f1fc47989a2c9c3d9c7491092fea1ddcac5684929eaca0ec9deead335c6b1203274517fd50150d8e4057e38cbfd071c92723e6ceff7fd59f9f4d31ddcef3dd5b77300080bb278ba576bdf3868b18e1426b2a9ba26d5885a416c7168a66bb09692dbdf4ac7596e050793701baefbf8d356c07ca1036550ccbf1a49162e27197b7781ac555a9df2a167479a62abe3ee19d032fceda508d13694345c8bd772dccb0fc01010e62a067e3e28a14161d973b7ec124171702bc85fe810320e3ab" + }, + { + "seed": "3dfae184b19c5e7b44bf7dc59903f480cf46042d0f279b9ad48934024f16dccb", + "pub": "389f174343c9a7e98d47638a97d7e9771706007efe4d87ae002bf802e51e79a5d75f0f18f14359c66098173ea32cc5d54e470253a1d8cd0203459e15245167c7ccf2679f5e3374f842856ae11a68b09b5afc5c4e2bb310d68a734181645f85eb217cdcecc3b149bcbd27d5df7ae8bd8dbc9ac01543f7d3111dded409f1aac53968c781be497497f0d9a50dcab76158ebdcda232421194a9a81f63ebf6ac406e5d59df7de4edb391ffd0d45e117351f26576702e55061559c9445bec4a17706ed9de17b5879b32cece5e4c23d2a221a2db42f5dcbc7e1057fb97f9369b9b97210347b175d16342882b89ad6ccae4062a068e37eee891ebcd745f77e09ea942c62e1832ba850c20a59bc1c7482112717437da8ce9a0ce48105bea8f2ec816577dc1f88566c764e1eda9b155525056026e56db3410ed1c6e24294e0e10df5ed05a3d5614b1fdb76c386575ad6b94f9d5f61d5b37a445f2f21cbb10db212573b47e32c634496506672c3564daa7efa53297be70ce01c653fbdf977672da8e6544248a7aec0d07e19e0744788867ef85928879904472239fa4cc9aa11d068bfaf455a8accf7852b42d83244db916e0ea3cedf366b675f1b047f41dfe5fabce944bd2f63ad714fe2ed9513235962e0f071119b90f9c2714571542184f83120ace05106bdfaf7359882c74c287a430f5fe15cccae2c52a0a6aedd0dd4d97aebe1bda6277ad4ab6272c1ca4d1de004d6f5a4bf6cace14bf56e5b64d54f57f2f3df07b10f5d032b57b023a9c31cf07077ff075eb7199b5aa8b277ed62113141cf0d9baabb0adcbdcc362ad0ba269eb73f2b50afafb8dc188f52572288ebe367eb64cabd5a049b39673fb89c1f47112d35f15e53665abee143161d7d09f02b6d0611981700dfc800ba8b61bea8fa30727b2ce5a229fef68ac63f01e1092d2dbbb220a20757bc8a4717818f5b43d0003d1e824b44943c97875f2477073f090f2585dd3e5f23db756f46ccdf0e7e1267d13a3320f10dbe7b8c07441c021d84b400512db41c286e268126a6be93e6b4ec47b80e604d4355f2c32bf8885dfb24d9033bf71b9a00422ec6ed356dac04f59db8f31aba54c00082372298248cde2d73576d247b5c5793f09f3f68f2c142f89a1dc8c58a7db91d82b00098331591eed39f30ea82c5a81d95b82cb2a9272ae629708a74a3896058acee21bfb7f9707e5488ea69119f36c0a1f5ae3df33c341a41ad1dc9e88c17c5664ac5c773fc6084880cfc9286b61ee51a870a4aaf0f70bb3f6eceb633c1181926b28fd2affbb06ea90b9e2ddd6ac48d2a25868a6c0b8cdeda8d35d505ee3b0b852d4f06bdf32f16d1d8e44e29ee3c2a51433d550aa0dc88b1336d2ba287a610898ed2c5a92d4b183cd395e84593087f96260f913748cb6e536cfabb38543502f65b6770830fc2577fe4ba876c6b9c875b4e2e95a52c3cb1aa00436630760663c69ea2e437d49f13a6625573d6ed93de1cd05379595ce209759f497fec00c2b94fd9b1948c3bbd89031dad8fb265002c41a81c3949f023edfdf620f33590e2af4ee4fcfa4f9dc5c4c196d41233bde6c4ad002974ec097783a80937f00204b5159b6d7a7ce79a0e926e21cd43befe57c8d42b6f008015a8ba410146b097a63c081eb3537957a32cc048f7b57c44194f8f80d6580f52a369b9a4a0e4e6ca03e67dce3eb45f404739a7065e30cb97eb0d98b60f43209d1d22ac69d889afad6af630f0471e15bb123c9f205e3f373a55ee188a54109d648ebd4824d6e88f01ca4ce4d7e0b12037a251dc0343ef99db3b04e389f48416eb36ecedcac389c2edcfe6ef3e4159ce7a2cdffbd822bde7cedb87", + "priv": "389f174343c9a7e98d47638a97d7e9771706007efe4d87ae002bf802e51e79a54fc92d4c67243d9501b5d5bb3bacc367c973dae95bb621c178bc46efa8d0142469b9ae65edd2c3cc65495d8133a4a0b5176c927c2aba8637e8505f51f0c809eadbabb4ab2e537bf717418860691d5eb547da7b1adac22ee68224ff937639289742940dd4b8490249711c36729c348603171194406902410de3162ee3167112345022a40808b4686140319c482960a04840320e6334320bc370148789d3142da4280cdc844d61b0416214025c100998908418856889b405a3107012b48408c869c1964c88442c8b040a5ab42c9b86811a98405a148ee2188c1a31322206681b060a2313665c126d5308124a4620e0162084a40cd2280ed2042803068d01c56d5212409ca2811c032a02466812a7051847051a85258882419c22691c428514159164440c41140020964dc2107283388408b34d8c40011b0069a0b82188c04d03c62d5046529c226a20484013c78021b771d8242e11c761e3324c542289c9468ee1182994c891e448480827925288859c0282d1900901056dd12012ca340a9108108b329080224824247048c0690b32268304300ca0654ab66da0941064c669d2829003852020062821934c038161e0068223218d59a22550280560244148a42d08138d1cc6118b2828d1404581088920332a51960d82348a5bb8711b304a94046e13068663340c1a96850b260a021246120424d88845a4469111286059b269a446850c927014b04c209511028061c9166c9c34500cb3840a907118004d1aa965c8246882144ed1166662029019328ad9206812062c4a927151241000096e934440a3408661b4500136510c0944131885091948114005dac0911407080b0329cb882409c389d914061c1830c9022cd196059b9884143470522826c22029a0a61118224a49060112406dcb006003138a603492010604e1348149b67091c4650b838d033500019520cca041104321daa64c1cc285c3322a1a152a00984560424d229441039660cb9665541209a410640a0061d4102e84b0050104849928060cc08810170aca462ce2244ae412459a2801a3b208c9302919b32802b068da128800276e59328ae3067062445189166d1216019180682047311bc609448661cc408a0003461941218a86841ac86c89440912078a0b830c820602913029dbc04c11304801236a92966021946991348290346410197141986421b94c044661042591a4a625d6d1acb4b67eecd1bbe15a6dd5c82d5051685006ede6fedc225b94ceacb71633eeac8bd1b050f49ded46deb127b34174fa6f3850a4270f1c2aa165492864be6b12766c5fd216c216160f3f8d69691d8b837c338bc178be83f7d741952f066d8c8a38c19981d8b4b7fdfcb10f649aab588fc88316130ff2d5c50192bd3ea358d8a6c18585ba8471b21c962badc204113e87eec05b3942791a16c5871702e74f00705447278e7ba346e83df28b1591c5a9d4bb01244c26ae513da0a055d81721b7e66bc14a26930436fcba8d22536a13bf4ee84836b1f5b0b7eb7da9aac5fce0800b2b57abe742063be76496f3a31e9239c15f6a447f7893f3ea018fbb415c5e3f1540b631f2a8cb16ea2aa46dff00e64a9f411f8ba4de1f67a0072ffa843004ee00e02f1dc9ea5f28540b31fa95ebff4f42daf3d006c2c91d16d581db050eea461968bcf59e4e781af4c68a181c4ae60a954e9f2779dbfe4a03acb93f68ad2c5680d09bf4141a6bbca65b12f8f9b5641bbc73f56d8fe849cd6898d94ef712747118b406e00f534f60841ff5b0ee16b42b2484b85c73ff80b6bc197a93c8f843873f407c3d5b2855efdd0fc49add2911afb9836272b885873f38eddb3adb336744b379789bbfce9f9d6b52c91b2368ad134009f35ec8c15401e05c6286b4f662e2e460197ef80ac05e67be6824036b0eadc0b1f0657cd384c50f73dbcae14b30389f5c03343ecaf97c1ea438ed713fb116106c589321e21ea24280342339483aa0959fa49384a30ff7d81a6b59cf71311156e5da28b69d51181bdcf955d3a85d84d2734d7cd4ef825db8b3bbc4b7367df28e950a27b94b10c192d51e90a6cd5b3d6ef7dc7f310d671699842d75646c8477edfed05ecfd3a20fe56fa786ddc1c1f0b5ccbf1486057b3bbf4fced7114e67994d44386842fde37fc19ddacdffd8b67356a954eced5c4f7c77d1728eb60b6ff66b61c038177c1f992d65e7abced6bbf5bafb8571de86b18fe7181d688729d4a03549e199ffdb85062320a19fa829bcefd6639be5e43e8b60a7b7fbb3a7fa7cf57f66c3f973b4066da6f0dfa5dd8fb4af125998d5f808938aab11b24561c9e042981d8235dbbca51b4a130e7b9f3c60e5d1edc5c191b1f6407025dbe1146f804bd9ea9270b0b6d4b9dce4dfac9c42d9c75e744f2ea8432c69b35346988d9e625aa4ca3fd20c72aa98a59be5309e99418984fef2df4ed9b54e78887e4505ae0e6f286d24668c765a30325368511f6d756ff37a0152d48d942d3ae32f2f6ef18003d2666be3375adc7e3ced8782b71535da716234f9d074e6d6d49c17f22dd9ee1e1cf3911ce64777147ce2ea4dd0962295ebd49b0f4a57aeec5826146d58398ce55b1f5804901df60ab6ebfaabc612933ba5e40ce4824430616b68e47baa9ffddcebcdd03a8400487c75ec1a1b1e507ba5484f5e4bd347f03d8e57acf5004b5333788087ae22d6868643b4803a99243214754c8cce8a2f0c014c52d2000ce26a08c62c6fc5b45f0ae45b6d3b431f83ea38eb47d34ea0f4b666e8abacfc1b5e8f5759dc5c93694ce33fd498ffa0a505b30394fbb9f953737608e8defdc8369d000cbe1c0912bb0243c42e4b7d48b36d15357334d3b456714d0082798e03758c8ca8177ff0763f80f57639eda9b05fe3d6d36ba427223895f49c46c12334b1ef1f324ededf3e5c413d6e1628c0d481f86809a5e8f32bc7e5da7ee88afdfd1d59e510a99ac70d75ae633abe1979ce17f24b8f3726c9cdc7c09d4d9f1059cb8e657b026f18eb04130282922501b2255c874fdf19299a44d00d8e83a6cac9a126f6ff04e4cdf5674e6e2b040248d1afcc8d97db4bbf062d080230aaeb8fc1ddd9f86d2069ff6cf8a1eafe919cdf6d1ae66479cf50bd08f6941475d2a819ee1f58e972324d6730f9b908259035bb89316814cf45aef0099f2a9b21c77dba9c71b3badc6ab5e50a379d2383df56d6ba855245462b1148761ad09eaa29ea4546d7d91852fa28439bac4dccc6b07f361ee0d1e2d6b8542717a7bd43377931b82020a8166d99c5eca6278404fc68c3c1397b146176ef15a5440684b436f6738e987d2df052285a1381af09d5f4adf14872a53cfb11c8d168be37deb859bd6db65c593f67ddf2d737ecb53d8c8250719621c19090afb70505e2becc2ffcf3ffb0b3b56b909390b7ee6655a155a4f49f24692bb02e1c259301add2314b9b3cc07ed0ba4a90ae9ec2be69d7405f8775df8d040ae8c0f656420a6a2e83ac4fce515cf27e0a5b5d0c1e76cd1d08eab7e8cc390ce32ae17be28c365599e51c45d1d9927ad8ffc3d3f6a800ef0ba91f42adce90cccc64b46686" + }, + { + "seed": "821229e3226bb7a7774bfc3c3593da9c4de76255374cc4b13f9f62860e0c3e06", + "pub": "d56225f685bff9271174e8b1e87c7d5717848a0a5dc1b7a463d55027a7e0c0c6ab64771db20d5302deab324cf9a45172d5143b85ae0628535c404fd1d18b20dc9e8c3203b846574c28dc138fd54f3971f64c2dca154bb1760ca34153474dfe15ed30d653c508669f88f77d25eaa27d3e5bea76d86468e317a874fb245b4d8aa645f3fc3f84ca741be38bd25ca82d17e0c522cb6afdd1f4ccc5aed8ce7b8dddad205ea450699e82c31486cf8c629dbd50bf5c22c9e0e6d46135f6afd5500b4b6494fca1446f8fa796314fef71cf0f3537ed4da25cfaa1b8d7d04780f0073d2fc8e93aa779ac849d5fab2959589d988e82c675dfa440713ec87854fdb20b58290e798305e337629d9c1a73a950dd23fca16482a35b672ec6b3b6699023f26e9fa5d10bc5c42ea491a52fabc3504fca7d3e6368360b894c89b9fdef1df96714d2c467f634a01426973f6185409087c3fb650aa5543de1e7682088f492b0b34f86708914b5ed033632bcf832322d34f9aeb402bd2ee5d8ee860e1544355bb080a5a344fb9e19543d5e10448b997d1e5a0f63b264439dfdcb2203cdc66f96302769b7ea237a078aa6c735a0f72675d74022d0bc66c3620ea92f90d012f611940c5c9b2b2adaacd2211e11343a1301ed9071aa561c129565627d60c538fef847aaa434b13034d91179d66ab75a6d5651535fd8a9c76a4f40fe24c1950cd74b40d14ba7f3c30b71610f3d04e3e4829e8109cd322e43eb0ed9c34ab8975b310f11c87aced092ecc183f8e4544a6e762832afecbcedce21fb0a61f899dbc0c5de2b9f682d43129a23ecd9f09b7296ed785575ee607050e498e33235199b45d96635c0cc7beea6dbe9deac915785106a0c0a4bc8bdb1fa2a04a893abcfc9bb544f21f86f643346d446fda8d6684689c412ab537615ee70afffc070c3cb90d5991cee41c0c8e032d45be3cd241009b6e27f9ece9ab8804e263f3c558b7952b2554e352f310765a907106e6b85b81125e8ca13d5e1c2f80c252793be931fd84b6c74e1b647636d085ecb2a85be8abcaed676fbf61da7eb189659b11594464d9d675cbd1c1a4a5668aeb2e5d467effed9abb72aa41e1b775fc8e0681043c7e44b0de5297fa7c6d45b44a7fd93e082545712b1a0ba3b2f859652fa16649c18005f43662d81b7917231d85105179305eba5748cfcbca8f75aa9c9577913eed325221dea1683790629c991034b91781f10a8d95033b77ee30155e66b6ab913105a534393151988a8f34cacd2d675b93203a1cc24f1dfb3db70e03bed92b6b388989b5086e233ba8587f27fbc497433ba77a083c462203898cbffc5686b8e7f331ab1cba43fb2f4f2caeb1af101d2d5aea49bd843a9531bafad4d7ad563860c2ac2f57bc48da216dac140f28302debc7f7c8d508a04d5d380a5ece965d1a4c2abc6435082f503310fd0388c9a35e96335bdd5e7b02d58633a889ef2731e248cc0f8b5b022cd671a77b055e3c012417a45d48987553101703b81012cf131fe1fd686790569579dfeef9773e58e554bb898cd171b41cc0d593f0a0bf370555d3d84a04d99069ba964f5c5b09599a6fa175e449f5b39a9a42c9480ef7d6cfaca68a67eeab49cede9478e5f3fa48d9d4bca0b1153e903584148ad1114ad22da94da5aedd63b526f6559cd4fc73f95ae1c79a8f4fca429dc0c5d0bf4be7a6dcb36245f9c51f6775fbbe1a53895112afeaa4acfec96c80818d00b658e8d3b9d5f7bff820a2be8d20ed95bb14e9410a5473360f3d8112415b873d82253eae8417ed29f66ba8b44fb0cedf4d73fd757c97691ed0ebf685e3e266cd1e4cc8a31857d585239d2b8c45b369b7514", + "priv": "d56225f685bff9271174e8b1e87c7d5717848a0a5dc1b7a463d55027a7e0c0c631029a583d4ec308d185c5ca1a535d0de55012f63013a7b74cf941ca069bb010a2e604b882ad9a6fcb9822f9b0f08efef0fc8bc2808da6e1bde9b1c8d54fe8486b43eb5b64cc5262ab2ecad6685964fe9b131480c386104eaac804ac7629702251264994c8409106011c448d63846d83000d011770e3204e53828d03910c23814d14c50813285013300453b411a1188d831632c9424553166e14082809030080144421a21020342d52422e4348408b244c4ac89164164dc1902502c8690888101c0012a0040e11b160d93680e11432883088241272a0a48d13842c51340114138ac0a20d8b90401b4165180589e0c26422a7095226469a362593886d01410563c68909a70161106e8bb48d21b7719c282e0947080941710b99889cc40801392e80842114070418b064cca408c82652ca2681d9b08892366c12c960cc3220ca926dc29228a012689b3246223580999401c3022ecb3470c1440ec9244d4110400006051214865898014a428a12358ed22080e3b6440bc551d832681a09510838021248221285800bb0918b345089b00080a00d09b645913824c3b0444044268a9260e3c28980c090ca906522124a8848648b985020972c20222650066544a6210a078618132ec42871202726d1b844cb2002e4a6119418411a3828a14410014230190426e02628c2884419040408078511224de3367091a82c0c374e8a082e19c690532830d2c271d400518cc04998244841260192b65011256d0c352802338c94b430da101099484a4ba86d5ab02981c8880c264dcaa6511b155213374ea40024c9209248144c18018cd01652504668e33609e1b6715a36444a94209ca62909b348d3b471a0486202050e812266dc404840422ee1280411c364a1148d81c89019296d8b460a62348cc4084ecb004003410e419461632640093149a314728b186d122348119250d1a6908bc6610ca4510a340808924d1cc01090468688485012b55160a485c4b44dd2380c184909da12619a8801db209204929092206cd10429dcc0650c0942d400691b2505c0a4719286606244601b956d21326094164813106659480621234d1c894451c4605a1289c0084583800c8c988002168993224e1933125c882402072a08342991408aa0b8892148124138218488640934504c325010a088e1386908462a60942103c4654848665a208664427009b6511449804b8880063a2ac026a3394c43b5226567228896fd2cbb52d02f8df3f2e893c880cfa06d6c46f8b32714815af86ff48422e54c684587a1d882d39ce4e8333b5480a367b7cb879a67ec170db468ef94146241ea286663a2833ceb75fd301ad6869c75ed748f7b0d890a82fe1cd9c7e32475f60f4d4cf8e222e60900278f5d8821dcbd9f17cdaab7b43807e7947828559969083f973674f054faf64d4506adf9d36d35a82ac942aff596834dd4482dcf7a80643f3bb5eb7a8eaf744c2a68e9f71fb17ea7f2feafdf659a247b1368c50c30d19d68fd9ed923ed035089a67f7cce25ab62fb33afbfb48affcaa0b1890c0c3cca4c2504b70e8badc9a87957ebba3e4fd80cf96aaac2eec6de60e015813f1eeb4fcc0d77ffd1e293549e6f579460a5921f794538271344d34143c1d699e5df15871c8a03a5bc77951b374656017539c6abc7677e9d2a9ad37ebd8e032a7454eb3ccd9194e6f2b47a4b5f3b48aaec1abf166a3f81d5f533a8b189e92199307d983d1cb2c1282f48509a05c56db952e39584d0028b0a6fcf94ce096d8687123937b84272d48d7720d8d0385b17ba624863efe0f008857306ae44da5d1cb8a98380b6e52db920f1a4c942c634b87db67e559cfb8ed1b43908c6dced9f00fb185389cddc1adf787c1df06522b74801541a310bdd2fed22270e04f433b387e016e1ec5e5a9996e82b6e5bce9e417ac799130ae90a261de9c7d50e69f1a85c269d9a59f1514fa350441213e2780fafa76468340cb349342a2e37f780bb32ff3ce1e981e00f7aab78c1e2e81adc6327f2dfa9d4513e9f5b6404b933ce8d4d65efa4750905606e4515b5073b6a5041c643ecad0cbe83af72c844295cca158c7527b2fbd21010858b8fa72db16b165cb51b3d071c25e51f192341881fef2e5ab2f7dd7398d1fe76c59f0bc35db5e529428919c35ff49be2ce5378db81c290fbbfbd719d2a71048d0c5da78c9b21f8907cd7a319f92730381afb6dd229fbc58bc10a56c55d43de4864bdb04b4fa8113da209a39ff914014eea30befe582798bf8edaf7fe55e52d1710a728dea2a7c2eff0dc69c39c43ed26d5c314b4d34367acc055aefbcd7f995c0fe310e1f5654395862e498fff488467bfc56bef1ed761ba4233bbcde8d2d4c1ad15e68299fae4f037e4e49f6dc49095bcee4fc4fd83cdfe715230c9f763f904f250abf93b25b9a08d69fa257da73d81403ac195154c0ad02364e1f284f144157259be6574269035cba5a1dafbe04c2625597adda138f1e7d769a1b05c4ba72f452c732ce1286f13b995f0c111ba700589ffb143b10d58bf48555be55a9ce6325934e3247ffb22aaaa1fe89d57a6669a689a033d53b8f2383096c14b22e6b7be3056b19277fa01565a79cb97f207ae55b935c011f68cd395e865af7be570a17575ad92acc454936b5beb748312925dafad96f073790d00b9fca86a4a3671637c9b4826d1ec2f59678526b0f4c9dbde27f34193f6b53e488cf6ccad9561f4ea0b575b9ea4322e3690a826bd3ac718602f01f91e903414254e7221a84ea764d3a95b7428fbeb2ab2da0ffe079c7aa788e367bad8fae71f0f83498dc36e8db57fddfc3ea5e53ea0878c2cc9b36cc4d5619239cd1f526ae625bac5fa2413688d6615becd2ba322efc5a066759087ee3862307630781cac102a7fb772d8c90c9233a4451a63c1009121b00ea0a6d3af4612fb7faeeec4e100fa44fb23c6cdfdd2666d55815e7170d934b9c26e3945dc6d0355b598886e0cc399a789e47a8cf8ed4ffa1a1f86f7ee6b6b535ca26acc5ebeb3262ad40cd4bb330b1fc926ba202a65faa42252a78da797caa822e4fa45f5e449b10dbf0654482292dd29db11b5dc1d2ced5814c373d94de7bb64098ed3101ebfd847c51c4c90d5ae8e5f07636cac1950cf4eeeb191c329ffc95f3d3767b56e65991039fb7ac6786d573c56273aae78e53d4a642e020b27d2c961305bfef66d5818d504fb4533c4faba7fd594919b96f20a44baca34173aa9bfe115ed6b5bf6f03ecbd004d3417d6dedfbeb7508612bdb90afa7f61e838ced2a13c6e5bcdad3a3d4377bacaa20835352d71f6e7722bfc61a8430a82afe4ee3acebe407aec930fefbfd387f696ac1021325f5a5792b9fd5ad2c5d57acc496a9b0fb264ae2e0d5c37b1b719d7a6b1077e3fa531c2a584562764b1a76409c3421c8d6a40a80a55d49da80a5e9afcdc6766f1721408dfd5ac34b1301128d374e085456e2831c7c7fdde3cb4698708ed8f447d59201e017ea7552f53c4e62d61e4a69cbd3116c193b4304f80f828068fb3e47e704c1c62f9f4c0baf5cd69e6c113afd90cf1238ce8c7c783d" + } + ] +} \ No newline at end of file