parsing asn.1 with bouncycastle - decode

I have some asn.1 encoded data. I have some success decoding it with bouncycastle but I hit the wall with quite complex example. It must be doable, but can't get further, hope you could help me, here is the example data:
A1 81 A9 02 02 1C 1F 02 01 15 30 81 9F 55 02 01 14 A0 81 98 A4 81 95 6B 10 30 0E 80 04 00 00 01 1F A1 06 30 04 80 02 33 32 63 06 30 04 80 02 33 32 61 0E 30 0C 80 0A 30 32 32 33 37 38 33 36 31 30 62 06 30 04 80 02 33 32 64 02 87 00 6B 13 30 11 80 04 00 00 01 1F A1 09 30 07 80 05 23 23 37 30 30 4E 01 02 0A 01 16 67 12 30 10 A3 0E 81 0C 2B 34 38 32 32 33 37 38 33 36 31 30 68 0F 30 0D A3 0B 82 09 32 32 38 36 35 33 33 39 38 65 09 30 07 80 05 23 23 37 30 30 7E 12 A0 10 18 0E 32 30 31 36 30 33 30 32 32 32 30 31 33 36
CSTA Browser decoded:
rOSE.roiv-apdu
{ -- SEQUENCE --
invokeID = 7199,
operation-value = 21 (cSTAEventReport),
argument
{ -- SEQUENCE --
crossRefIdentifier = '01 14'H,
eventSpecificInfo.callControlEvents.delivered
{ -- SEQUENCE --
connection.both
{ -- SEQUENCE --
callID = '00 00 01 1F'H,
deviceID.staticID
{ -- SEQUENCE --
deviceIdentifier.dialingNumber = "32" '33 32'H
}
},
alertingDevice.deviceIdentifier
{ -- SEQUENCE --
deviceIdentifier.dialingNumber = "32" '33 32'H
},
callingDevice.deviceIdentifier
{ -- SEQUENCE --
deviceIdentifier.dialingNumber = "0223783610" '30 32 32 33 37 38 33 36 31 30'H
},
calledDevice.deviceIdentifier
{ -- SEQUENCE --
deviceIdentifier.dialingNumber = "32" '33 32'H
},
lastRedirectionDevice.notKnown NULL,
originatingNIDConnection.both
{ -- SEQUENCE --
callID = '00 00 01 1F'H,
deviceID.staticID
{ -- SEQUENCE --
deviceIdentifier.dialingNumber = "##700" '23 23 37 30 30'H
}
},
localConnectionInfo = 2 (alerting),
cause = 22 (newCall),
networkCallingDevice.deviceIdentifier
{ -- SEQUENCE --
deviceIdentifier.explicitPublic.international = "+48223783610" '2B 34 38 32 32 33 37 38 33 36 31 30'H
},
networkCalledDevice.deviceIdentifier
{ -- SEQUENCE --
deviceIdentifier.explicitPublic.national = "228653398" '32 32 38 36 35 33 33 39 38'H
},
associatedCallingDevice.deviceIdentifier
{ -- SEQUENCE --
deviceIdentifier.dialingNumber = "##700" '23 23 37 30 30'H
},
extensions
{ -- SEQUENCE --
security
{ -- SEQUENCE --
timestamp = "20160302220136" '32 30 31 36 30 33 30 32 32 32 30 31 33 36'H
}
}
}
}
}
bouncycastle's dump produces something like that:
00 AC A1 81 A9 02 02 5F B9 02 01 15 30 81 9F 55 02 01 91 A0 81 98 A4 81 95 6B 10 30 0E 80 04 00 00 03 98 A1 06 30 04 80 02 33 32 63 06 30 04 80 02 33 32 61 0E 30 0C 80 0A 30 32 32 33 37 38 33 36 31 30 62 06 30 04 80 02 33 32 64 02 87 00 6B 13 30 11 80 04 00 00 03 98 A1 09 30 07 80 05 23 23 37 30 30 4E 01 02 0A 01 16 67 12 30 10 A3 0E 81 0C 2B 34 38 32 32 33 37 38 33 36 31 30 68 0F 30 0D A3 0B 82 09 32 32 38 36 35 33 33 39 38 65 09 30 07 80 05 23 23 37 30 30 7E 12 A0 10 18 0E 32 30 31 36 30 33 30 34 31 35 32 32 34 30
buf:Tagged [1] IMPLICIT
Sequence
Integer(24505)
Integer(21)
DER Sequence
DER ApplicationSpecific[21] (0191)
Tagged [0]
Tagged [4] IMPLICIT
Sequence
DER ApplicationSpecific[11]
Sequence
Tagged [0] IMPLICIT
DER Octet String[4]
Tagged [1]
DER Sequence
Tagged [0] IMPLICIT
DER Octet String[2]
DER ApplicationSpecific[3]
Sequence
Tagged [0] IMPLICIT
DER Octet String[2]
DER ApplicationSpecific[1]
Sequence
Tagged [0] IMPLICIT
DER Octet String[10]
DER ApplicationSpecific[2]
Sequence
Tagged [0] IMPLICIT
DER Octet String[2]
DER ApplicationSpecific[4]
Tagged [7] IMPLICIT
DER Octet String[0]
DER ApplicationSpecific[11]
Sequence
Tagged [0] IMPLICIT
DER Octet String[4]
Tagged [1]
DER Sequence
Tagged [0] IMPLICIT
DER Octet String[5]
DER ApplicationSpecific[14] (02)
DER Enumerated(22)
DER ApplicationSpecific[7]
Sequence
Tagged [3]
Tagged [1] IMPLICIT
DER Octet String[12]
DER ApplicationSpecific[8]
Sequence
Tagged [3]
Tagged [2] IMPLICIT
DER Octet String[9]
DER ApplicationSpecific[5]
Sequence
Tagged [0] IMPLICIT
DER Octet String[5]
DER ApplicationSpecific[30]
Tagged [0]
GeneralizedTime(20160304152240GMT+01:00)
and I am trying to parse it:
protected void parse() {
logger.trace("Executing parse()");
try {
ASN1InputStream input = new ASN1InputStream(asn1Data);
ASN1Primitive p;
if ((p = input.readObject()) != null) {
ASN1TaggedObject o1 = ASN1TaggedObject.getInstance(p);
ASN1Sequence s1 = ASN1Sequence.getInstance(o1.getObject());
invokeID = Integer.parseInt(s1.getObjectAt(0).toString());
operationValue = Integer.parseInt(s1.getObjectAt(1).toString());
DERSequence ders = (DERSequence) DERSequence.getInstance(s1.getObjectAt(2));
DERApplicationSpecific das = (DERApplicationSpecific) ders.getObjectAt(0);
crossRefIdentifier = das.getContents();
//here are some experiments, but can't get the right objects I could parse / walk through
ASN1TaggedObject o2 = ASN1TaggedObject.getInstance(ders.getObjectAt(1));
DERTaggedObject dto = (DERTaggedObject) o2.getObject();
ASN1Sequence s2 = ASN1Sequence.getInstance(dto.getObject());
DERApplicationSpecific das1 = (DERApplicationSpecific) s2.getObjectAt(0);
ASN1Sequence s3 = (ASN1Sequence) das1.getObject();
}
} catch (Exception ex) {
logger.warn("exception while parsing ASN1 data", ex);
}
}
as you can see I've been able to decode some basic tags (ie. invokeID, operation-value and crossRefIdentifier) but can't get deeper in the tree (callId, callingNumber ...).
If you have experience in that matter I would appreciate your help.

It is a very complex ASN.1 object. There are several places where it could go wrong with another sample due to structure differences. Here is an example.
using System;
using System.Collections;
using System.IO;
using Org.BouncyCastle.Asn1;
namespace Asn1ParseBouncy
{
class Program
{
static void Main(string[] args)
{
var bytes2Parse = File.ReadAllBytes(#"c:\a.req");
// (0,169) CONTEXT SPECIFIC(1)
DerTaggedObject rootObj = (DerTaggedObject)Asn1Object.FromByteArray(bytes2Parse);
if (rootObj.TagNo == 1)
{
ParseAtRootLevel(rootObj);
}
else
{
throw new Exception("Expected Tag number to be 1");
}
}
private static void ParseAtRootLevel(DerTaggedObject rootObj)
{
// SEQUENCE under CONTEXT SPECIFIC(1)
var seq = (Asn1Sequence)rootObj.GetObject();
IEnumerator e = seq.GetEnumerator();
bool hasNext;
hasNext = e.MoveNext();
// (3,2) INTEGER -> invokeID
{
Asn1Encodable obj;
if (!hasNext)
{
throw new Exception("more entries expected in sequence");
}
obj = (Asn1Encodable)e.Current;
// TODO: put in a property of class that represents whole ASN.1 message
var invokeID = DerInteger.GetInstance(obj);
hasNext = e.MoveNext();
}
// (7,1) INTEGER -> operation-value
{
Asn1Encodable obj;
if (!hasNext)
{
throw new Exception("more entries expected in sequence");
}
obj = (Asn1Encodable)e.Current;
// TODO: put in a property of class that represents whole ASN.1 message
var operationValue = DerInteger.GetInstance(obj);
hasNext = e.MoveNext();
}
// (10,159) SEQUENCE -> argument
{
Asn1Encodable obj;
if (!hasNext)
{
throw new Exception("more entries expected in sequence");
}
obj = (Asn1Encodable)e.Current;
var argumentSeq = Asn1Sequence.GetInstance(obj);
// argumentData is parsed asn.1 object - argument
var argumentData = ParseArgumentData(argumentSeq);
hasNext = e.MoveNext();
}
if (hasNext)
{
throw new Exception("no more entries expected in sequence");
}
}
private static object ParseArgumentData(Asn1Sequence argumentSeq)
{
IEnumerator e = argumentSeq.GetEnumerator();
bool hasNext;
hasNext = e.MoveNext();
// (13,2) APPLICATION (21) -> crossRefIdentifier
{
Asn1Encodable obj;
if (!hasNext)
{
throw new Exception("more entries expected in sequence");
}
obj = (Asn1Encodable)e.Current;
var crossRefIdentifierAppSpecific = (DerApplicationSpecific)obj;
if (crossRefIdentifierAppSpecific.ApplicationTag != 21)
throw new Exception("Expected application tag 21");
// TODO: put in a property of class that represents whole ASN.1 message
var crossRefIdentifier = crossRefIdentifierAppSpecific.GetContents();
hasNext = e.MoveNext();
}
// (17,152) CONTEXT SPECIFIC (0) -> eventSpecificInfo.callControlEvents.delivered
{
Asn1Encodable obj;
if (!hasNext)
{
throw new Exception("more entries expected in sequence");
}
obj = (Asn1Encodable)e.Current;
var eventSpecificInfo = ((DerTaggedObject)obj);
if (eventSpecificInfo.TagNo != 0)
throw new Exception("Expected Context specific tag number to be 0");
// TODO: put in a property of class that represents whole ASN.1 message
var eventSpecificInfoData = ParseEventSpecificInfo(eventSpecificInfo);
hasNext = e.MoveNext();
}
if (hasNext)
{
throw new Exception("no more entries expected in sequence");
}
// TODO: return parsed values in some class
return null;
}
private static object ParseEventSpecificInfo(DerTaggedObject obj)
{
// still (17,152) CONTEXT SPECIFIC (0)
var connectionBothData = ParseConnectionBoth(obj);
return connectionBothData;
}
private static object ParseConnectionBoth(DerTaggedObject connectionBoth)
{
// (20,149) CONTEXT SPECIFIC (4)->connection.both
var connectionBothTagged = (DerTaggedObject)connectionBoth.GetObject();
if (connectionBothTagged.TagNo != 4)
throw new Exception("Expected Context specific tag number to be 4");
// Sequence under (20,149) CONTEXT SPECIFIC (4) -> connection.both
var connectionBothSeq = (DerSequence)connectionBothTagged.GetObject();
IEnumerator e = connectionBothSeq.GetEnumerator();
bool hasNext;
hasNext = e.MoveNext();
// callID
{
Asn1Encodable obj;
if (!hasNext)
{
throw new Exception("more entries expected in sequence");
}
obj = (Asn1Encodable)e.Current;
// (23,16) APPLICATION (11)
// TODO: put in a property of class that represents whole ASN.1 message
var callIDTagged = (DerApplicationSpecific)obj;
if (callIDTagged.ApplicationTag != 11)
throw new Exception("Expected tag number 11");
// (25,14) SEQUENCE
var callIdSeq = callIDTagged.GetObject().GetDerEncoded();
// TODO: parse CallIdSeq -> (27,4) ContextSpecific(0), (33,6) ContextSpecific(1)
hasNext = e.MoveNext();
}
// TODO: continue with (41,6) Application (3)
// TODO: continue with (49,14) Application (1)
// TODO: continue with (65,6) Application (2)
// etc.
// TOOD: return something useful
throw new NotImplementedException();
}
}
}
I placed notes in the code sample according to dump from ASN.1 Editor
You could make your life a lot easier if you had ASN.1 definition of the object. With help from Binary Notes you could generate classes to parse ASN.1 data object.

Related

Parsing bytes as BCD with Indy C++ Builder

I am trying to parse the length of a message received. The length is in BCD. When I use ReadSmallInt(), I get a reading interpreted as a hex value, not as BCD.
So, if I have a message like this:
00 84 60 00 00 00 19 02 10 70 38 00 00 0E C0 00
00 16 45 93 56 00 01 79 16 62 00 00 00 00 00 00
08 00 00 00 00 02 10 43 02 04 02 35 31 35 31 35
31 35 31 35 31 35 31 53 41 4C 45 35 31 30 30 31
32 33 34 35 36 37 38 31 32 33 34 35 36 37 38 39
30 31 32 33
I am expecting ReadSmallInt() to return 84, but instead it is returning 132, which is correct if you are reading a hex value instead of a BCD one.
According to this answer, ReadSmallInt() reads BCD, as in the examples it gets 11 and 13 (BCD) as lengths instead of 17 and 19 (hex).
I have fixed this with duct tape, but is there a more elegant way?
int calculated_length;
// getting the length in Hexa
calculated_length = AContext->Connection->IOHandler->ReadSmallInt();
// converting from hex binary to hex string
UnicodeString bcdLength = UnicodeString().sprintf(L"%04x", calculated_length);
// converting from hex string to int
calculated_length = bcdLength.ToInt();
ucBuffer.Length = calculated_length -2;
AContext->Connection->IOHandler->ReadBytes(ucBuffer, calculated_length - 2);
According to this answer, ReadSmallInt reads BCD
That is incorrect. You have misinterpreted what that answer is saying. NOTHING in that answer indicates that ReadSmallInt() reads in a Binary Coded Decimal, because it doesn't, as Indy DOES NOT support reading/writing BCDs at all. ReadSmallInt() simply reads in 2 bytes and returns them as-is as a 16-bit decimal integer (swapping the byte order, if needed). So, if you need to read in a BCD instead, you will have to read in the bytes and then parse them yourself. Or find a BCD library to handle it for you.
If you re-read that other question again more carefully, in the 2 examples it gives:
24 24 00 11 12 34 56 FF FF FF FF 50 00 8B 9B 0D 0A
24 24 00 13 12 34 56 FF FF FF FF 90 02 00 0A 8F D4 0D 0A
The 3rd and 4th bytes represent the message lengths (x00 x11 and x00 x13, respectively). As 16-bit values in network byte order, they represent decimal integers 17 and 19, respectively. And if you count the bytes present, you will see those values are the correct byte lengths of those messages. So, there are no BCDs involved here.
That is different than your example. Bytes x00 x84 in network byte order represent decimal integer 132. But your message is 84 bytes in size, not 132 bytes. So clearly the bytes x00 x84 DO NOT represent a 16-bit decimal value, so ReadSmallInt() is the wrong method to use in the first place.
In your "duct tape" code, you are taking the decimal value that ReadSmallInt() returns (132), converting it to a hex string ('0084'), and then parsing that to a decimal value (84). There is no method in Indy that will do that kind of conversion for you.
That "works" in your case, but whether or not that is the correct conversion to perform, I could not say for sure as you have not provided any details about the protocol you are dealing with. But, if you think the bytes represent a BCD then you should interpret the bytes in terms of an actual BCD.
In a packed BCD, a byte can represent a 2-digit number. In this case, byte x84 (10000100b) contains two nibbles 1000b (8) and 0100b (4), thus put together they form decimal 84, which is calculated as follows:
BYTE b = 0x84;
int len = (int((b >> 4) & 0x0F) * 10) + int(b & 0x0F);
Now, how that extends to multiple bytes in a BCD, I'm not sure, as my experience with BCDs is very limited. But, you are going to have to figure that out if you need to handle message lengths greater than 99 bytes, which is the highest decimal that a single BCD byte can represent.

Get non html content from rvest_session object

I am trying to get a text file from a URL. From the browser, its fairly simple. I just have to "save as" the from the URL and I get the file I want. At first, i had some trouble logging in using rvest (see [https://stackoverflow.com/questions/66352322/how-to-get-txt-file-from-password-protected-website-jsp-in-r][1])(I uploaded a couple of probably useful picture there). When I use the following code:
fileurl <- "http://www1.bolsadecaracas.com/esp/productos/dinamica/downloadDetail.jsp?symbol=BNC&dateFrom=20190101&dateTo=20210101&typePlazo=nada&typeReg=T"
session(fileurl)
I get the following (note how I am redirected to a different URL, as happens in the browser when you try to get to the fileurl without first logging in):
<session> http://www1.bolsadecaracas.com/esp/productos/dinamica/downloadDetail.jsp?symbol=BNC&dateFrom=20190101&dateTo=20210101&typePlazo=nada&typeReg=T
Status: 200
Type: text/html; charset=ISO-8859-1
Size: 84
I managed to log in using the following code:
#Define URLs
loginurl <- "http://www1.bolsadecaracas.com/esp/usuarios/customize.jsp"
fileurl <- "http://www1.bolsadecaracas.com/esp/productos/dinamica/downloadDetail.jsp?symbol=BNC&dateFrom=20190101&dateTo=20210101&typePlazo=nada&typeReg=T"
#Create session
pgsession <- session(loginurl)
pgform<-html_form(pgsession)[[1]] #Get form
#Create a fake submit button as form does not have one
fake_submit_button <- list(name = NULL,
type = "submit",
value = NULL,
checked = NULL,
disabled = NULL,
readonly = NULL,
required = FALSE)
attr(fake_submit_button, "class") <- "input"
pgform[["fields"]][["submit"]] <- fake_submit_button
#Create and submit filled form
filled_form<-html_form_set(pgform, login="******", passwd="******")
session_submit(pgsession, filled_form)
#Jump to new url
loggedsession <- session_jump_to(pgsession, url = fileurl)
#Output
loggedsession
It seems to me that the login was succesful, as the session output is the exact same size than the .txt file when I download it and I am no longer redirected. See the output.
<session> http://www1.bolsadecaracas.com/esp/productos/dinamica/downloadDetail.jsp?symbol=BNC&dateFrom=20190101&dateTo=20210101&typePlazo=nada&typeReg=T
Status: 200
Type: text/plain; charset=ISO-8859-1
Size: 32193
However, whenever I try to extract the content of the session with read_html() or the like, i get the following error: "Error: Page doesn't appear to be html.". I dont know if it has anything to do with the "Type: text/plain" of the session.
When I run
loggedsession[["response"]][["content"]]
I get
[1] 0d 0a 0d 0a 0d 0a 0d 0a 0d 0a 7c 30 32 2f 30 31 2f 32 30 31 39 7c 52 7c 31 34 2c 39 30 7c 31 35 2c
[34] 30 30 7c 31 37 2c 38 33 7c 31 33 2c 35 30 7c 39 7c 31 33 2e 35 33 33 7c 32 30 33 2e 30 36 30 2c 31
[67] 39 7c 0a 7c 30 33 2f 30 31 2f 32 30 31 39 7c 52 7c 31 35 2c 30 30 7c 31 37 2c 39 38 7c 31 37 2c 39
Any help on how to extract the text file??? Would be greatly appreciated.
PS:
At one point, just playing with functions I managed to get something that would have worked with httr::: GET(fileurl). That was after playing with rvest functions and managing to log in. However, after closing and opening RStudio I was not able to get the same output with that function.
Because rvest uses httr package internally, you can use the httr and base to save your file. The key to the solution is that your response (in terms of the httr package) is in the session object:
library(rvest)
library(httr)
httr::content(loggedsession$response, as = "text") %>%
cat(file = "your_file.txt")
More importantly, if your file were binary (e.g. a zip archive), you would have to do:
library(rvest)
library(httr)
httr::content(loggedsession$response, as = "raw") %>%
writeBin(con = 'your_file.zip')

Read an incoming HEX packet from Serial

I'm coding a on an arduino and I am communicating with an other device in HEX. I would like to know how to read the data he sends me.
I am sending a HEX packet (everything good here, no problem)
//Ask for Data
Serial.write(askData, sizeof(askData));
After this I will receive data (in HEX). I need to store it all to use it later. The only thing I know is that it will end with a "16". I dont know the length of the packet in advance.
Here is an example or packet that I can reveive :
68 4E 4E 68 08 09 72 90 90 85 45 68 50 49 06 19 00 00 00
0C 14 02 00 00 00 8C 10 12 35 02 00 00 0B 3B 00 00 00 8C
20 14 02 00 00 00 8C 30 14 00 00 00 00 04 6D 2F 09 61 24
4C 14 02 00 00 00 42 6C 5F 2C 42 EC 7E 7F 2C 0A 92 2A 00
10 0A 92 2B 00 10 39 16
Can someone help me please ?
This arduino example slightly modified:
/* reserve 200 bytes for the inputString:
* assuming a maximum of 200 bytes
*/
uint8_t inputString[200]; // a String to hold incoming data
int countInput = 0;
bool stringComplete = false; // whether the string is complete
void setup() {
// initialize serial:
Serial.begin(115200);
}
void loop() {
// print the string when 0x16 arrives:
if (stringComplete) {
for (int i=0; i<countInput; i++) {
Serial.print(inputString[i], HEX);
Serial.print(" ");
}
// clear the string:
countInput = 0;
stringComplete = false;
}
}
/*
SerialEvent occurs whenever a new data comes in the hardware serial RX. This
routine is run between each time loop() runs, so using delay inside loop can
delay response. Multiple bytes of data may be available.
*/
void serialEvent() {
while (Serial.available()) {
// get the new byte:
inputString[countInput] = (uint8_t)Serial.read();
// if the incoming character is '0x16', set a flag so the main loop can
// do something about it:
if (inputString[countInput] == 0x16) {
stringComplete = true;
}
countInput++;
}
}

Reverse engineering a file encryption (most likely XOR)

Im trying to reverse engineer a file format which is encrypted. Most likely it uses an XOR encryption. I can create encrypted files with known plaintext, which I analyzed:
enc 71 8d 7e 84 29 20 b8 cb 6c ed bb 8a 62 a1
dec 74 68 69 73 20 69 73 20 61 20 74 65 73 74
xor 05 e5 17 f7 09 49 cb eb 0d cd cf ef 11 d5
txt t h i s i s a t e s t
enc 61 ad be 84 29 20 b8 cb 6c ed bb 8a 62 a1
dec 64 68 69 73 20 69 73 20 61 20 74 65 73 74
xor 05 c5 d7 f7 09 49 cb eb 0d cd cf ef 11 d5
txt d h i s i s a t e s t
enc 62 a5 ae a4 e9 a0 b8 cb 6c ed bb 8a 62 a1
dec 67 68 69 73 20 69 73 20 61 20 74 65 73 74
xor 05 cd c7 d7 c9 c9 cb eb 0d cd cf ef 11 d5
txt g h i s i s a t e s t
It is obvious that the original text is part of the encryption. The first byte of the key is always 05. The second byte of the key can be calculated like this:
(enc1 + dec1) OR xor1
The rather low entropy of the key implies a similar rule for the other key-bytes.
Any ideas?
You almost got it!
The key's byte at the m position is given by :
km = [(en + dn) ^ kn] | secret
where :
en is the previous encrypted byte
dn is the previous plain text byte
kn is the previous key byte (k0 = 5)
secret is an arbitrary number starting at 5 and incremented by 2 every two turns
^ is the xor operator
| is the or operator
A simple C# key generator :
namespace Sample.CustomEncrypt {
using System.Collections.Generic;
using System.Text;
class Program {
static void Main() {
var key1 = GenerateKey("this is a test");
var key2 = GenerateKey("dhis is a test");
var key3 = GenerateKey("ghis is a test");
}
public static byte[] GenerateKey(string input) {
var plain = Encoding.UTF8.GetBytes(input);
var secret = 5;
var key = new List<byte> {
0x05
};
for (var i = 0; i < plain.Length - 1; i++) {
var dn = plain[i];
var kn = key[i];
var en = (byte)(dn ^ kn);
var km = (byte)(((dn + en) ^ kn) | secret);
key.Add(km);
if (i % 2 == 0) {
secret += 2;
}
}
return key.ToArray();
}
}
}
PS: As pointed out by Eugene you should post on Reverse Engineering or Cryptography next time.

Which standards are SS7 MAP Tags defined in?

Can anyone give me information on which standard contains MAP Tags - sm-RP-UI?
04 1a - sm-RP-UI
24 - TP-RP/UDHI/SRI/MMS/MTI
0b - length
91 26 18 18 55 32 f7 - TP-Originating-Address
00 - TP-PID
00 - TP-DCS
90 40 02 91 61 42 82 - TP-Service-Centre-Time-Stamp
07 - TP-User-Data-Length: (7) depends on Data-Coding-Scheme
ca f0 3a 2c a7 87 01 - TP-User-Data
The details are needed for coding and I'd like to know which standard they are in. I have been looking in GSM 29.002, GSM 23.040, and GSM 24.011 and I haven't found them.
Any help would be greatly appreciated,
Thank you.
The SMTL PDUs are defined in 3GPP TS 23.040 - Technical realization of the Short Message Service (SMS)
More specifically:
04 1a
This is ASN.1 tag a length (OCTET STRING). Since you say this is sm-RP-UI
it would be the SignalInfo ASN.1 type defined in 3GPP TS 29.002
used with labels sm-RP-UI on different MAP operations.
24
First thing to look here are the last two bits (TP-Message-Type-Indicator: 9.2.3.1 of 23.040)
Since you have H'24 -> B'00100100. This is an SMS-DELIVER (SC to MS)
SMS-DELIVER (9.2.2.1) contains
TP-Message-Type-Indicator (TP-MTI on 9.2.3.1) (bit 0-1 --> 00)
TP-More-Messages-To-Send (TP-MMS on 9.2.3.2) (bit 2 --> 1: "No more messages are waiting for the MS in this SC
TP-Status-Report-Indication (TP-SRI on 9.2.3.4) (bit 5 --> 1: "A status report shall be returned to the SME")
TP-User-Data_Header-Indicator (TP-UDHI on 9.2.3.23) (bit 6 -> 0: "The TP-UD field contains only the short message")
TP-Reply-Path (TP-RP on 9.2.3.17) (bit 7 -> 0: "Not set")
0b 91 26 18 18 55 32 f7
TP-Originating-Address (TP-OA 9.2.3.7 - Address fields in 9.1.2.5) that works:
** Address-Length: H'B = D'11 (not this is in semi-octets)
** Type-of-Address: H'91=B'10010001 with Type-of-Nuymber (B'001: International number) and Numbering-Plan (B'0001: ISDN/E.164)
** Address-Value: BCD: 62818155327 (F is filler)
00
TP-Protocol-Identifier (TP-PID 9.2.3.9)
00
TP-Data-Coding-Scheme (TP-DCS 9.2.3.10)
90 40 02 91 61 42 82
TP-Service-Centre-Time-Stamp (TP-SCTS 9.2.3.11) 2009/04/20 19:16:24
07
TP-User-Data-Length (TP-UDL 9.2.3.16)
ca f0 3a 2c a7 87 01
* TP-User-Data (TP-UD 9.2.3.24)

Resources