Sending a byte [] over javacard apdu - digital-signature

I send a byte [] from the host application to the javacard applet. But when I try to retrieve it as byte [] via the command buffer[ISO7816.OFFSET_CDATA], I am told that I cannot convert byte to byte[]. How can I send a byte [] via command APDU from the host application and retrieve it as byte[] on the other end (javacard applet). It appears buffer[ISO7816.OFFSET_CDATA] returns byte. See my comments on where the error occurs.
My idea is as follows:
The host application sends challenge as a byte [] to be signed by the javacard applet. Note that the signature requires the challenge to be a byte []. The javacard signs as follows:
private void sign(APDU apdu) {
if(!pin.isValidated()) ISOException.throwIt(SW_PIN_VERIFICATION_REQUIRED);
else{
byte [] buffer = apdu.getBuffer();
byte numBytes = buffer[ISO7816.OFFSET_LC];
byte byteRead =(byte)(apdu.setIncomingAndReceive());
if ( ( numBytes != 20 ) || (byteRead != 20) )
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
byte [] challenge = buffer[ISO7816.OFFSET_CDATA];// error point cannot convert from byte to byte []
byte [] output = new byte [64];
short length = 64;
short x =0;
Signature signature =Signature.getInstance(Signature.ALG_RSA_SHA_PKCS1, false);
signature.init(privKey, Signature.MODE_SIGN);
short sigLength = signature.sign(challenge, offset,length, output, x); // challenge must be a byte []
//This sequence of three methods sends the data contained in
//'serial' with offset '0' and length 'serial.length'
//to the host application.
apdu.setOutgoing();
apdu.setOutgoingLength((short)output.length);
apdu.sendBytesLong(output,(short)0,(short)output.length);
}
}
The challenge is sent by the host application as shown below:
byte [] card_signature=null;
SecureRandom random = SecureRandom . getInstance( "SHA1PRNG" ) ;
byte [] bytes = new byte [ 20 ] ;
random . nextBytes ( bytes) ;
CommandAPDU challenge;
ResponseAPDU resp3;
challenge = new CommandAPDU(IDENTITY_CARD_CLA,SIGN_CHALLENGE, 0x00, 0x20,bytes);
resp3= c.transmit(challenge);
if(resp3.getSW()==0x9000) {
card_signature = resp3.getData();
String s= DatatypeConverter.printHexBinary(card_signature);
System.out.println("signature: " + s);
} else System.out.println("Challenge signature error " + resp3.getSW());

Generally, you send bytes over through the APDU interface. A Java or Java Card byte[] is a construct that can hold those bytes. This is where the APDU buffer comes in: it is the byte array that holds the bytes sent over the APDU interface - or at least a portion of them after calling setIncomingAndReceive().
The challenge therefore is within the APDU buffer; instead of calling:
short sigLength = signature.sign(challenge, offset,length, output, x);
you can therefore simply call:
short sigLength = signature.sign(buffer, apdu.getOffsetCdata(), CHALLENGE_SIZE, buffer, START);
where CHALLENGE_SIZE is 20 and START is simply zero.
Then you can use:
apdu.getOutgoingAndSend(START, sigLength);
to send back the signed challenge.
If you require to keep the challenge for a later stage then you should create a byte array in RAM using JCSystem.makeTransientByteArray() during construction of the Applet and then use Util.arrayCopy() to move the byte values into the challenge buffer. However, since the challenge is generated by the offcard system, there doesn't seem to be any need for this. The offcard system should keep the challenge, not the card.
You should not use ISO7816.OFFSET_CDATA anymore; it will not return the correct result if you would use larger key sizes that generate larger signatures and therefore require the use of extended length APDUs.

Related

android AudioTrack playback short array (16bit)

I have an application that playback audio. It takes encoded audio data over RTP and decode it to 16bit array. The decoded 16bit array is converted to 8 bit array (byte array) as this is required for some other functionality.
Even though audio playback is working it is breaking continuously and very hard to recognise audio output. If I listen carefully I can tell it is playing the correct audio.
I suspect this is due to the fact I convert 16 bit data stream into a byte array and use the write(byte[], int, int, AudioTrack.WRITE_NON_BLOCKING) of AudioTrack class for audio playback.
Therefore I converted the byte array back to a short array and used write(short[], int, int, AudioTrack.WRITE_NON_BLOCKING) method to see if it could resolve the problem.
However now there is no audio sound at all. In the debug output I can see the short array has data.
What could be the reason?
Here is the AUdioTrak initialization
sampleRate =AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC);
minimumBufferSize = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
AudioFormat.CHANNEL_OUT_STEREO,
AudioFormat.ENCODING_PCM_16BIT,
minimumBufferSize,
AudioTrack.MODE_STREAM);
Here is the code converts short array to byte array
for (int i=0;i<internalBuffer.length;i++){
bufferIndex = i*2;
buffer[bufferIndex] = shortToByte(internalBuffer[i])[0];
buffer[bufferIndex+1] = shortToByte(internalBuffer[i])[1];
}
Here is the method that converts byte array to short array.
public short[] getShortAudioBuffer(byte[] b){
short audioBuffer[] = null;
int index = 0;
int audioSize = 0;
ByteBuffer byteBuffer = ByteBuffer.allocate(2);
if ((b ==null) && (b.length<2)){
return null;
}else{
audioSize = (b.length - (b.length%2));
audioBuffer = new short[audioSize/2];
}
if ((audioSize/2) < 2)
return null;
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
for(int i=0;i<audioSize/2;i++){
index = i*2;
byteBuffer.put(b[index]);
byteBuffer.put(b[index+1]);
audioBuffer[i] = byteBuffer.getShort(0);
byteBuffer.clear();
System.out.print(Integer.toHexString(audioBuffer[i]) + " ");
}
System.out.println();
return audioBuffer;
}
Audio is decoded using opus library and the configuration is as follows;
opus_decoder_ctl(dec,OPUS_SET_APPLICATION(OPUS_APPLICATION_AUDIO));
opus_decoder_ctl(dec,OPUS_SET_SIGNAL(OPUS_SIGNAL_MUSIC));
opus_decoder_ctl(dec,OPUS_SET_FORCE_CHANNELS(OPUS_AUTO));
opus_decoder_ctl(dec,OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND));
opus_decoder_ctl(dec,OPUS_SET_PACKET_LOSS_PERC(0));
opus_decoder_ctl(dec,OPUS_SET_COMPLEXITY(10)); // highest complexity
opus_decoder_ctl(dec,OPUS_SET_LSB_DEPTH(16)); // 16bit = two byte samples
opus_decoder_ctl(dec,OPUS_SET_DTX(0)); // default - not using discontinuous transmission
opus_decoder_ctl(dec,OPUS_SET_VBR(1)); // use variable bit rate
opus_decoder_ctl(dec,OPUS_SET_VBR_CONSTRAINT(0)); // unconstrained
opus_decoder_ctl(dec,OPUS_SET_INBAND_FEC(0)); // no forward error correction
Let's assume you have a short[] array which contains the 16-bit one channel data to be played.
Then each sample is a value between -32768 and 32767 which represents the signal amplitude at the exact moment. And 0 value represents a middle point (no signal). This array can be passed to the audio track with ENCODING_PCM_16BIT format encoding.
But things are going weird when playing ENCODING_PCM_8BIT is used (See AudioFormat)
In this case each sample encoded by one byte. But each byte is unsigned. That means, it's value is between 0 and 255, while 128 represents the middle point.
Java has no unsigned byte format. Byte format is signed. I.e. values -128...-1 will represent actual values of 128...255. So you have to be careful when converting to the byte array, otherwise it will be a noise with barely recognizable source sound.
short[] input16 = ... // the source 16-bit audio data;
byte[] output8 = new byte[input16.length];
for (int i = 0 ; i < input16.length ; i++) {
// To convert 16 bit signed sample to 8 bit unsigned
// We add 128 (for rounding), then shift it right 8 positions
// Then add 128 to be in range 0..255
int sample = ((input16[i] + 128) >> 8) + 128;
if (sample > 255) sample = 255; // strip out overload
output8[i] = (byte)(sample); // cast to signed byte type
}
To perform backward conversion all should be the same: each single sample to be converted to exactly one sample of the output signal
byte[] input8 = // source 8-bit unsigned audio data;
short[] output16 = new short[input8.length];
for (int i = 0 ; i < input8.length ; i++) {
// to convert signed byte back to unsigned value just use bitwise AND with 0xFF
// then we need subtract 128 offset
// Then, just scale up the value by 256 to fit 16-bit range
output16[i] = (short)(((input8[i] & 0xFF) - 128) * 256);
}
The issue of not being able to convert data from byte array to short array was resolved when used bitwise operators instead of using ByteArray. It could be due not setting the correct parameters in ByteArray or it is not suitable for such conversion.
Nevertheless implementing conversion using bitwise operators resolved the problem. Since the original question has been resolved by this approach, please consider this as the final answer.
I will raise a separate topic for playback issue.
Thank you for all your support.

How to sign on a javacard applet and return the signature to the host application

I have the following function in the javacard applet that is supposed to receive a challenge from the host application, sign it, and return it to the host via command-response apdu communication.
private void sign(APDU apdu) {
if(!pin.isValidated())ISOException.throwIt(SW_PIN_VERIFICATION_REQUIRED);
else{
byte[] buffer = apdu.getBuffer();
byte [] output = new byte [20];
short length = 20;
short x =0;
Signature signature =Signature.getInstance(Signature.ALG_RSA_SHA_PKCS1, false);
signature.init(privKey, Signature.MODE_SIGN);
short sigLength = signature.sign(buffer, offset,length, output, x);
//This sequence of three methods sends the data contained in
//'serial' with offset '0' and length 'serial.length'
//to the host application.
apdu.setOutgoing();
apdu.setOutgoingLength((short)output.length);
apdu.sendBytesLong(output,(short)0,(short)output.length);
}
}
The host computes the challenge as follows and sends it to the javacard applet for signing:
//produce challenge
SecureRandom random = SecureRandom . getInstance( "SHA1PRNG" ) ;
byte [ ]bytes = new byte [ 20 ] ;
random . nextBytes ( bytes) ;
CommandAPDU challenge;
ResponseAPDU resp3;
challenge = new CommandAPDU(IDENTITY_CARD_CLA,SIGN_CHALLENGE, 0x00, 0x00,bytes ,20 );
resp3= c.transmit(challenge);
if(resp3.getSW()==0x9000) {
card_signature = resp2.getData();
String s = new String(card_signature);
System.out.println("signature " + s);
}else System.out.println("Challenge signature error: " + resp3.getSW());
As you can see, I check for both succesful and unsuccesful signing but I get the following printed out:
Challenge signature error:28416
Where exactly do I go wrong? Is it possible I retrieve the challenge in a faulty way with `byte[] buffer = apdu.getBuffer(); or is my signature all wrong?
You are trying to sign using an RSA key. However, the signature size of an RSA generated signature is identical to the key size (the modulus size) encoded in a minimum number of bytes. So e.g. a 2048 bit key results in a signature with size ceil(2028D / 8D) = 256 bytes (the maximum response size, unless you use extended length APDU's).
You should never create byte arrays in Java except when creating the class or when personalizing the applet. Any array created in persistent memory using new byte[] will likely remain until the garbage collector is run, and it may wear out the EEPROM or flash. And for signatures you don't need persistent memory.
If you look at the Signature.sign method:
The input and output buffer data may overlap.
So you can just generate the signature into the APDU buffer instead. Otherwise you can generate it in a JCSystem.makeTransientByteArray created buffer, but if you want to communicate it to the client you'll have to copy it into the APDU buffer anyway.
Please don't ever do the following:
String s = new String(card_signature);
A signature is almost indistinguishable from random bytes, so printing this out will generate just garbage. If you need text output try hexadecimals or base 64 encoding of the signature. Or print it as decimal number (but note that this may lead to loss of leading bytes with value 00).

How to send an int over uint8_t data?

I'm using the RadioHead Packet Radio library from airspayce.com. In the example (nrf24_reliable_datagram_client & server) they let two nodes communicate with each other by sending strings back and forth. Now I want to send an int instead of a string there, and do something with this data. This is what they do in the example:
Define the buf byte.
uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN];
This function receives the data:
manager.recvfromAckTimeout(buf, &len, 500, &from)
Print the buf variable.
Serial.print((char*)buf);
So far so good.Now I want to do something like:
int value = (char*)buf;
Or:
char value[10] = { (char*)buf };
But then I get:
invalid conversion from 'char*' to 'int' (or to 'char'...)
Next to that, on the other side where I'm sending the data, I have:
uint8_t data[] = { analogRead(A0) };
When I'm printing this data on the receiver side, using the code from the first question, I get weird characters. So I thought, let's try:
Serial.print((char*)buf, DEC); // or BYTE
But then I get:
call of overloaded 'print(char*, int)' is ambiguous
What am I doing wrong? Thanks in advance!
You can't just assign an array to an integer and hope that it merges the elements together for you - for example, how does it know how to merge them?
For converting a uint16_t to a uint8_t[2] array you would want to do something like this:
uint16_t analog = analogRead(A0); //read in as int.
uint8_t data[2] = {analog, (analog >> 8)}; // extract as {lower byte, upper byte)
Serial.write(data,2); //write the two bytes to the serial port, lower byte first.
You could do it in other ways like using a union of a uint16_t with an array of two uint8_t's, but the above way is more portable. You could also do it by type casting the pointer to an int, however if one end uses big endian and the other uses little endian, that won't work unless you flip the data around in the array as you are receiving it.
For the receiver end, you would have:
uint8_t data[2];
...
... //whatever you do to receive the bytes that were sent over serial.
...
//Now assuming that data[] contains the received bytes where:
//data[0] was the first in (lower byte) and data[1] was the second in (upper byte)
uint16_t merged = (data[1] << 8) | data[0]; //merge them back together
Hopefully that helps.
Also, the 'overloaded prototype' is saying that no function exists which takes that particular set of input variables. From the print class header you will find there is however this prototype:
write(const uint8_t *buffer, size_t size);
which does what you want - print a specified number of uint8_t's from an array.

C++/CLI converting from a String^ to array <Byte>

I have a serial port application which is written C++/CLI
To read data from the ports input buffer I am using
String^ inputString = System::IO:Ports::SerialPort::ReadExisting();
I need to convert the inputString value to an array of bytes. I have tried using
array<Byte> ^unicodeBytes = System::Text::Encoding::Unicode->GetBytes( inputString );
This works so long as the value being read in to my port input buffer is less than 0x7F (hex). Any values greater than 0x7F gets converted to 0x3F = "?"
E.g. if I send two bytes comprising {0x7F, 0xFF} to my input port then when I read and convert them the array unicodeBytes = { 0x7F 0x00, 0x3F 0x00} when looked at in the debugger watch window of VS2008
According to the unicode tables I have looked at OxFF is a valid unicode value equal to a latin small letter 'y' with two small dots above it.
Any suggestins on how to convert 'y' with two samll dots = 0xFF in string format to 0xFF in a byte array would be greatly appreciated.
Use the SerialPort's Read method to get bytes instead of decoding encoded text:
int BufferSize = <some size>;
array<byte> ^bytes = gcnew array<byte>(BufferSize);
int available = serialPort->BytesAvailable;
serialPort->Read(bytes, 0, Math::Min(available, BufferSize));

How do I get a max number of bytes from a string?

In .NET 2.0, I need to send some strings across the wire, but when building my packets I need to limit them to only 255 bytes.
I was going to use
UnicodeEncoding.GetBytes(char* chars,int charCount, byte* bytes, int byteCount)
MSDN: http://msdn.microsoft.com/en-US/library/z2s6sc1d(v=vs.80)
but I can't compile my app with /unsafe, so this is out.
Here's the basic method I need to fill out:
private void GenerateSNMPObjectPacket(String value, int maxByteCount)
{
int byteCount = Encoding.UTF8.GetByteCount(value);
if (byteCount > maxByteCount)
byteCount = maxByteCount;
byte[] valueBytes = new byte[byteCount];
//somehow get UTF8 bytes into valueBytes
//do what I do with my byte array
}

Resources