]>
git.saurik.com Git - apple/security.git/blob - SecuritySNACCRuntime/c-lib/src/asn-real.c
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
20 * asn_real.c - BER encode, decode, print and free routines for ASN.1 REAL type.
23 * Copyright (C) 1992 Michael Sample and the University of British Columbia
25 * This library is free software; you can redistribute it and/or
26 * modify it provided that this copyright/license information is retained
29 * If you modify this file, you must clearly indicate your changes.
31 * This source code is distributed in the hope that it will be
32 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
33 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
35 * $Header: /cvs/Darwin/src/live/Security/SecuritySNACCRuntime/c-lib/src/asn-real.c,v 1.1.1.1 2001/05/18 23:14:08 mb Exp $
36 * $Log: asn-real.c,v $
37 * Revision 1.1.1.1 2001/05/18 23:14:08 mb
38 * Move from private repository to open source repository
40 * Revision 1.2 2001/05/05 00:59:25 rmurphy
41 * Adding darwin license headers
43 * Revision 1.1.1.1 1999/03/16 18:06:31 aram
44 * Originals from SMIME Free Library.
46 * Revision 1.5 1997/02/28 13:39:50 wan
47 * Modifications collected for new version 1.3: Bug fixes, tk4.2.
49 * Revision 1.4 1995/07/24 21:04:54 rj
50 * changed `_' to `-' in file names.
52 * Revision 1.3 1995/02/18 16:25:13 rj
53 * added support for CPU/compiler combination presenting 64 bit little endian long integers
54 * (in addition to the aforesupported 32 bit big endian long ints).
56 * Revision 1.2 1994/09/01 00:06:28 rj
57 * reduce the risk of unwanted surprises with macro expansion by properly separating the C tokens.
59 * Revision 1.1 1994/08/28 09:46:00 rj
60 * first check-in. for a list of changes to the snacc-1.1 distribution please refer to the ChangeLog.
64 #include "asn-config.h"
69 double pow
PROTO ((double base
, double exp
));
72 /* ieee functions (in case not in math.h)*/
73 extern int iszero (double);
74 extern int isinf (double);
75 extern int signbit (double);
76 extern int ilogb (double);
77 extern double scalbn (double, int);
82 * You must call InitAsnInfinity() to initailize these values
83 * (necessary if you deal with REAL values.)
85 AsnReal PLUS_INFINITY
;
86 AsnReal MINUS_INFINITY
;
89 #define ENC_PLUS_INFINITY 0x40
90 #define ENC_MINUS_INFINITY 0x41
92 #define REAL_BINARY 0x80
93 #define REAL_SIGN 0x40
94 #define REAL_EXPLEN_MASK 0x03
95 #define REAL_EXPLEN_1 0x00
96 #define REAL_EXPLEN_2 0x01
97 #define REAL_EXPLEN_3 0x02
98 #define REAL_EXPLEN_LONG 0x03
99 #define REAL_FACTOR_MASK 0x0c
100 #define REAL_BASE_MASK 0x30
101 #define REAL_BASE_2 0x00
102 #define REAL_BASE_8 0x10
103 #define REAL_BASE_16 0x20
107 * Returns the smallest octet length needed to
108 * hold the given long int value
111 SignedIntOctetLen
PARAMS ((val
),
114 unsigned long int mask
= (0x7f80L
<< ((sizeof (long int) - 2) * 8));
115 unsigned int retVal
= sizeof (long int);
118 val
= val
^ (~0L); /* XOR val with all 1's */
120 while ((retVal
> 1) && ((val
& mask
) == 0))
128 } /* SignedIntOctetLen */
132 * encodes universal TAG LENGTH and Contents of and ASN.1 REAL
135 BEncAsnReal
PARAMS ((b
, data
),
141 len
= BEncAsnRealContent (b
, data
);
142 len
+= BEncDefLen (b
, len
);
143 len
+= BEncTag1 (b
, UNIV
, PRIM
, REAL_TAG_CODE
);
149 * decodes universal TAG LENGTH and Contents of and ASN.1 REAL
152 BDecAsnReal
PARAMS ((b
, result
, bytesDecoded
, env
),
154 AsnReal
*result _AND_
155 AsnLen
*bytesDecoded _AND_
161 if ((tag
= BDecTag (b
, bytesDecoded
, env
)) != MAKE_TAG_ID (UNIV
, PRIM
, REAL_TAG_CODE
))
163 Asn1Error ("BDecAsnReal: ERROR wrong tag on REAL.\n");
167 elmtLen
= BDecLen (b
, bytesDecoded
, env
);
168 BDecAsnRealContent (b
, tag
, elmtLen
, result
, bytesDecoded
, env
);
177 * Inits the PLUS_INFINITY and MINUS_INFINITY globals assuming
178 * that the double is an IEEE DOUBLE
179 * The bits for MINUS_INFINITY are 0xfff0000000000000
180 * The bits for PLUS_INFINITY are 0x7ff0000000000000
185 unsigned char *c
= (unsigned char *)&PLUS_INFINITY
;
188 if (sizeof (double) != 8)
189 Asn1Error ("InitAsnInfinity: ERROR expected sizeof (AsnReal) to be 8");
191 #if WORDS_BIGENDIAN /* Big endian */
194 for (i
= 2; i
< sizeof (double); i
++)
196 #else /* Little endian */
199 for (i
= 0; i
< 6; i
++)
203 MINUS_INFINITY
= -PLUS_INFINITY
;
204 } /* InitAsnInfinity */
207 * Encodes the content of an ASN.1 REAL value to the given buffer.
208 * This version of the routine ASSUMES that the C rep. of a double
209 * is the same as the IEEE std.
212 BEncAsnRealContent
PARAMS ((b
, value
),
220 unsigned long int *first4
;
221 unsigned long int *second4
;
224 unsigned long mantissa
, val
, *p
;
229 /* no contents for 0.0 reals */
230 if (*value
== 0.0) /* all bits zero, disregarding top/sign bit */
235 #error sorry! this 32 bit code requires big endianess.
238 /* this code is designed to work were longs are 32 bit wide and big endian */
240 dbl
= (unsigned char *) value
;
242 first4
= (unsigned long int *) dbl
;
243 second4
= (unsigned long int *) (dbl
+ sizeof (long int));
245 isNeg
= dbl
[0] & 0x80;
247 /* special real values for +/- oo */
248 if (((*first4
& 0x7fffffff) == 0x7ff00000) && (*second4
== 0))
252 BufPutByteRvs (b
, ENC_MINUS_INFINITY
);
256 BufPutByteRvs (b
, ENC_PLUS_INFINITY
);
260 else /* encode a binary real value */
262 exponent
= (((*first4
) >> 20) & 0x07ff);
264 /* write the mantissa (N value) */
265 BufPutSegRvs (b
, (char *)(dbl
+2), sizeof (double)-2);
268 * The rightmost 4 bits of a double 2nd octet are the
269 * most sig bits of the mantissa.
270 * write the most signficant byte of the asn1 real mantissa,
271 * adding implicit bit to 'left of decimal' if not de-normalized
272 * (de normalized if exponent == 0)
274 * if the double is not in de-normalized form subtract 1023
275 * from the exponent to get proper signed exponent.
277 * for both the normalized and de-norm forms
278 * correct the exponent by subtracting 52 since:
279 * 1. mantissa is 52 bits in the double (56 in ASN.1 REAL form)
280 * 2. implicit decimal at the beginning of double's mantissa
281 * 3. ASN.1 REAL's implicit decimal is after its mantissa
282 * so converting the double mantissa to the ASN.1 form has the
283 * effect of multiplying it by 2^52. Subtracting 52 from the
284 * exponent corrects this.
286 if (exponent
== 0) /* de-normalized - no implicit 1 to left of dec.*/
288 BufPutByteRvs (b
, dbl
[1] & 0x0f);
293 BufPutByteRvs (b
, (dbl
[1] & 0x0f) | 0x10); /* 0x10 adds implicit bit */
294 exponent
-= (1023 + 52);
300 #error sorry! this 64 bit code requires little endianess.
303 /* this code is designed to work on Alpha under OSF/1 (64 bit longs, little endian) */
305 p
= (unsigned long *) value
;
308 isNeg
= (val
>> 63) & 1;
309 /* special real values for +/- oo */
310 if (!finite (*value
))
314 BufPutByteRvs (b
, ENC_MINUS_INFINITY
);
318 BufPutByteRvs (b
, ENC_PLUS_INFINITY
);
322 else /* encode a binary real value */
324 exponent
= (val
>> 52) & 0x7ff;
325 mantissa
= (val
& 0xfffffffffffffL
) | 0x10000000000000L
;
327 for (i
= 0; i
< 7; i
++)
329 BufPutByteRvs (b
, mantissa
& 0xff);
332 exponent
-= (1023 + 52);
335 #error long neither 8 nor 4 bytes in size?
339 /* write the exponent */
340 BufPutByteRvs (b
, exponent
& 0xff);
341 BufPutByteRvs (b
, exponent
>> 8);
343 /* write format octet */
344 /* bb is 00 since base is 2 so do nothing */
345 /* ff is 00 since no other shifting is nec */
348 BufPutByteRvs (b
, REAL_BINARY
| REAL_EXPLEN_2
| REAL_SIGN
);
352 BufPutByteRvs (b
, REAL_BINARY
| REAL_EXPLEN_2
);
355 return sizeof (double) + 2;
360 } /* BEncAsnRealContent */
362 #else /* IEEE_REAL_FMT not def */
368 * Inits the PLUS_INFINITY and MINUS_INFINITY globals assuming
369 * that the ieee_values library is present
374 PLUS_INFINITY
= infinity();
375 MINUS_INFINITY
= -PLUS_INFINITY
;
376 } /* InitAsnInfinity */
379 * Encodes the content of an ASN.1 REAL value to the given buffer.
380 * This version of the routine does not assume an IEEE double rep.
381 * ieee library conversion routine are used instead.
384 BEncAsnRealContent
PARAMS ((b
, value
),
388 unsigned long int encLen
;
391 unsigned int truncatedMantissa
;
395 unsigned char buf
[sizeof (double)];
397 unsigned char firstOctet
;
399 /* no contents for 0.0 reals */
403 /* special real values for +/- oo */
406 if (signbit (*value
)) /* neg */
407 BufPutByteRvs (b
, ENC_MINUS_INFINITY
);
409 BufPutByteRvs (b
, ENC_PLUS_INFINITY
);
412 else /* encode a binary real value */
414 if (signbit (*value
))
419 exponent
= ilogb (*value
);
421 /* get the absolute value of the mantissa (subtract 1 to make < 1) */
422 mantissa
= scalbn (fabs (*value
), -exponent
-1);
425 tmpMantissa
= mantissa
;
427 /* convert mantissa into an unsigned integer */
428 for (i
= 0; i
< sizeof (double); i
++)
430 /* normalizied so shift 8 bits worth to the left of the decimal */
431 tmpMantissa
*= (1<<8);
433 /* grab only (octet sized) the integer part */
434 truncatedMantissa
= (unsigned int) tmpMantissa
;
436 /* remove part to left of decimal now for next iteration */
437 tmpMantissa
-= truncatedMantissa
;
439 /* write into tmp buffer */
440 buf
[i
] = truncatedMantissa
;
442 /* keep track of last non zero octet so can zap trailing zeros */
443 if (truncatedMantissa
)
448 * write format octet (first octet of content)
452 * 1 in bit#1 means binary rep
453 * 1 in bit#2 means the mantissa is neg, 0 pos
454 * bb is the base: 65 base
460 * ff is the Value of F where Mantissa = sign x N x 2^F
461 * FF can be one of 0 to 3 inclusive. (used to save re-alignment)
463 * ee is the length of the exponent: 21 length
470 * encoded binary real value looks like
473 * --------------------------------------------------------
474 * |1Sbbffee| exponent (2's comp) | N (unsigned int) |
475 * --------------------------------------------------------
478 firstOctet
= REAL_BINARY
;
479 if (signbit (*value
))
480 firstOctet
|= REAL_SIGN
;
482 /* bb is 00 since base is 2 so do nothing */
483 /* ff is 00 since no other shifting is nec */
486 * get exponent calculate its encoded length
487 * Note that the process of converting the mantissa
488 * double to an int shifted the decimal mantissaLen * 8
489 * to the right - so correct that here
491 exponent
++; /* compensate for trick to put mantissa < 1 */
492 exponent
-= (mantissaLen
* 8);
493 expLen
= SignedIntOctetLen (exponent
);
498 firstOctet
|= REAL_EXPLEN_1
;
501 firstOctet
|= REAL_EXPLEN_2
;
504 firstOctet
|= REAL_EXPLEN_3
;
507 firstOctet
|= REAL_EXPLEN_LONG
;
511 encLen
= mantissaLen
+ expLen
+ 1;
513 /* write the mantissa (N value) */
514 BufPutSegRvs (b
, (char*)buf
, mantissaLen
);
516 /* write the exponent */
517 for (i
= expLen
; i
> 0; i
--)
519 BufPutByteRvs (b
, exponent
);
523 /* write the exponents length if nec */
527 BufPutByteRvs (b
, expLen
);
530 /* write the format octet */
531 BufPutByteRvs (b
, firstOctet
);
536 } /* BEncAsnRealContent */
538 #else /* neither IEEE_REAL_FMT or IEEE_REAL_LIB are def */
541 * Inits the PLUS_INFINITY and MINUS_INFINITY globals assuming
542 * that the double is an IEEE DOUBLE. This should be changed
543 * for the target architecture (if it is not IEEE)
551 if (sizeof (double) != 8)
552 Asn1Error ("InitAsnInfinity: ERROR expected sizeof (AsnReal) to be 8");
554 c
= (unsigned char*)&PLUS_INFINITY
;
557 for (i
= 2; i
< sizeof (double); i
++)
560 MINUS_INFINITY
= -PLUS_INFINITY
;
561 } /* InitAsnInfinity */
564 * Encodes the content of an ASN.1 REAL value to the given buffer.
565 * This version of the routine does not assume an IEEE double rep.
566 * or the existence of the IEEE library routines. Uses old style
570 BEncAsnRealContent
PARAMS ((b
, value
),
574 unsigned long int encLen
;
577 unsigned int truncatedMantissa
;
581 unsigned char buf
[sizeof (double)];
583 unsigned char firstOctet
;
585 /* no contents for 0.0 reals */
589 /* special real values for +/- oo */
590 if (*value
== MINUS_INFINITY
)
592 BufPutByteRvs (b
, ENC_MINUS_INFINITY
);
595 else if (*value
== PLUS_INFINITY
)
597 BufPutByteRvs (b
, ENC_PLUS_INFINITY
);
600 else /* encode a binary real value */
603 * this is what frexp gets from *value
604 * *value == mantissa * 2^exponent
605 * where 0.5 <= |manitissa| < 1.0
607 mantissa
= frexp (*value
, &exponent
);
609 /* set sign and make mantissa = | mantissa | */
619 tmpMantissa
= mantissa
;
621 /* convert mantissa into an unsigned integer */
622 for (i
= 0; i
< sizeof (double); i
++)
624 /* normalizied so shift 8 bits worth to the left of the decimal */
625 tmpMantissa
*= (1<<8);
627 /* grab only (octet sized) the integer part */
628 truncatedMantissa
= (unsigned int) tmpMantissa
;
630 /* remove part to left of decimal now for next iteration */
631 tmpMantissa
-= truncatedMantissa
;
633 /* write into tmp buffer */
634 buf
[i
] = truncatedMantissa
;
636 /* keep track of last non zero octet so can zap trailing zeros */
637 if (truncatedMantissa
)
642 * write format octet (first octet of content)
646 * 1 in bit#1 means binary rep
647 * 1 in bit#2 means the mantissa is neg, 0 pos
648 * bb is the base: 65 base
654 * ff is the Value of F where Mantissa = sign x N x 2^F
655 * FF can be one of 0 to 3 inclusive. (used to save re-alignment)
657 * ee is the length of the exponent: 21 length
664 * encoded binary real value looks like
667 * --------------------------------------------------------
668 * |1Sbbffee| exponent (2's comp) | N (unsigned int) |
669 * --------------------------------------------------------
672 firstOctet
= REAL_BINARY
;
674 firstOctet
|= REAL_SIGN
;
676 /* bb is 00 since base is 2 so do nothing */
677 /* ff is 00 since no other shifting is nec */
680 * get exponent calculate its encoded length
681 * Note that the process of converting the mantissa
682 * double to an int shifted the decimal mantissaLen * 8
683 * to the right - so correct that here
685 exponent
-= (mantissaLen
* 8);
686 expLen
= SignedIntOctetLen (exponent
);
691 firstOctet
|= REAL_EXPLEN_1
;
694 firstOctet
|= REAL_EXPLEN_2
;
697 firstOctet
|= REAL_EXPLEN_3
;
700 firstOctet
|= REAL_EXPLEN_LONG
;
704 encLen
= mantissaLen
+ expLen
+ 1;
706 /* write the mantissa (N value) */
707 BufPutSegRvs (b
, (char*)buf
, mantissaLen
);
709 /* write the exponent */
710 for (i
= expLen
; i
> 0; i
--)
712 BufPutByteRvs (b
, exponent
);
716 /* write the exponents length if nec */
720 BufPutByteRvs (b
, expLen
);
723 /* write the format octet */
724 BufPutByteRvs (b
, firstOctet
);
729 } /* BEncAsnRealContent */
731 #endif /* IEEE_REAL_LIB */
732 #endif /* IEEE_REAL_FMT */
737 * Decodes the content of a BER REAL value.
738 * This only supports the binary REAL encoding. The decimal encoding
739 * is left as an exercise to the reader.
742 BDecAsnRealContent
PARAMS ((b
, tagId
, len
, result
, bytesDecoded
, env
),
746 AsnReal
*result _AND_
747 AsnLen
*bytesDecoded _AND_
750 unsigned char firstOctet
;
751 unsigned char firstExpOctet
;
756 long int exponent
= 0;
766 firstOctet
= BufGetByte (b
);
769 (*bytesDecoded
) += 1;
770 if (firstOctet
== ENC_PLUS_INFINITY
)
771 *result
= PLUS_INFINITY
;
772 else if (firstOctet
== ENC_MINUS_INFINITY
)
773 *result
= MINUS_INFINITY
;
776 Asn1Error ("BDecAsnRealContent: ERROR - unrecognized real number of length 1 octet.\n");
782 if (firstOctet
& REAL_BINARY
)
784 firstExpOctet
= BufGetByte (b
);
785 if (firstExpOctet
& 0x80)
787 switch (firstOctet
& REAL_EXPLEN_MASK
)
791 exponent
= (exponent
<< 8)| firstExpOctet
;
796 exponent
= (exponent
<< 16) |
797 (((unsigned long int) firstExpOctet
) << 8) |
803 exponent
= (exponent
<< 16) |
804 (((unsigned long int) firstExpOctet
) << 8) |
806 exponent
= (exponent
<< 8) | BufGetByte (b
);
809 default: /* long form */
810 expLen
= firstExpOctet
+1;
812 firstExpOctet
= BufGetByte (b
);
813 if (firstExpOctet
& 0x80)
814 exponent
= (-1 <<8) | firstExpOctet
;
816 exponent
= firstExpOctet
;
817 for (;i
> 0; firstExpOctet
--)
818 exponent
= (exponent
<< 8) | BufGetByte (b
);
823 for (i
= 1 + expLen
; i
< len
; i
++)
826 mantissa
+= BufGetByte (b
);
829 /* adjust N by scaling factor */
830 mantissa
*= (1<<((firstOctet
& REAL_FACTOR_MASK
) >> 2));
832 switch (firstOctet
& REAL_BASE_MASK
)
847 Asn1Error ("BDecAsnRealContent: ERROR - unsupported base for a binary real number.\n");
856 *result
= mantissa
* pow ((double)base
, (double)exponent
);
858 if (firstOctet
& REAL_SIGN
)
861 (*bytesDecoded
) += len
;
863 else /* decimal version */
865 Asn1Error ("BDecAsnRealContent: ERROR - decimal REAL form is not currently supported\n");
870 } /* BDecAsnRealContent */
874 * Prints given REAL value to the given FILE * in ASN.1 Value Notation.
878 PrintAsnReal
PARAMS ((f
, v
, indent
),
881 unsigned short int indent
)
883 fprintf (f
, "%.17E", *v
);