Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sender HMAC to MessageV2 #218

Merged
merged 29 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8d3ccba
bump the proto code
nplasterer Jan 19, 2024
27cbb5a
add should push entitlement
nplasterer Jan 19, 2024
f355cc4
dump the protos one more time
nplasterer Jan 24, 2024
858cff4
update the protos again
nplasterer Jan 24, 2024
a60d655
feat: add to TextCodec & Composite
Jan 25, 2024
e269e40
feat: add HKDF key derivation and HMAC signature generation
Jan 25, 2024
b0e163d
feat: add shouldPush parameter to MessageV2.encode() and prepareMessa…
Jan 29, 2024
000f28f
feat: add shouldPush method to the remaining content codecs
Jan 29, 2024
7c1ea75
fix: tests
Jan 29, 2024
d99a1c8
fix: remove print statement in MessageTests.swift
Jan 29, 2024
0d70a8f
fix: remove commented code
Jan 30, 2024
d6981b9
feat: set for all Content Types
Jan 30, 2024
9818ee5
fix: add decoded content in prepareMessage function
Jan 30, 2024
821f77e
chore: remove unused dependencies
Jan 31, 2024
5a42744
Replace xmtp-rust-swift with libxmtp-swift (#217)
nakajima Jan 22, 2024
eddfa0e
Update for latest uniffi (#219)
nakajima Jan 22, 2024
64b6ae0
Fix example app (#220)
nakajima Jan 22, 2024
be1eea4
Access libxmtp-swift with static binaries (#225)
cameronvoell Jan 29, 2024
8ed7e47
Merge pre libxmtp into main (#223)
nplasterer Jan 31, 2024
e8800dd
Update Package.resolved with new branch and revision
Jan 31, 2024
dad85c7
Merge branch 'main' into np/ios-entitlement
nplasterer Feb 1, 2024
45bdee1
Merge branch 'np/ios-entitlement' of https://github.com/xmtp/xmtp-ios…
nplasterer Feb 1, 2024
d8cfd6a
bring back the proto work
nplasterer Feb 1, 2024
a5e00a0
remove the xmtp folder
nplasterer Feb 1, 2024
e622a5d
chore: refactor message encoding and decoding
Feb 1, 2024
8e662c7
chore: update libxmtp-swift to latest version
Feb 1, 2024
ec8192b
fix: refactor ConversationTests.swift
Feb 1, 2024
1967934
potential test
nplasterer Feb 1, 2024
c21b280
chore: update XMTP.podspec version to 0.7.9-alpha0
Feb 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/xmtp/libxmtp-swift",
"state" : {
"branch" : "ccbf6ac",
"revision" : "ccbf6ac71b8c5a89c3078d8dc4057123bea8a291"
"branch" : "92274fe",
"revision" : "92274fe0dde1fc7f8f716ebcffa3d252813be56d"
}
},
{
Expand Down
4 changes: 4 additions & 0 deletions Sources/XMTPiOS/Codecs/AttachmentCodec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,8 @@ public struct AttachmentCodec: ContentCodec {
public func fallback(content: Attachment) throws -> String? {
return "Can’t display “\(content.filename)”. This app doesn’t support attachments."
}

public func shouldPush(content: Attachment) throws -> Bool {
return true
}
}
4 changes: 4 additions & 0 deletions Sources/XMTPiOS/Codecs/Composite.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ struct CompositeCodec: ContentCodec {
public func fallback(content: DecodedComposite) throws -> String? {
return nil
}

public func shouldPush(content: DecodedComposite) throws -> Bool {
return false
}

func toComposite(content decodedComposite: DecodedComposite) -> Composite {
var composite = Composite()
Expand Down
1 change: 1 addition & 0 deletions Sources/XMTPiOS/Codecs/ContentCodec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ public protocol ContentCodec: Hashable, Equatable {
func encode(content: T, client: Client) throws -> EncodedContent
func decode(content: EncodedContent, client: Client) throws -> T
func fallback(content: T) throws -> String?
func shouldPush(content: T) throws -> Bool
}

public extension ContentCodec {
Expand Down
11 changes: 11 additions & 0 deletions Sources/XMTPiOS/Codecs/ReactionCodec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,15 @@ public struct ReactionCodec: ContentCodec {
return nil
}
}

public func shouldPush(content: Reaction) throws -> Bool {
switch content.action {
case .added:
return true
case .removed:
return false
case .unknown:
return false
}
}
}
4 changes: 4 additions & 0 deletions Sources/XMTPiOS/Codecs/ReadReceiptCodec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,8 @@ public struct ReadReceiptCodec: ContentCodec {
public func fallback(content: ReadReceipt) throws -> String? {
return nil
}

public func shouldPush(content: ReadReceipt) throws -> Bool {
return false
}
}
4 changes: 4 additions & 0 deletions Sources/XMTPiOS/Codecs/RemoteAttachmentCodec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -206,4 +206,8 @@ public struct RemoteAttachmentCodec: ContentCodec {

return Data(parameterData)
}

public func shouldPush(content: RemoteAttachment) throws -> Bool {
return true
}
}
4 changes: 4 additions & 0 deletions Sources/XMTPiOS/Codecs/ReplyCodec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,8 @@ public struct ReplyCodec: ContentCodec {
public func fallback(content: Reply) throws -> String? {
return "Replied with “\(content.content)” to an earlier message"
}

public func shouldPush(content: Reply) throws -> Bool {
return true
}
}
4 changes: 4 additions & 0 deletions Sources/XMTPiOS/Codecs/TextCodec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,8 @@ public struct TextCodec: ContentCodec {
public func fallback(content: String) throws -> String? {
return nil
}

public func shouldPush(content: String) throws -> Bool {
return true
}
}
8 changes: 6 additions & 2 deletions Sources/XMTPiOS/ConversationV2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,14 @@ public struct ConversationV2 {
}

func prepareMessage(encodedContent: EncodedContent, options: SendOptions?) async throws -> PreparedMessage {
let codec = client.codecRegistry.find(for: options?.contentType)

let message = try await MessageV2.encode(
client: client,
content: encodedContent,
topic: topic,
keyMaterial: keyMaterial
keyMaterial: keyMaterial,
codec: codec
)

let topic = options?.ephemeral == true ? ephemeralTopic : topic
Expand Down Expand Up @@ -233,7 +236,8 @@ public struct ConversationV2 {
client: client,
content: content,
topic: topic,
keyMaterial: keyMaterial
keyMaterial: keyMaterial,
codec: codec
)

let envelope = Envelope(
Expand Down
40 changes: 39 additions & 1 deletion Sources/XMTPiOS/Crypto.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Foundation
public typealias CipherText = Xmtp_MessageContents_Ciphertext

enum CryptoError: Error {
case randomBytes, combinedPayload
case randomBytes, combinedPayload, keyDerivationError, hmacSignatureError
}

enum Crypto {
Expand Down Expand Up @@ -103,4 +103,42 @@ enum Crypto {
throw CryptoError.randomBytes
}
}

static func hkdfHmacKey(secret: Data, info: Data) throws -> SymmetricKey {
do {
let salt = try secureRandomBytes(count: 32)
let key = HKDF<SHA256>.deriveKey(
inputKeyMaterial: SymmetricKey(data: secret),
salt: salt,
info: info,
outputByteCount: 32)
return key
} catch {
throw CryptoError.keyDerivationError
}
}

static func generateHmacSignature(secret: Data, info: Data, message: Data) throws -> Data {
do {
let key = try hkdfHmacKey(secret: secret, info: info)
let signature = HMAC<SHA256>.authenticationCode(for: message, using: key)
return Data(signature)
} catch {
throw CryptoError.hmacSignatureError
}
}

static func exportHmacKey(key: SymmetricKey) -> Data {
var exportedData = Data(count: key.bitCount / 8)
exportedData.withUnsafeMutableBytes { buffer in
key.withUnsafeBytes { keyBuffer in
buffer.copyMemory(from: keyBuffer)
}
}
return exportedData
}

static func importHmacKey(keyData: Data) -> SymmetricKey {
return SymmetricKey(data: keyData)
}
}
24 changes: 20 additions & 4 deletions Sources/XMTPiOS/Messages/MessageV2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ import LibXMTP
typealias MessageV2 = Xmtp_MessageContents_MessageV2

enum MessageV2Error: Error {
case invalidSignature, decodeError(String)
case invalidSignature, decodeError(String), invalidData
}

extension MessageV2 {
init(headerBytes: Data, ciphertext: CipherText) {
init(headerBytes: Data, ciphertext: CipherText, senderHmac: Data, shouldPush: Bool) {
self.init()
self.headerBytes = headerBytes
self.ciphertext = ciphertext
self.senderHmac = senderHmac
self.shouldPush = shouldPush
}

static func decrypt(_ id: String, _ topic: String, _ message: MessageV2, keyMaterial: Data, client: Client) throws -> DecryptedMessage {
Expand Down Expand Up @@ -78,7 +80,7 @@ extension MessageV2 {
}
}

static func encode(client: Client, content encodedContent: EncodedContent, topic: String, keyMaterial: Data) async throws -> MessageV2 {
static func encode<Codec: ContentCodec>(client: Client, content encodedContent: EncodedContent, topic: String, keyMaterial: Data, codec: Codec) async throws -> MessageV2 {
let payload = try encodedContent.serializedData()

let date = Date()
Expand All @@ -95,10 +97,24 @@ extension MessageV2 {
let signedBytes = try signedContent.serializedData()

let ciphertext = try Crypto.encrypt(keyMaterial, signedBytes, additionalData: headerBytes)

let thirtyDayPeriodsSinceEpoch = Int(date.timeIntervalSince1970 / 60 / 60 / 24 / 30)
let info = "\(thirtyDayPeriodsSinceEpoch)-\(client.address)"
guard let infoEncoded = info.data(using: .utf8) else {
throw MessageV2Error.invalidData
}

let senderHmac = try Crypto.generateHmacSignature(secret: keyMaterial, info: infoEncoded, message: headerBytes)

let decoded = try codec.decode(content: encodedContent, client: client)
let shouldPush = try codec.shouldPush(content: decoded)


return MessageV2(
headerBytes: headerBytes,
ciphertext: ciphertext
ciphertext: ciphertext,
senderHmac: senderHmac,
shouldPush: shouldPush
)
}
}
Loading
Loading