The security envelope of the RSFN, the network of the Brazilian Financial System, as defined in chapter 5 of the Manual de Segurança do SFN published by Banco Central do Brasil. Section numbers such as §5.5.3 refer to that manual (version 5.07; Volume I of version 6.00 keeps the same chapter 5).
An envelope is a 588-byte header (Sinetex.Header) followed by the body:
- Security Protocol v3: AES-256-GCM, RSA-2048, SHA-256;
- Security Protocol v2 (legacy): 3DES-EDE3-CBC, RSA-2048, SHA-256.
The library works on bytes. It does not parse the XML of the message, does not talk to the network and does not store keys.
Example
{:ok, me} = Sinetex.Identity.load(File.read!("me.pem"), File.read!("me.key"))
{:ok, peer} = Sinetex.Certificate.load(File.read!("peer.pem"))
# Sending
{:ok, envelope} = Sinetex.seal(xml, from: me, to: peer)
# Receiving
{:ok, %Sinetex.Opened{content: xml}} = Sinetex.open(incoming, as: me, from: peer)Special treatment (C04)
| C04 | Meaning | Compressed | Encrypted |
|---|---|---|---|
| 0 | message or file, signed and encrypted (default) | ✔ | |
| 1 | uses a certificate that is not activated yet (GEN0006) | ✔ | |
| 2 | public broadcast, no recipient | ||
| 3 | security message, e.g. GEN0004 (see error_envelope/2) | ||
| 4 | file, not compressed | ✔ | |
| 6 | file, not compressed, public | ||
| 8 | file, compressed | ✔ | ✔ |
| 10 | file, compressed, public | ✔ |
Every value is signed. That 1 and 4 are encrypted is market practice, not stated by the manual.
Errors
open/2 returns {:error, %Sinetex.Error{}} with the C03 code of the
manual's error table. When there is more than one error in the header, the
lowest code is reported (§5.5.3). Malformed input never raises.
Summary
Functions
Builds the envelope that reports a security error to a counterparty (§5.5.2, §5.5.4, §5.5.7): signed, not encrypted (C04 3), with C03 set.
Opens an envelope: checks the header, decrypts, verifies the signature and, for C04 8 and 10, decompresses.
Builds an envelope: compresses (C04 8 and 10), pads (v2), signs and
encrypts content.
Functions
Builds the envelope that reports a security error to a counterparty (§5.5.2, §5.5.4, §5.5.7): signed, not encrypted (C04 3), with C03 set.
content is the GEN0004 message, which the caller builds.
Options
:c03(required) - the error code, usuallyerror.c03fromopen/2.:from,:to,:version- as inseal/2.
Example
with {:error, %Sinetex.Error{} = error} <- Sinetex.open(envelope, opts) do
gen0004 = build_gen0004(error.egen)
Sinetex.error_envelope(gen0004, from: me, to: peer, c03: error.c03)
end
@spec open(binary(), keyword()) :: {:ok, Sinetex.Opened.t()} | {:error, Sinetex.Error.t()}
Opens an envelope: checks the header, decrypts, verifies the signature and, for C04 8 and 10, decompresses.
Options
:as(required) - yourSinetex.Identity; decrypts C14.:from(required) - the expected sender: aSinetex.Certificateor a list of them. The certificate is picked by C13 and C12.:known- other certificates you know, default[]. A message signed with one of them is rejected with0x0C("certificate does not belong to the sender") instead of0x0A.:certificate_check-fn certificate, header -> :ok | {:error, c03} end, called with the sender certificate. Use it for what depends on the certificate registry: not activated (0x10), expired or revoked (0x11), invalid (0x14). C04 1 means the sender uses a certificate that is not activated yet; the header is passed so you can allow it.:c14_errors-:manual(default) reports a failure to decrypt C14 as0x0D, as the manual says.:uniformreports it as0x0E, the same as a failure in the body, so the error code is not a padding oracle. SeeSECURITY.md.:trim_padding- v2 only. The manual says the padding is not removed (§5.7.14), so by default the content comes back as signed, with its trailing0x00.trueremoves up to 7 trailing0x00bytes, which also removes legitimate zeros at the end of the content.:allow_clear- accept the "empty" header of §5.6.3 (GEN0001 echo), whose body is neither signed nor encrypted. Defaultfalse: such an envelope is rejected with0x02.:max_output_bytes- limit for decompressed content, default 512 MiB.:max_input_bytes- limit for the envelope, default:infinity.
Return
{:ok, %Sinetex.Opened{}} or {:error, %Sinetex.Error{}}.
An envelope whose C03 is not 0x00 is a counterparty reporting an error;
it is opened like any other and the code is in opened.header.c03. Do not
answer it with another error (§5.5.5).
Invalid options raise ArgumentError.
@spec seal(binary(), keyword()) :: {:ok, binary()} | {:error, :compression_required | :recipient_required | :des3_unavailable | :zip64_unsupported}
Builds an envelope: compresses (C04 8 and 10), pads (v2), signs and
encrypts content.
Options
:from(required) - yourSinetex.Identity; signs the content.:to- the recipient'sSinetex.Certificate. Required when C04 means encrypted; optional otherwise (when given, it fills C10/C11).:version-3(default) or2. Version 2 is legacy and needs 3DES in the host OpenSSL, which FIPS mode may disable.:c04- special treatment indicator, default0. See the table in the module documentation.:compression-:gzipor:zip. Required with C04 8 and 10 and not accepted otherwise. There is no default because the right format depends on the destination: clearing houses use GZIP, the manual describes ZIP (and the Bacen SFTP servers use ZIP with DEFLATE).:zip_entry_name- name of the single ZIP entry, default"content".
Errors
{:error, :compression_required}- C04 8 or 10 without:compression;{:error, :recipient_required}- encrypted C04 without:to;{:error, :des3_unavailable}- version 2 and no 3DES in OpenSSL;{:error, :zip64_unsupported}- ZIP content of 4 GiB or more.
Invalid options raise ArgumentError.