JMP

XMPPTwitterReddit
Featured Image

Mobile-friendly Gateway to any SIP Provider

singpolyma@singpolyma.net

We have for a long time supported the public Cheogram SIP instance, which allows easy interaction between the federated Jabber network and the federated SIP network. When it comes to connecting to the phone network via a SIP provider, however, very few of these providers choose to interact with the federated SIP network at all. It has always been possible to work around this with a self-hosted PBX, but documentation on the best way to do this is scant. We have also heard from some that they would like hosting the gateway themselves to be easier, as increasingly people are familiar with Docker and not with other packaging formats. So, we have sponsored the development of a Docker packaging solution for the full Cheogram SIP solution, including an easy ability to connect to an unfederated SIP server

XMPP Server

First of all, in order to self-host a gateway speaking the XMPP protocol on one side, you’ll need an XMPP server. We suggest Prosody, which is already available from many operating systems. While a full Prosody self-hosting tutorial is out of scope here, the relevant configuration to add looks like this:

Component "asterisk"
    component_secret = "some random secret 1"
    modules_disabled = { "s2s" }
Component "sip"
    component_secret = "some random secret 2"
    modules_disabled = { "s2s" }

Note that, especially if you are going to set the gateway up with access to your private SIP account at some provider, you almost certaintly do not want either of these federated. So no DNS setup is needed, nor do the component names need to be real hostnames. The rest of this guide will assume you’ve used the names here.

If you don’t use Prosody, configuration for most other XMPP servers should be similar.

Run Docker Image

You’ll need to pull the Docker image:

docker pull singpolyma/cheogram-sip:latest

Then run it like this:

docker run -d \
    --network=host \
    -e COMPONENT_DOMAIN=sip \
    -e COMPONENT_SECRET="some random secret 2" \
    -e ASTERISK_COMPONENT_DOMAIN=asterisk \
    -e ASTERISK_COMPONENT_SECRET="some random secret 1" \
    -e SIP_HOST=sip.yourprovider.example.com \
    -e SIP_USER=your_sip_username \
    -e SIP_PASSWORD=your_sip_password \
    -e SIP_JID=your-jabber-id@yourdomain.example.com \
    singpolyma/cheogram-sip:latest

If you just want to connect with the federated SIP network, you can leave off the SIP_HOST, SIP_USER, SIP_PASSWORD, and SIP_JID. If you are using a private SIP provider for connecting to the phone network, then fill in those values with the connection information for your provider, and also your own Jabber ID so it knows where to send calls that come in to that SIP address.

Make a Call

You can now make a call to any federated SIP address at them\40theirdomain.example.com@sip and to any phone number at +15551234567@sip which wil route via your configured SIP provider.

You should even be able to use the dialler in Cheogram Android:

Cheogram Android Dialler
Cheogram Android Dialler

Inbound calls will route to your Jabber ID automatically as well.

What About SMS?

Cheogram SIP does have some basic support for SIP MESSAGE protocol, so if your provider has that it may work, but more testing and polish is needed since this is not a very common feature at providers we have tested with.

Where to Learn More

If you have any questions or feedback of any kind, don’t hesistate to stop by the project channel which you can get on the web or using your Jabber ID.

Featured Image

Newsletter: JMP is 7 years old — thanks to our awesome community!

denver@ozg.ca

Hi everyone!

Welcome to the latest edition of your pseudo-monthly JMP update!

In case it’s been a while since you checked out JMP, here’s a refresher: JMP lets you send and receive text and picture messages (and calls) through a real phone number right from your computer, tablet, phone, or anything else that has a Jabber client. Among other things, JMP has these features: Your phone number on every device; Multiple phone numbers, one app; Free as in Freedom; Share one number with multiple people.

Today JMP is 7 years old! We launched on this day in 2017 and a lot has changed since then. In addition to what we talked about in past years (see https://blog.jmp.chat/b/february-newsletter-2022 and https://blog.jmp.chat/b/february-newsletter-2023 for example), in the last year we’ve brought JMP out of beta, launched a data plan, and have continued to grow our huge community of people (channel participants, JMP customers, and many more) excited about communication freedom. So, in light of some vibes from yesterday’s “celebration” in some countries, we’d like to take this opportunity to say: Thank you to everyone involved in JMP, however that may be! You are part of something big and getting bigger! Communication freedom knows no bounds, technically, socially, or geographically. And you make that happen!

Along with this huge community growing, we’ve been growing JMP’s staff as well — we’re now up to 5 employees working hard to build and maintain the foundations of communication freedom every day. We look forward to continuing this growth, in a strong and sustainable way, for years to come.

Lastly, while dates have not been announced yet, we’re excited to say we’ll be back at FOSSY in Portland, Oregon, this year! FOSSY is expected to happen in July and, if last year is any indication, it will be a blast. We’d love to see some of you there!

Thanks again to everyone for helping us get to where we are today. We’re super grateful for all your support!

As always, we’re very open to feedback and would love to hear from you if you have any comments, questions, or otherwise. Feel free to reply (if you got this by email), comment, or find us on any of the following:

Thanks for reading and have a wonderful rest of your week!

Featured Image

Newsletter: JMP Data Plan

singpolyma@singpolyma.net

Hi everyone!

Welcome to the latest edition of your pseudo-monthly JMP update!

In case it’s been a while since you checked out JMP, here’s a refresher: JMP lets you send and receive text and picture messages (and calls) through a real phone number right from your computer, tablet, phone, or anything else that has a Jabber client. Among other things, JMP has these features: Your phone number on every device; Multiple phone numbers, one app; Free as in Freedom; Share one number with multiple people.

The biggest announcement this month is that the JMP Data Plan is, for customers anyway, no longer behind a waiting list! For those not yet familiar with the plan, this is USA+Canada only (for now) and also data only (no phone number, since if you want one of those you can use JMP!) It works like other pre-paid data plans you might be used to, except greatly simplified. Data never expires (there is a nominal annual fee to keep a plan active) and by default auto-refills whenever it gets low (up to a user-configurable limit every month). Data is purchased in blocks of 5GB and works on most major carriers in the USA and Canada.

Any JMP customer can go now to their account settings and use a command to buy one or more data plans, delivered using either a Physical SIM in postal mail, or eSIM download. People who want a data plan but don’t want a JMP number will need to wait a little longer, and can still add themselves to the waiting list for now, as we work out the billing system changes needed to support this seamlessly.

Speaking of eSIMs, we have heard from a lot of you since we first launched the data plan in the testing phase about gaps in the current eSIM ecosystem. Many people are still using devices that do not support eSIM, or operating systems that do not support downloading an eSIM with freedomware. Others just have trouble getting an eSIM moved from an old device to a new device, or prefer the flexibility to move their plans between multiple devices on a regular basis. All of this is why we have, since the beginning, offered the option to get our data plan shipped on a physical SIM card. However, we are currently investigating some options to do more, and bring the flexibility of a physical SIM (and software freedom and broad device compatibility) to eSIMs from any provider. It’s early days yet, but if this interests you, come by the chatroom and talk to us about what you’d love to see in the future.

To learn what’s happening with JMP between newsletters, here are some ways you can find out:

Thanks for reading and have a wonderful rest of your week!

Featured Image

SMS Censorship

singpolyma@singpolyma.net

Since almost the very beginning of JMP there have been occasional SMS and MMS delivery failures with an error message like “Rejected for SPAM”. By itself this is not too surprising, since every communications system has a SPAM problem and every SPAM blocking technique has some false positives. Over the past few years, however, the incidence of this error has gone up and up. But whenever we investigate, we find no SPAM being sent, just regular humans having regular conversations. So what is happening here? Are the SPAM filters getting worse?

In a word: yes.

It seems that in an effort to self-regulate and reduce certain kinds of “undesirable content” most carriers have resorted to wholesale keyword blocking of words not commonly found in SPAM, but referring to items and concepts the carriers find undesirable. For example, at least one major USA carrier blocks every SMS message containing the word “morphine”. How any hospital staff or family with hospitalized members are meant to know they must avoid this word is anyone’s guess, hopefully after being told their messages are “SPAM” they can guess to say “they upped Mom’s M dose” instead?

What We Are Doing

To preserve our reputation with these carriers we have begun to build an internal list of the keywords being blocked by different major carriers, and blocking all messages with those keywords ourselves rather than attempt to deliver them. While this seems like a suboptimal solution, the messages would never have been delivered anyways and this reduces the amount of “SPAM” that the carriers see coming from us. We have also insituted a cooldown such that if your account triggers a “SPAM” error from a major carrier, further messages are blocked for a short time to avoid repeated attempts to send the same message.

So what are the kinds of “undesirable content” the carriers are attempting to avoid here?

  • Obviously please do not use JMP for anything illegal. This has never been allowed and we continue to not tolerate this in any way.
  • Additionally, please avoid sexually explicit or graphically violent discussions, or discussions about drugs illegal in any part of the USA.

This is not really our policy so much as it is that of the carriers we must work with in order to continue delivering your messages to friends and family.

What You Can Do

Every JMP account comes with, as an option, a Snikket instance of your very own. As always, we highly recommend inviting friends and family you have many discussions with (especially discussions about sex, firearms, or drugs) to your Snikket instance and continuing all conversations there in private instead of broadcasting them over the phone network. Sending an invite link to your Snikket instance is easy, and anyone who uses the link will get an account on your instance, with yourself and others as a contact, set up automatically, so it is a great way to speak more securely with family and friend groups. Snikket will also enable higher quality media sharing, video calls, and many other benefits for your regular contacts.

Of course we know you will continue to need SMS and MMS for many of your contacts now and in the future, and JMP is dedicated to continuing to provide best-in-class service for person to person communication in this way as well.

Featured Image

Newsletter: Holidays

singpolyma@singpolyma.net

Hi everyone!

Welcome to the latest edition of your pseudo-monthly JMP update!

In case it’s been a while since you checked out JMP, here’s a refresher: JMP lets you send and receive text and picture messages (and calls) through a real phone number right from your computer, tablet, phone, or anything else that has a Jabber client. Among other things, JMP has these features: Your phone number on every device; Multiple phone numbers, one app; Free as in Freedom; Share one number with multiple people.

Automatic refill for users of the data plan was rolled out to everyone this fall. This has been going well and we fully expect to enable new SIM and eSIM orders for all JMP customers (with no waitlist) in January, after the holidays.

Speaking of holidays, MBOA staff, including JMP support staff, will be taking an end of year break just like we always do. Expect support response times to be longer than usual from December 18 until January 2.

This fall also saw the silent launch of new inventory features for JMP. Historically, JMP has never held inventory of phone numbers, buying them directly from our carrier partners when a customer places an order. Unfortunately, this leaves us at the mercy of which regions our partners choose to keep in stock, and this year saw several occasions where there was no stock at all for all of Canada. So we now have a limited amount of local inventory to improve coverage of important regions, and may eventually be adding a function for “premium numbers” for very rare area codes or similar which cost more to stock.

We have also been working in partnership with Snikket on a cross-platform SDK which we hope will make it easier for developers to build applications that integrate with the Jabber network without needing to be protocol or standards experts. Watch the chatroom and the Snikket blog for more information and demos.

There have also been several releases of the Cheogram Android app (latest is 2.13.0-1) with new features including:

  • Improved call connection stability
  • Verify DNSSEC and DANE and show status in UI
  • Show command UI on channels when there are commands to show
  • Show thread selector when starting a mention
  • Circle around thread selector
  • Several Android 14 specific fixes, including for dialler integration
  • Opening WebXDC from home screen even from a very old message

To learn what’s happening with JMP between newsletters, here are some ways you can find out:

Thanks for reading and have a wonderful rest of your week!

Featured Image

CertWatch

singpolyma@singpolyma.net

As you may have already seen, on October 21st, it was reported that a long-running, successful MITM (Machine-In-The-Middle) attack against jabber.ru had been detected. The nature of this attack was not specific to the XMPP protocol in any way, but it was of special interest to us as members of the XMPP community. This kind of attack relies on being able to present a TLS certificate which anyone trying to connect will accept as valid. In this case, it was done by getting a valid certificate from Let’s Encrypt.

When it comes to mitigation strategies for client-to-server connections, luckily there is already an excellent option called channel binding. Most XMPP clients and servers already have some amount of support for this technique, and in the wake of this attack, most are scrambling to make sure their implementations are complete. Many service providers have also added CAA DNS records which can prevent the very specific way this attack was executed from succeeding.

We’ve been hard at work on a different tool that can also help with defense-in-depth for this kind of situation. Ultimately, a MITM will use a different public key from the one the server uses, even if it is wrapped in a signed certificate declared as valid by a trustworthy authority (like Let’s Encrypt). If we know what key is seen when trying to connect, and we know what key the server administrator expects us to see, we can detect an ongoing MITM of this variety even when the certificate presented is valid. The tool we have developed is in early testing now. We call it CertWatch.

The premise is simple. The server administrator knows exactly what public/private keypair they are using (or can easily find out) and publishes this in DNSSEC-signed DNS records for our tool to find. The tool then periodically polls the XMPP server over Tor to see what certificate is presented. If the key in the certificate matches the key in the DNS zone, we know the session is not MITM’d (some caveats below). CertWatch checks the current setup of any domain entered, and if not yet declaring any keys, it displays setup instructions. It will either tell you to enable DNSSEC or it will tell you which DNS records to add. Note that these records are additive, so it is safe to add multiple sets when serving multiple domains from one host through SRV records. Once everything looks good, running a domain through CertWatch will display a success message and instructions for getting notified of any issues. It will then poll the domain periodically, and if any key mismatches are found, those subscribing to notifications will receive an alert.

Some tools change your key on every certificate renewal, which means you would have to update your zone setup every time your certificates renew. Other tools allow you to reuse existing keys and save some hassle, such as certbot with the --reuse-key option.

Caveats

If we did our polls from our main server IPs, it would be easy for any attacker to detect our probes and selectively disable the MITM attack for us, making themselves invisible. Probing over Tor gives CertWatch a different IP for every request and a traffic profile almost certainly consistent with the sort that many MITM attackers are going to want to inspect. This is not perfect, however, and it may be possible to fingerprint our probes in other ways to selectively MITM some traffic and ignore others. Just because our tool’s sessions were not MITM’d does not prove that no sessions are.

Anyone with physical access to the server may also scrape the actual certificates and keys off the disk, or use similar techniques in order to execute a MITM with exactly the same key the server operator expects and would use. The particular mitigation technique CertWatch helps administrators implement is ineffective against this. Rotating the key occasionally may help, but it really depends on the sophistication of the attacker and how much access they have.

Check it Out

So head over to CertWatch, enter your service domain, and let us know what you think.

Featured Image

Newsletter: Summer in Review

singpolyma@singpolyma.net

Hi everyone!

Welcome to the latest edition of your pseudo-monthly JMP update!

In case it’s been a while since you checked out JMP, here’s a refresher: JMP lets you send and receive text and picture messages (and calls) through a real phone number right from your computer, tablet, phone, or anything else that has a Jabber client.  Among other things, JMP has these features: Your phone number on every device; Multiple phone numbers, one app; Free as in Freedom; Share one number with multiple people.

Since our launch at the beginning of the summer, we’ve kept busy.  We saw some of you at the first FOSSY, which took place in July.  For those of you who missed it, the videos are out now.

Automatic refill for users of the data plan is in testing now.  That should be fully automated a bit later this month and will pave the way for the end of the waiting list, at least for existing JMP customers.

This summer also saw the addition of two new team members: welcome to Gnafu the Great who will be helping out with support, and Amolith, who will be helping out on the technical side.

There have also been several releases of the Cheogram Android app (latest is 2.12.8-2) with new features including:

  • Support for animated avatars
  • Show “hats” in the list of channel participants
  • An option to show related channels from the channel details area
  • Emoji and sticker autocomplete by typing ‘:’ (allows sending custom emoji)
  • Tweaks to thread UI, including no more auto-follow by default in channels
  • Optionally allow notifications for replies to your messages in channels
  • Allow selecting text and quoting the selection
  • Allow requesting voice when you are muted in a channel
  • Send link previews
  • Support for SVG images, avatars, etc.
  • Long press send button for media options
  • WebXDC importFiles and sendToChat support, allowing, for example, import and export of calendars from the calendar app
  • Fix Command UI in tablet mode
  • Manage permissions for channel participants with a dialog instead of a submenu
  • Ask if you want to moderate all recent messages by a user when banning them from a channel
  • Show a long streak of moderated messages as just one indicator

To learn what’s happening with JMP between newsletters, here are some ways you can find out:

Thanks for reading and have a wonderful rest of your week!

Featured Image

JMP is Launched and Out of Beta

singpolyma@singpolyma.net

JMP has been in beta for over six years, and today we are finally launching! With feedback and testing from thousands of users, our team has made improvements to billing, phone network compatibility, and also helped develop the Cheogram Android app which provides a smooth onboarding process, good Android integration, and phone-like UX for users of that platform. There is still a long road ahead of us, but with so much behind us we’re comfortable saying JMP is ready for launch, and that we know we can continue to work with our customers and our community for even better things in the future.  Check out our launch on Product Hunt today as well!

JMP’s pricing has always been “while in beta” so the first question this raises for many is: what will the price be now? The new monthly price for a customer’s first JMP phone number is now $4.99 USD / month ($6.59 CAD), but we are running a sale so that all customers will continue to pay beta pricing of $2.99 USD / month ($3.59 CAD) until the end of August. We are extending until that time the option for anyone who wishes to prepay for up to 3 years and lock-in beta pricing. Contact support if you are interested in the prepay option. After August, all accounts who have not pre-paid will be put on the new plan with the new pricing. Those who do pre-pay won’t see their price increase until the end of the period they pre-paid for.  The new plan will also include a multi-line discount, so second, third, etc JMP phone numbers will be $2.45 USD / month ($3.45 CAD) when they are set to all bill from the same balance.  The new plan will also finally have zero-rated toll free calling.  All other costs (per-minute costs, etc) remain the same, see the pricing page for details.

The account settings bot now has an option “Create a new phone number linked to this balance” so that you can get new numbers using your existing account credit and linked for billing to the same balance without support intervention.

Thanks so much to all of you who have helped us get this far.  There is lots more exciting stuff coming this year, and we are so thankful to have such a supportive community along the way with us.  Don’t forget we’ll be at FOSSY in July, and be sure to check out our launch on Product Hunt today as well.

Featured Image

Newsletter: Jabber ID Discovery, New Referral Codes

singpolyma@singpolyma.net

Hi everyone!

Welcome to the latest edition of your pseudo-monthly JMP update!

In case it’s been a while since you checked out JMP, here’s a refresher: JMP lets you send and receive text and picture messages (and calls) through a real phone number right from your computer, tablet, phone, or anything else that has a Jabber client.  Among other things, JMP has these features: Your phone number on every device; Multiple phone numbers, one app; Free as in Freedom; Share one number with multiple people.

It has been a while since we got a newsletter out, and lots has been happening as we race towards our launch.

For those who have experienced the issue with Google Voice participants not showing up properly in our MMS group texting stack, we have a new stack in testing right now.  Let support know if you want to try it out, it has been working well so far for those already using it.

If you check your account settings for the “refer a friend” option you will now see two kinds of referral code.  The list of one-time use codes remains the same as always: a free month for your friend, and a free month’s worth of credit for you if they start paying.  The new code up in the top is multi-use and you can post and share it as much as you like.  It provides credit equivalent to an additional month to anyone who uses it on sign up after their initial $15 deposit as normal, and then a free month’s worth of credit for you after that payment fully clears.

We mentioned before that much of the team will be present at FOSSY, and we can now reveal why: there will be a conference track dedicated to XMPP, which we are helping to facilitate!  Call for proposals ends May 14th. Sign up and come out this summer!

Quicksy Logo For quite some time now, customers have been asked while registering if they would like to enable others who know their phone number to discover their Jabber ID, to enable upgrading to end-to-end encryption, video calls, etc.  The first version of this feature is now live, and users of at least Cheogram Android and Movim can check the contact details of anyone they exchange SMS with to see if a Jabber ID is listed.  We are happy to announce that we have also partnered with Quicksy to allow discovery of anyone registered for their app or directory as well.

Tapbacks Jabber-side reactions are now translated where possible into the tapback pseudo-syntax recognized by many Android and iMessage users so that your reactions will appear in a native way to those users.  In Cheogram Android you can swipe to reply to a message and enter a single emoji as the reply to send a reaction/tapback.

Cheogram Android There have been two Cheogram Android releases since our last newsletter, with a third coming out today.  You no longer need to add a contact to send a message or initiate a call.  The app has seen the addition of moderation features for channel administrators, as well as respecting these moderation actions on display.  For offensive media arriving from other sources, in avatars, or just not moderated quickly enough, users also have the ability to permanently block any media they see from their device.

Cheogram Android has seen some new sticker-related features including default sticker packs and the ability to import any sticker pack made for signal (browse signalstickers.com to find more sticker packs, just tap “add to signal” to add them to Cheogram Android).

There are also brand-new features today in 2.12.1-5, including a new onboarding flow that allows new users to register and pay for JMP before getting a Jabber ID, and then set up their very own Snikket instance all from within the app.  This flow also features some new introductory material about the Jabber network which we will continue to refine over time:

Welcome to Cheogram Android Screenshot How the Jabber network works Screenshot Welcome Screen Screenshot

Notifications about new messages now use the conversation style in Android.  This means that you can set seperate priority and sounds per-conversation at the OS level on new enough version of Android.  There is also an option in each conversation’s menu to add that conversation to your homescreen, something that has always been possible with the app but hopefully this makes it more discoverable for some.

For communities organizing in Jabber channels, sometimes it can be useful to notify everyone present about a message.  Cheogram Android now respects the attention element from members and higher in any channel or group chat.  To send a message with this priority attached, start the message body with @here (this will not be included in the actual message people see).

WebXDC Logo

This release also brings an experimental prototype supporting WebXDC.  This is an experimental specification to allow developers to ship mini-apps that work inside your chats.  Take any *.xdc file and send it to a contact or group chat where everyone uses Cheogram Android and you can play games, share notes, shopping lists, calendars, and more.  Please come by the channel to discuss the future of this technology on the Jabber network with us.

To learn what’s happening with JMP between newsletters, here are some ways you can find out:

Thanks for reading and have a wonderful rest of your week!

Featured Image

Verify Google Play App Purchase on Your Server

singpolyma@singpolyma.net

We are preparing for the first-ever Google Play Store launch of Cheogram Android as part of JMP coming out of beta later this year.  One of the things we wanted to “just work” for Google Play users is to be able to pay for the app and get their first month of JMP “bundled” into that purchase price, to smooth the common onboarding experience.  So how do the JMP servers know that the app communicating with them is running a version of the app bought from Google Play as opposed to our builds, F-Droid’s builds, or someone’s own builds?  And also ensure that this person hasn’t already got a bundled month before?  The documentation available on how to do this is surprisingly sparse, so let’s do this together.

Client Side

Google publishes an official Licensing Verification Library for communicating with Google Play from inside an Android app to determine if this install of the app can be associated with a Google Play purchase.  Most existing documentation focuses on using this library, however it does not expose anything in the callbacks other than “yes license verified” or “no, not verified”.  This can allow an app to check if it is a purchased copy itself, but is not so useful for communicating that proof onward to a server.  The library also contains some exciting snippets like:

// Base64 encoded -
// com.android.vending.licensing.ILicensingService
// Consider encoding this in another way in your
// code to imp rove security
Base64.decode(
    "Y29tLmFuZHJvaWQudmVuZGluZy5saWNlbnNpbmcuSUxpY2Vuc2luZ1NlcnZpY2U=")))

Which implies that they expect developers to fork this code to use it.  Digging in to the code we find in LicenseValidator.java:

public void verify(PublicKey publicKey, int responseCode, String signedData, String signature)

Which looks like exactly what we need: the actual signed assertion from Google Play and the signature!  So we just need a small patch to pass those along to the callback as well as the response code currently being passed.  Then we can use the excellent jitpack to include the forked library in our app:

implementation 'com.github.singpolyma:play-licensing:1c637ea03c'

Then we write a small class in our app code to actually use it:

import android.content.Context;
import com.google.android.vending.licensing.*;
import java.util.function.BiConsumer;

public class CheogramLicenseChecker implements LicenseCheckerCallback {
    private final LicenseChecker mChecker;
    private final BiConsumer mCallback;

    public CheogramLicenseChecker(Context context, BiConsumer<String, String> callback) {
        mChecker = new LicenseChecker(  
            context,  
            new StrictPolicy(), // Want to get a signed item every time  
            context.getResources().getString(R.string.licensePublicKey)  
        );
        mCallback = callback;
    }

    public void checkLicense() {
        mChecker.checkAccess(this);
    }

    @Override
    public void dontAllow(int reason) {
        mCallback.accept(null, null);
    }

    @Override
    public void applicationError(int errorCode) {
        mCallback.accept(null, null);
    }

    @Override
    public void allow(int reason, ResponseData data, String signedData, String signature) {
        mCallback.accept(signedData, signature);
    }
}

Here we use the StrictPolicy from the License Verification Library because we want to get a fresh signed data every time, and if the device is offline the whole question is moot because we won’t be able to contact the server anyway.

This code assumes you put the Base64 encoded licensing public key from “Monetisation Setup” in Play Console into a resource R.string.licensePublicKey.

Then we need to communicate this to the server, which you can do whatever way makes sense for your protocol; with XMPP we can easily add custom elements to our existing requests so:

new com.cheogram.android.CheogramLicenseChecker(context, (signedData, signature) -> {
    if (signedData != null && signature != null) {
        c.addChild("license", "https://ns.cheogram.com/google-play").setContent(signedData);
        c.addChild("licenseSignature", "https://ns.cheogram.com/google-play").setContent(signature);
    }

    xmppConnectionService.sendIqPacket(getAccount(), packet, (a, iq) -> {
        session.updateWithResponse(iq);
    });
}).checkLicense();

Server Side

When trying to verify this on the server side we quickly run into some new issues.  What format is this public key in?  It just says “public key” and is Base64 but that’s about it.  What signature algorithm is used for the signed data?  What is the format of the data itself?  Back to the library code!

private static final String KEY_FACTORY_ALGORITHM = "RSA";
…
byte[] decodedKey = Base64.decode(encodedPublicKey);
…
new X509EncodedKeySpec(decodedKey)

So we can see it is an X509 related encoded, and indeed turns out to be Base64 encoded DER.  So we can run this:

echo "BASE64_STRING" | base64 -d | openssl rsa -pubin -inform der -in - -text

to get the raw properties we might need for any library (key size, modulus, and exponent).  Of course, if your library supports parsing DER directly you can also use that.

import java.security.Signature;
…
private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
…
Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
sig.initVerify(publicKey);
sig.update(signedData.getBytes());

Combined with the java documentation we can thus say that the signature algoritm is PKCS#1 padded RSA with SHA1.

And finally:

String[] fields = TextUtils.split(mainData, Pattern.quote("|"));
data.responseCode = Integer.parseInt(fields[0]);
data.nonce = Integer.parseInt(fields[1]);
data.packageName = fields[2];
data.versionCode = fields[3];
// Application-specific user identifier.
data.userId = fields[4];
data.timestamp = Long.parseLong(fields[5]);

The format of the data, pipe-seperated text. The main field of interest for us is userId which is (as it says in a comment) “a user identifier unique to the <application, user> pair”. So in our server code:

import Control.Error (atZ)
import qualified Data.ByteString.Base64 as Base64
import qualified Data.Text as T
import Crypto.Hash.Algorithms (SHA1(SHA1))
import qualified Crypto.PubKey.RSA as RSA
import qualified Crypto.PubKey.RSA.PKCS15 as RSA
import qualified Data.XML.Types as XML

googlePlayUserId
    | googlePlayVerified = (T.split (=='|') googlePlayLicense) `atZ` 4
    | otherwise = Nothing
googlePlayVerified = fromMaybe False $ fmap (\pubKey ->
    RSA.verify (Just SHA1) pubKey (encodeUtf8 googlePlayLicense)
        (Base64.decodeLenient $ encodeUtf8 googlePlaySig)
    ) googlePlayPublicKey
googlePlayLicense = mconcat $ XML.elementText
    =<< XML.isNamed (s"{https://ns.cheogram.com/google-play}license")
    =<< XML.elementChildren payload
googlePlaySig = mconcat $ XML.elementText
    =<< XML.isNamed (s"{https://ns.cheogram.com/google-play}licenseSignature")
    =<< XML.elementChildren payload

We can then use the verified and extracted googlePlayUserId value to check if this user has got a bundled month before and, if not, to provide them with one during signup.

Creative Commons Attribution ShareAlike