Skip to content

Commit

Permalink
Merge pull request #155 from xmtp/xmtp-proto-v3
Browse files Browse the repository at this point in the history
Upgrade to V3 of XMTP Proto
  • Loading branch information
neekolas authored Sep 7, 2022
2 parents b8085fd + 50a2660 commit 028a56e
Show file tree
Hide file tree
Showing 13 changed files with 69 additions and 64 deletions.
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"dependencies": {
"@noble/secp256k1": "^1.5.2",
"@stardazed/streams-polyfill": "^2.4.0",
"@xmtp/proto": "^1.3.1",
"@xmtp/proto": "~3.0.0",
"ethers": "^5.5.3",
"long": "^5.2.0",
"node-localstorage": "^2.2.1"
Expand Down
13 changes: 7 additions & 6 deletions src/ContactBundle.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { xmtpEnvelope as proto } from '@xmtp/proto'
import { contact, publicKey } from '@xmtp/proto'
import { PublicKeyBundle } from './crypto'
import PublicKey from './crypto/PublicKey'

// ContactBundle packages all the infromation which a client uses to advertise on the network.
export default class ContactBundle implements proto.ContactBundleV1 {
export default class ContactBundle implements contact.ContactBundleV1 {
keyBundle: PublicKeyBundle

constructor(publicKeyBundle: PublicKeyBundle) {
Expand All @@ -14,10 +14,11 @@ export default class ContactBundle implements proto.ContactBundleV1 {
}

toBytes(): Uint8Array {
return proto.ContactBundle.encode({
return contact.ContactBundle.encode({
v1: {
keyBundle: this.keyBundle,
},
v2: undefined,
}).finish()
}

Expand All @@ -42,9 +43,9 @@ export default class ContactBundle implements proto.ContactBundleV1 {
)
}

static decodeV1(bytes: Uint8Array): proto.PublicKeyBundle | undefined {
static decodeV1(bytes: Uint8Array): publicKey.PublicKeyBundle | undefined {
try {
const b = proto.ContactBundle.decode(bytes)
const b = contact.ContactBundle.decode(bytes)
return b.v1?.keyBundle
} catch (e) {
if (
Expand All @@ -53,7 +54,7 @@ export default class ContactBundle implements proto.ContactBundleV1 {
) {
// Adds a default fallback for older versions of the proto (Which may also fail)
try {
return proto.PublicKeyBundle.decode(bytes)
return publicKey.PublicKeyBundle.decode(bytes)
} catch (e) {
throw new Error("Couldn't decode contact bundle: " + e)
}
Expand Down
23 changes: 13 additions & 10 deletions src/Message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { bytesToHex } from './crypto/utils'
import { sha256 } from './crypto/encryption'
import { ContentTypeId } from './MessageContent'

const extractV1Message = (msg: proto.Message): proto.V1Message => {
const extractV1Message = (msg: proto.Message): proto.MessageV1 => {
if (!msg.v1) {
throw new Error('Message is not of type v1')
}
Expand All @@ -23,8 +23,8 @@ const extractV1Message = (msg: proto.Message): proto.V1Message => {
// Message is basic unit of communication on the network.
// Message header carries the sender and recipient keys used to protect message.
// Message timestamp is set by the sender.
export default class Message implements proto.V1Message {
header: proto.MessageHeader // eslint-disable-line camelcase
export default class Message implements proto.MessageV1 {
header: proto.MessageHeaderV1 // eslint-disable-line camelcase
headerBytes: Uint8Array // encoded header bytes
ciphertext: Ciphertext
decrypted?: Uint8Array
Expand All @@ -46,7 +46,7 @@ export default class Message implements proto.V1Message {
id: string,
bytes: Uint8Array,
obj: proto.Message,
header: proto.MessageHeader
header: proto.MessageHeaderV1
) {
const msg = extractV1Message(obj)
this.id = id
Expand All @@ -65,7 +65,7 @@ export default class Message implements proto.V1Message {

static async create(
obj: proto.Message,
header: proto.MessageHeader,
header: proto.MessageHeaderV1,
bytes: Uint8Array
): Promise<Message> {
const id = bytesToHex(await sha256(bytes))
Expand All @@ -75,7 +75,7 @@ export default class Message implements proto.V1Message {
static async fromBytes(bytes: Uint8Array): Promise<Message> {
const msg = proto.Message.decode(bytes)
const innerMessage = extractV1Message(msg)
const header = proto.MessageHeader.decode(innerMessage.headerBytes)
const header = proto.MessageHeaderV1.decode(innerMessage.headerBytes)
return Message.create(msg, header, bytes)
}

Expand Down Expand Up @@ -116,14 +116,17 @@ export default class Message implements proto.V1Message {
false
)
// eslint-disable-next-line camelcase
const header: proto.MessageHeader = {
const header: proto.MessageHeaderV1 = {
sender: sender.getPublicKeyBundle(),
recipient,
timestamp: Long.fromNumber(timestamp.getTime()),
}
const headerBytes = proto.MessageHeader.encode(header).finish()
const headerBytes = proto.MessageHeaderV1.encode(header).finish()
const ciphertext = await encrypt(message, secret, headerBytes)
const protoMsg = { v1: { headerBytes: headerBytes, ciphertext } }
const protoMsg = {
v1: { headerBytes: headerBytes, ciphertext },
v2: undefined,
}
const bytes = proto.Message.encode(protoMsg).finish()
const msg = await Message.create(protoMsg, header, bytes)
msg.decrypted = message
Expand All @@ -139,7 +142,7 @@ export default class Message implements proto.V1Message {
): Promise<Message> {
const message = proto.Message.decode(bytes)
const v1Message = extractV1Message(message)
const header = proto.MessageHeader.decode(v1Message.headerBytes)
const header = proto.MessageHeaderV1.decode(v1Message.headerBytes)
if (!header) {
throw new Error('missing message header')
}
Expand Down
6 changes: 3 additions & 3 deletions src/authn/Authenticator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { keccak256 } from 'js-sha3'
import { authn, xmtpEnvelope } from '@xmtp/proto'
import { authn, signature, publicKey } from '@xmtp/proto'
import AuthData from './AuthData'
import { PrivateKey } from '../crypto'
import { hexToBytes } from '../crypto/utils'
Expand All @@ -26,7 +26,7 @@ export default class Authenticator {

return new Token(
authn.Token.fromPartial({
identityKey: xmtpEnvelope.PublicKey.fromPartial(
identityKey: publicKey.PublicKey.fromPartial(
// The generated types are overly strict and don't like our additional methods
// eslint-disable-next-line
// @ts-ignore
Expand All @@ -36,7 +36,7 @@ export default class Authenticator {
// The generated types are overly strict and don't like our additional methods
// eslint-disable-next-line
// @ts-ignore
authDataSignature: xmtpEnvelope.Signature.fromPartial(authSig),
authDataSignature: signature.Signature.fromPartial(authSig),
})
)
}
Expand Down
6 changes: 3 additions & 3 deletions src/authn/Token.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { authn, xmtpEnvelope } from '@xmtp/proto'
import { authn, signature, publicKey } from '@xmtp/proto'
import AuthData from './AuthData'

export default class Token implements authn.Token {
identityKey: xmtpEnvelope.PublicKey
identityKey: publicKey.PublicKey
authDataBytes: Uint8Array
authDataSignature: xmtpEnvelope.Signature
authDataSignature: signature.Signature
private _authData?: AuthData

constructor({ identityKey, authDataBytes, authDataSignature }: authn.Token) {
Expand Down
12 changes: 6 additions & 6 deletions src/crypto/Ciphertext.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { xmtpEnvelope as proto } from '@xmtp/proto'
import { ciphertext } from '@xmtp/proto'

export const AESKeySize = 32 // bytes
export const KDFSaltSize = 32 // bytes
Expand All @@ -8,10 +8,10 @@ export const AESGCMTagLength = 16 // property tagLength

// Ciphertext packages the encrypted ciphertext with the salt and nonce used to produce it.
// salt and nonce are not secret, and should be transmitted/stored along with the encrypted ciphertext.
export default class Ciphertext implements proto.Ciphertext {
aes256GcmHkdfSha256: proto.Ciphertext_Aes256gcmHkdfsha256 | undefined // eslint-disable-line camelcase
export default class Ciphertext implements ciphertext.Ciphertext {
aes256GcmHkdfSha256: ciphertext.Ciphertext_Aes256gcmHkdfsha256 | undefined // eslint-disable-line camelcase

constructor(obj: proto.Ciphertext) {
constructor(obj: ciphertext.Ciphertext) {
if (!obj.aes256GcmHkdfSha256) {
throw new Error('invalid ciphertext')
}
Expand All @@ -34,10 +34,10 @@ export default class Ciphertext implements proto.Ciphertext {
}

toBytes(): Uint8Array {
return proto.Ciphertext.encode(this).finish()
return ciphertext.Ciphertext.encode(this).finish()
}

static fromBytes(bytes: Uint8Array): Ciphertext {
return new Ciphertext(proto.Ciphertext.decode(bytes))
return new Ciphertext(ciphertext.Ciphertext.decode(bytes))
}
}
12 changes: 6 additions & 6 deletions src/crypto/PrivateKey.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { privateKey as proto } from '@xmtp/proto'
import { privateKey } from '@xmtp/proto'
import * as secp from '@noble/secp256k1'
import Long from 'long'
import Signature from './Signature'
Expand All @@ -7,12 +7,12 @@ import Ciphertext from './Ciphertext'
import { decrypt, encrypt, sha256 } from './encryption'

// PrivateKey represents a secp256k1 private key.
export default class PrivateKey implements proto.PrivateKey {
export default class PrivateKey implements privateKey.PrivateKey {
timestamp: Long
secp256k1: proto.PrivateKey_Secp256k1 | undefined // eslint-disable-line camelcase
secp256k1: privateKey.PrivateKey_Secp256k1 | undefined // eslint-disable-line camelcase
publicKey: PublicKey // caches corresponding PublicKey

constructor(obj: proto.PrivateKey) {
constructor(obj: privateKey.PrivateKey) {
if (!obj.secp256k1) {
throw new Error('invalid private key')
}
Expand Down Expand Up @@ -127,10 +127,10 @@ export default class PrivateKey implements proto.PrivateKey {
}

toBytes(): Uint8Array {
return proto.PrivateKey.encode(this).finish()
return privateKey.PrivateKey.encode(this).finish()
}

static fromBytes(bytes: Uint8Array): PrivateKey {
return new PrivateKey(proto.PrivateKey.decode(bytes))
return new PrivateKey(privateKey.PrivateKey.decode(bytes))
}
}
14 changes: 7 additions & 7 deletions src/crypto/PublicKey.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { xmtpEnvelope as proto } from '@xmtp/proto'
import { publicKey } from '@xmtp/proto'
import * as secp from '@noble/secp256k1'
import Long from 'long'
import Signature from './Signature'
Expand All @@ -9,12 +9,12 @@ import { sha256 } from './encryption'
// PublicKey represents uncompressed secp256k1 public key,
// that can optionally be signed with another trusted key pair.
// PublicKeys can be generated through PrivateKey.generate()
export default class PublicKey implements proto.PublicKey {
export default class PublicKey implements publicKey.PublicKey {
timestamp: Long
secp256k1Uncompressed: proto.PublicKey_Secp256k1Uncompressed // eslint-disable-line camelcase
secp256k1Uncompressed: publicKey.PublicKey_Secp256k1Uncompressed // eslint-disable-line camelcase
signature?: Signature

constructor(obj: proto.PublicKey) {
constructor(obj: publicKey.PublicKey) {
if (!obj?.secp256k1Uncompressed?.bytes) {
throw new Error('invalid public key')
}
Expand Down Expand Up @@ -58,7 +58,7 @@ export default class PublicKey implements proto.PublicKey {
}

bytesToSign(): Uint8Array {
return proto.PublicKey.encode({
return publicKey.PublicKey.encode({
timestamp: this.timestamp,
secp256k1Uncompressed: this.secp256k1Uncompressed,
}).finish()
Expand Down Expand Up @@ -154,10 +154,10 @@ export default class PublicKey implements proto.PublicKey {
}

toBytes(): Uint8Array {
return proto.PublicKey.encode(this).finish()
return publicKey.PublicKey.encode(this).finish()
}

static fromBytes(bytes: Uint8Array): PublicKey {
return new PublicKey(proto.PublicKey.decode(bytes))
return new PublicKey(publicKey.PublicKey.decode(bytes))
}
}
8 changes: 4 additions & 4 deletions src/crypto/PublicKeyBundle.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { xmtpEnvelope as proto } from '@xmtp/proto'
import { publicKey } from '@xmtp/proto'
import PublicKey from './PublicKey'

// PublicKeyBundle packages all the keys that a participant should advertise.
// The PreKey must be signed by the IdentityKey.
// The IdentityKey can be signed by the wallet to authenticate it.
export default class PublicKeyBundle implements proto.PublicKeyBundle {
export default class PublicKeyBundle implements publicKey.PublicKeyBundle {
identityKey: PublicKey
preKey: PublicKey

Expand All @@ -24,11 +24,11 @@ export default class PublicKeyBundle implements proto.PublicKeyBundle {
}

toBytes(): Uint8Array {
return proto.PublicKeyBundle.encode(this).finish()
return publicKey.PublicKeyBundle.encode(this).finish()
}

static fromBytes(bytes: Uint8Array): PublicKeyBundle {
const decoded = proto.PublicKeyBundle.decode(bytes)
const decoded = publicKey.PublicKeyBundle.decode(bytes)
if (!decoded.identityKey) {
throw new Error('missing identity key')
}
Expand Down
13 changes: 7 additions & 6 deletions src/crypto/Signature.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { xmtpEnvelope as proto } from '@xmtp/proto'
import { signature } from '@xmtp/proto'
import Long from 'long'
import * as secp from '@noble/secp256k1'
import PublicKey from './PublicKey'

// Signature represents an ECDSA signature with recovery bit.
export default class Signature implements proto.Signature {
ecdsaCompact: proto.Signature_ECDSACompact | undefined // eslint-disable-line camelcase
export default class Signature implements signature.Signature {
ecdsaCompact: signature.Signature_ECDSACompact | undefined // eslint-disable-line camelcase
walletEcdsaCompact: signature.Signature_WalletECDSACompact | undefined // eslint-disable-line camelcase

constructor(obj: proto.Signature) {
constructor(obj: Partial<signature.Signature>) {
if (!obj.ecdsaCompact) {
throw new Error('invalid signature')
}
Expand Down Expand Up @@ -43,10 +44,10 @@ export default class Signature implements proto.Signature {
}

toBytes(): Uint8Array {
return proto.Signature.encode(this).finish()
return signature.Signature.encode(this).finish()
}

static fromBytes(bytes: Uint8Array): Signature {
return new Signature(proto.Signature.decode(bytes))
return new Signature(signature.Signature.decode(bytes))
}
}
6 changes: 3 additions & 3 deletions test/ContentTypeTestKey.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { xmtpEnvelope as proto } from '@xmtp/proto'
import { publicKey } from '@xmtp/proto'
import { ContentTypeId, ContentCodec, PublicKey, EncodedContent } from '../src'

export const ContentTypeTestKey = new ContentTypeId({
Expand All @@ -17,11 +17,11 @@ export class TestKeyCodec implements ContentCodec<PublicKey> {
return {
type: ContentTypeTestKey,
parameters: {},
content: proto.PublicKey.encode(key).finish(),
content: publicKey.PublicKey.encode(key).finish(),
}
}

decode(content: EncodedContent): PublicKey {
return new PublicKey(proto.PublicKey.decode(content.content))
return new PublicKey(publicKey.PublicKey.decode(content.content))
}
}
Loading

0 comments on commit 028a56e

Please sign in to comment.